225 lines
11 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 v-if="logsTable.headers.length != 0" style="overflow-x: auto;">
<table>
<tbody>
<tr>
<th v-for="h in logsTable.headers">{{ h }}</th>
</tr>
<tr v-for="r in logsTable.rows">
<td v-for="value in r">{{ value }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script src="/js/vue.js?v=3.5.13"></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 app = Vue.createApp({
data() {
return {
// false - означает что статистика не отправляется, true - отправляется
submitStatus: {
loggingIps: false,
loggingStatistics: false,
logView: false
},
params: {
loggingIps: false,
loggingStatistics: {
en: false,
logPeriodMs: 1000,
maxAgeMs: 0
}
},
logsTable: {
headers: [],
rows: []
},
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/)
// Первая строка содержит заголовки
this.logsTable.headers = lines.shift().split('\t')
// Остальные строки содержат данные
this.logsTable.rows = lines.map(line => line.split('\t'))
})
.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")
}
});
app.mount('#app')
</script>
</body>
</html>