большое обновление фронта

This commit is contained in:
2024-01-03 12:38:37 +03:00
parent 4c94a7271a
commit a1f86691a4
9 changed files with 333 additions and 200 deletions

View File

@@ -1,27 +1,26 @@
/* TODO исправить стили, тут верхней навигации вообще нет */
/* ========== THEME ========== */
body {
--text-color: #111;
--brand-color: #231765;
--bkg-color-blue: #0066e3;
--text-color: #262626;
--text-color2: #3d3d3d;
--bkg-color: #fff;
--bkg-color2: #ccc;
--bkg-color3: #aaa;
--brand-bg: #EDF3FE;
--brand-text: #5488F7;
--bg-color: #FEFEFE;
--bg-selected: #F1F1F1;
}
@media (prefers-color-scheme: dark) {
/* defaults to dark theme */
body {
--text-color: #eee;
--brand-color: #654dea;
--bkg-color-blue: #003aac;
--text-color2: #bbb;
--bkg-color: #121212;
--bkg-color2: #202020;
--bkg-color3: #353435;
--brand-bg: #393E50;
--brand-text: #5F93F3;
--bg-color: #2d2c33;
--bg-selected: #424248;
}
}
@@ -30,50 +29,28 @@ body {
color: var(--text-color);
}
body {
background: var(--bkg-color);
*, *::before, *::after {
box-sizing: border-box;
}
.page-header {
text-align: center;
margin: 1em 3em;
body {
background: var(--bg-color);
margin: 0; /* браузеры зачем-то ставят свое значение */
}
#content {
margin: 0.2em;
}
/* ========== MAIN STYLES ========== */
#header-wrapper {
display: flex;
margin: 1em;
header > h1 {
text-align: center;
background-color: var(--brand-bg);
padding: 0.5em;
}
#header-wrapper * {
color: var(--brand-color);
header * {
color: var(--brand-text);
}
header {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
margin: 0 auto;
}
header > * {
margin: auto 0.5em;
text-decoration: none;
font-size: medium;
}
header > div > * {
display: block;
margin: 0.1em 0;
}
#logo-text {
font-weight: bolder;
font-size: xx-large;
}
#logo-image {
width: 50px;
height: 50px;
}

View File

@@ -1,2 +1,2 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:se="http://svg-edit.googlecode.com" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="85" height="80" style=""> <title>favicon</title>
<svg xmlns="http://www.w3.org/2000/svg" width="85" height="85" style=""><title>favicon</title>
<rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill-opacity="0.0" stroke="none" style="" class=""/> <g class="currentLayer" style=""><title>Layer 1</title><rect fill="#4a90d6" stroke="#222222" stroke-width="2" stroke-linejoin="round" stroke-dashoffset="" fill-rule="nonzero" id="svg_38" x="77.39605712890625" y="107.25747680664062" width="7" height="0" style="color: rgb(0, 0, 0);" class=""/><path fill="#4a90d6" stroke="#222222" stroke-width="2" stroke-linejoin="round" stroke-dashoffset="" fill-rule="nonzero" marker-start="" marker-mid="" marker-end="" id="svg_42" d="M4.913043206854184,73.1181132105101 L23.991979764689916,39.72997423429874 L43.07091632252451,73.1181132105101 L4.913043206854184,73.1181132105101 z" style="color: rgb(0, 0, 0);" class=""/><path fill="#4a90d6" stroke="#222222" stroke-width="2" stroke-linejoin="round" stroke-dashoffset="" fill-rule="nonzero" marker-start="" marker-mid="" marker-end="" d="M43.558975285215695,73.08701234133133 L62.63791184305143,39.698873365119965 L81.71684840088602,73.08701234133133 L43.558975285215695,73.08701234133133 z" style="color: rgb(0, 0, 0);" class="" id="svg_47"/><path fill="#4a90d6" stroke="#222222" stroke-width="2" stroke-linejoin="round" stroke-dashoffset="" fill-rule="nonzero" marker-start="" marker-mid="" marker-end="" d="M24.25514847628625,39.17074458868393 L43.33408503412198,5.782605612472565 L62.41302159195658,39.17074458868393 L24.25514847628625,39.17074458868393 z" style="color: rgb(0, 0, 0);" class="" id="svg_46"/><path fill="#4a90d6" stroke="#222222" stroke-width="2" stroke-linejoin="round" stroke-dashoffset="" fill-rule="nonzero" marker-start="" marker-mid="" marker-end="" id="svg_48" d="M33.91763093443063,55.92877816603246 L43.29425964116126,40.29240521591228 L52.6708883478924,55.92877816603246 L33.91763093443063,55.92877816603246 z" style="color: rgb(0, 0, 0);" class=""/></g></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

160
static/js/index-main.js Normal file
View File

@@ -0,0 +1,160 @@
// скрипт страницы index, на которой отображаются графики
// интервал обновления статуса в миллисекундах
const RELOAD_STATS_INTERVAL = 10000
// интервал обновления графика - 1% показываемого времени (2 недели)
// const RELOAD_CHART_INTERVAL = (3600 * 1000 * 24 * 14) / 100
// const RELOAD_CHART_INTERVAL = (3600 * 1000 * 24) / 100
const RELOAD_CHART_INTERVAL = 30000
const DATETIME_FORMAT = "DD MMM YYYY HH:mm:ss"
function unpackBits(num, desc) {
let out = ""
for (let i = 0; i < desc.length; i++) {
if (desc[i] !== null) {
if ((num & (1 << i)) !== 0) {
// бит установлен
if (out.length === 0) {
out = desc[i]
} else {
out += " + "
out += desc[i]
}
}
}
}
return out
}
/**
* Функция для линейной аппроксимации набора точек. Для заданных точек находит коэфицент a из уравнения y=ax+b.
* Уравнение подбирается при помощи метода наименьших квадратов.
* @param dataset набор точек в виде [[timestamp, val], [timestamp, val], ...], где timestamp - значение по X, val - значение по Y
* @returns {number}, коэфицент a
*/
function approximateWithTimestamps(dataset) {
// для точных расчетов нужно сместить timestamp
const timestamp_offset = dataset[0][0]
// сумма (x[i] * y[i])
let sum_x_mul_y = 0
for (let i = 0; i < dataset.length; i++) {
sum_x_mul_y += (dataset[i][0] - timestamp_offset) * dataset[i][1]
}
// сумма всех x[i]
let sum_x = 0;
for (let i = 0; i < dataset.length; i++) {
sum_x += dataset[i][0] - timestamp_offset
}
// сумма всех y[i]
let sum_y = 0;
for (let i = 0; i < dataset.length; i++) {
sum_y += dataset[i][1]
}
// сумма всех x[i]^2
let sum_x_mul_x = 0;
for (let i = 0; i < dataset.length; i++) {
sum_x_mul_x += Math.pow(dataset[i][0] - timestamp_offset, 2)
}
// вычисление коэфицента a из формулы y=ax+b
// нам нужен только он
return (dataset.length * sum_x_mul_y - sum_x * sum_y) / (sum_x_mul_x - Math.pow(sum_x, 2))
}
async function makeRequest(url) {
let response = await fetch(url)
if (response.status !== 200) {
console.log('fetch(' + url + ') failed. Status Code: ' + response.status);
return null;
}
return await response.json()
}
async function loadAllData() {
return await makeRequest('/fetch/all')
}
async function loadChartData() {
return (await makeRequest('/fetch/tank-chart'))['tank_chart']
}
async function loadLastUpdates() {
return (await makeRequest('/fetch/stats'))['stats']
}
async function updateStatus() {
let dataset = await loadLastUpdates()
// последнее обновление
document.getElementById("tank-last-update").innerHTML = moment(new Date(dataset['tank']['last_update'] * 1000)).format(DATETIME_FORMAT)
document.getElementById("pump-last-update").innerHTML = moment(new Date(dataset['pump']['last_update'] * 1000)).format(DATETIME_FORMAT)
//<p>Уровень воды <span id="tank-level-dir"></span></p>
const last_radar_values = dataset['tank']['last_radar_values']
if (last_radar_values.length === 0) {
document.getElementById("tank-level-dir").innerHTML = "(?)";
} else {
document.getElementById("tank-level-dir").innerHTML = approximateWithTimestamps(last_radar_values) < 0 ? '↘' : '↗';
}
//<p>Текущий уровень воды: <span id="tank-level-now"></span>%</p>
document.getElementById("tank-level-now").innerHTML = dataset['tank']['last_percentage']
//<p>Текущее значение с радара: <span id="tank-raw-now"></span></p>
document.getElementById("tank-raw-now").innerHTML = dataset['tank']['last_radar']
//<p>Статус: <span id="tank-status"></span></p>
const shur_status_bits = ['нужна вода', 'поплавок нижний', 'поплавок верхний', 'поплавок аварийный']
document.getElementById("tank-status").innerHTML = unpackBits(dataset['tank']['status_reg'], shur_status_bits) + " (" + dataset['tank']['status_reg'] + ")";
//<p>Частота ПЧ: <span id="pump-vfd-freq"></span>Гц</p>
document.getElementById("pump-vfd-freq").innerHTML = dataset['pump']['vfd_freq']
//<p>Ток ПЧ: <span id="pump-vfd-curr"></span>А</p>
document.getElementById("pump-vfd-curr").innerHTML = dataset['pump']['vfd_curr']
//<p>Ошибка ПЧ: <span id="pump-vfd-error"></span></p>
document.getElementById("pump-vfd-error").innerHTML = dataset['pump']['vfd_err']
//<p>Регистр аварий: <span id="pump-alarms"></span></p>
document.getElementById("pump-alarms").innerHTML = dataset['pump']['alarms']
//<p>Состояние КА: <span id="pump-stage"></span></p>
document.getElementById("pump-stage").innerHTML = dataset['pump']['pump_stage']
//<p>Текущий расход: <span id="pump-flow-meter"></span>м³</p>
document.getElementById("pump-flow-meter").innerHTML = dataset['pump']['flow_meter']
}
// состояния конченого автомата
// $states = [
// 0 => "отключен",
// 2 => "инициализация: установка задвижек в начальное состояние",
// 21 => "инициализация: закрытие задвижек 23.5 и 23.6",
// 31 => "инициализация: открытие задвижек 23.5 и 23.6",
// 99 => "<span class=\"value-bad\">авария</span>",
//
// 100 => "ожидание сигнала на перекачку воды",
//
// 102 => "запуск: открытие задвижки 23.6",
// 110 => "запуск: ожидание сигнала от датчика уровня поз.36",
// 121 => "запуск: открытие задвижек насоса",
// 131 => "запуск: закрытие задвижки 23.6",
// 141 => "запуск: пуск ПЧ",
//
// 200 => "<span class=\"value-good\">работает</span>",
//
// 202 => "остановка: закрытие задвижек 23.3 и 23.4",
// 211 => "остановка: ожидание остановки ПЧ",
// 221 => "остановка: перевод запорной арматуры в исходное состояние",
// 231 => "остановка: работа компрессора",
// 235 => "остановка: сброс конденсата клапанами 25.*"
// ];