265 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RSCM-101</title>
<link rel="stylesheet" type="text/css" href="/style.css">
<link rel="stylesheet" type="text/css" href="/fields.css">
<style>
header {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 10;
background: var(--bg-selected);
}
body { /* значение по-умолчанию */ --header-height: 60px; }
#content {
padding-top: var(--header-height);
}
.l3-proto-label > * {
display: inline-block;
}
.l3-proto-label input[type=checkbox] {
width: auto;
}
.
</style>
</head>
<body>
<div id="app" hidden>
<header>
<div class="tabs-header">
<span style="font-weight:bold">RSCM-101</span>
<a href="/#monitoring" class="tabs-btn">Мониторинг</a>
<a href="/#setup" class="tabs-btn">Настройки</a>
<a href="/#qos" class="tabs-btn">QoS</a>
<a href="/#admin" class="tabs-btn">Администрирование</a>
<a class="tabs-btn active">Разработчикам</a>
<a href="/logout" class="tabs-btn">Выход</a>
</div>
</header>
<div id="content">
<div class="settings-set-container">
<h2>Настройки записи пакетов в IPS</h2>
<label>
<span>Активировать запись</span>
<span class="toggle-input"><input type="checkbox" v-model="params.loggingIps" /><span class="slider"></span></span>
</label>
<button class="action-button" @click="settingsSubmitLoggingIps()">Сохранить <span class="submit-spinner" v-show="submitStatus.loggingIps"></span></button>
</div>
<div class="settings-set-container" v-if="settingFetchComplete">
<h2>Настройки журналирования статистики</h2>
<label>
<span>Активировать запись</span>
<span class="toggle-input"><input type="checkbox" v-model="params.loggingStatistics.en" /><span class="slider"></span></span>
</label>
<label>
<span>Период сбора статистики</span>
<select v-model="params.loggingStatistics.logPeriodMs">
<option :value="30000">30s</option>
<option :value="10000">10s</option>
<option :value="5000">5s</option>
<option :value="2000">2s</option>
<option :value="1000">1s</option>
<option :value="500">500ms</option>
<option :value="250">250ms (!)</option>
<option :value="50">FULL SPEED (!)</option>
</select>
</label>
<label>
<span>Время хранения</span>
<select v-model="params.loggingStatistics.maxAgeMs">
<option :value="60000">1 мин</option>
<option :value="300000">5 мин</option>
<option :value="900000">15 мин</option>
<option :value="1800000">30 мин</option>
<option :value="3600000">1 час</option>
<option :value="7200000">2 часа</option>
<option :value="21600000">6 часов</option>
<option :value="43200000">12 часов</option>
<option :value="86400000">24 часа</option>
<option :value="172800000">48 часов</option>
<option :value="604800000">7 суток</option>
</select>
</label>
<button class="action-button" @click="settingsSubmitLoggingStatistics()">Сохранить <span class="submit-spinner" v-show="submitStatus.loggingStatistics"></span></button>
</div>
<div class="settings-set-container">
<h2>Просмотр логов</h2>
<button class="action-button" @click="logView()">Обновить <span class="submit-spinner" v-show="submitStatus.logView"></span></button>
<a href="/dev/logs.csv" class="action-button" download>Скачать</a>
</div>
</div>
</div>
<div class="settings-set-container"><h2>График</h2><canvas id="mainChart" style="display: block; box-sizing: border-box;"></canvas></div>
<script src="/js/vue.js?v=3.5.13"></script>
<script src="/js/chart.js"></script>
<script src="/js/moment.js"></script>
<script src="/js/chartjs-adapter-moment.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);
let MainChart = new Chart(document.getElementById('mainChart').getContext('2d'), {
type: 'line',
data: {
datasets: []
},
options: {
responsive: true,
scales: {
x: {
type: 'time',
title: {
display: true,
text: 'Время',
font: {
padding: 4,
size: 20,
weight: 'bold',
family: 'Arial'
},
//color: 'darkblue'
},
},
y: {
beginAtZero: true,
type: 'linear',
position: 'left'
}
}
}
})
const app = Vue.createApp({
data() {
return {
// false - означает что статистика не отправляется, true - отправляется
submitStatus: {
loggingIps: false,
loggingStatistics: false,
logView: false
},
params: {
loggingIps: false,
loggingStatistics: {
en: false,
logPeriodMs: 1000,
maxAgeMs: 0
}
},
chart: null,
settingFetchComplete: false
}
},
methods: {
settingsSubmitLoggingIps() {
if (this.submitStatus.loggingIps) { return }
this.submitStatus.loggingIps = true
fetch(`/dev/cpapicall?f=SetDmaDebug&param=log_bool&value=${this.params.loggingIps}`, {method: 'POST', headers: {'Content-Type': 'application/json'}, credentials: 'same-origin' })
.then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } })
.catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) })
.finally(() => { this.submitStatus.loggingIps = false })
},
settingsSubmitLoggingStatistics() {
function modcodToStr(modcod) {
// модкоды из раздела 5.5.2.2 https://www.etsi.org/deliver/etsi_en/302300_302399/302307/01.01.02_60/en_302307v010102p.pdf
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") {
return "?";
}
if (modcod < 0 || modcod >= modcods.length) {
return `? (${modcod})`
}
return modcods[modcod]
}
if (this.submitStatus.loggingStatistics) { return }
let query = {
"en": this.params.loggingStatistics.en,
"logPeriodMs": this.params.loggingStatistics.logPeriodMs,
"maxAgeMs": this.params.loggingStatistics.maxAgeMs,
}
this.submitStatus.loggingStatistics = true
fetch('/dev/settings', {method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(query), credentials: 'same-origin' })
.then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.updateLoggingStatisticsSettings(vals) })
.catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) })
.finally(() => { this.submitStatus.loggingStatistics = false })
},
logView() {
if (this.submitStatus.logView) { return }
this.submitStatus.logView = true
fetch(`/dev/logs.csv`, {method: 'GET', credentials: 'same-origin' })
.then(async (resp) => {
let logfileContent = await resp.text()
const lines = logfileContent.trim().split(/\r\n|\n/)
let datasets = []
// столбец timestamp пропускаем
let labels = lines.shift().split('\t')
labels.shift()
let rows = lines.map(line => line.split('\t'))
for (let li = 0; li < labels.length; li++) {
let td = []
for (let ri = 0; ri < rows.length; ri++) {
td.push({x: new Date(rows[ri][0]), y: rows[ri][li + 1]})
}
datasets.push({
label: labels[li],
data: td,
//borderColor: 'blue',
borderWidth: 2,
fill: false,
})
}
MainChart.datasets = datasets
MainChart.update()
})
.catch((reason) => { alert(`Ошибка при чтении логов: ${reason}`) })
.finally(() => { this.submitStatus.logView = false })
},
updateLoggingStatisticsSettings(vals) {
this.params.loggingStatistics.en = vals['logstat']['en']
this.params.loggingStatistics.logPeriodMs = vals['logstat']['logPeriodMs']
this.params.loggingStatistics.maxAgeMs = vals['logstat']['maxAgeMs']
}
},
mounted() {
const doFetchSettings = async () => {
let d = await fetch("/dev/settings")
let vals = await d.json()
this.settingFetchComplete = true
this.updateLoggingStatisticsSettings(vals)
}
doFetchSettings().then(() => {})
document.getElementById("app").removeAttribute("hidden")
//this.chart = chart
}
});
app.mount('#app')
</script>
</body>
</html>