201 lines
9.7 KiB
HTML
201 lines
9.7 KiB
HTML
<!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.loggingIps"></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>
|
||
<pre style="overflow-x: auto">{{ logfileContent }}</pre>
|
||
</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
|
||
}
|
||
},
|
||
logfileContent: "",
|
||
|
||
settingFetchComplete: false
|
||
}
|
||
},
|
||
methods: {
|
||
settingsSubmitLoggingIps() {
|
||
if (this.submitStatus.loggingIps) { return }
|
||
|
||
this.submitStatus.loggingIps = true
|
||
fetch(`/dev/cpapicall?f=SetDmaDebug¶m=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) => { this.logfileContent = await resp.text() })
|
||
.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> |