front_generator: нормальные отступы для всех включаемых файлов, добавление страницы с логами для TDMA-терминала

This commit is contained in:
2025-09-16 18:52:16 +03:00
parent 483c174cd5
commit fa7be880a7
21 changed files with 1013 additions and 923 deletions

View File

@@ -133,7 +133,8 @@
"tabs": [ "tabs": [
{"name": "monitoring", "desc": "Мониторинг"}, {"name": "monitoring", "desc": "Мониторинг"},
{"name": "setup", "desc": "Настройки"}, {"name": "setup", "desc": "Настройки"},
{"name": "admin", "desc": "Администрирование"} {"name": "admin", "desc": "Администрирование"},
{"name": "logs", "desc": "Журнал"}
] ]
}, },
"scpc": { "scpc": {

View File

@@ -1,4 +1,4 @@
async settingsUploadUpdate() { async settingsUploadUpdate() {
if (!this.uploadFw.filename) { if (!this.uploadFw.filename) {
alert('Выберите файл для загрузки'); alert('Выберите файл для загрузки');
return; return;
@@ -40,9 +40,9 @@
alert(`Ошибка загрузки файла: ${e}`); alert(`Ошибка загрузки файла: ${e}`);
} }
this.submitStatus.firmwareUpload = false this.submitStatus.firmwareUpload = false
}, },
async settingsPerformFirmwareUpgrade() { async settingsPerformFirmwareUpgrade() {
if (this.submitStatus.firmwareUpgrade) { return } if (this.submitStatus.firmwareUpgrade) { return }
this.submitStatus.firmwareUpgrade = true this.submitStatus.firmwareUpgrade = true
try { try {
@@ -51,9 +51,9 @@
console.log("failed to perform upgrade firmware: ", e) console.log("failed to perform upgrade firmware: ", e)
} }
this.submitStatus.firmwareUpgrade = false this.submitStatus.firmwareUpgrade = false
}, },
{% if modem == 'tdma' %} {% if modem == 'tdma' %}
async settingsPerformFirmwareUpgradeOta() { async settingsPerformFirmwareUpgradeOta() {
if (this.submitStatus.firmwareUpgradeOta) { return } if (this.submitStatus.firmwareUpgradeOta) { return }
this.submitStatus.firmwareUpgradeOta = true this.submitStatus.firmwareUpgradeOta = true
try { try {
@@ -62,8 +62,8 @@
console.log("failed to perform upgrade firmware: ", e) console.log("failed to perform upgrade firmware: ", e)
} }
this.submitStatus.firmwareUpgradeOta = false this.submitStatus.firmwareUpgradeOta = false
}, },
async settingsPerformSetCesPassword() { async settingsPerformSetCesPassword() {
if (this.submitStatus.cesPassword) { return } if (this.submitStatus.cesPassword) { return }
this.submitStatus.cesPassword = true this.submitStatus.cesPassword = true
try { try {
@@ -78,17 +78,17 @@
console.log("failed to perform set CES password: ", e) console.log("failed to perform set CES password: ", e)
} }
this.submitStatus.cesPassword = false this.submitStatus.cesPassword = false
}, },
{% endif %} {% endif %}
doModemReboot() { doModemReboot() {
if (this.submitStatus.modemReboot !== null) { if (this.submitStatus.modemReboot !== null) {
return return
} }
this.submitStatus.modemReboot = 30 this.submitStatus.modemReboot = 30
fetch('/api/reboot', { method: 'POST' }).then((r) => {}) fetch('/api/reboot', { method: 'POST' }).then((r) => {})
}, },
async restoreAllSettings() { async restoreAllSettings() {
// Порядок применения настроек // Порядок применения настроек
const settingsApplyOrder = ['qos', 'tcpaccel', 'dpdi', 'rxtx', 'buclnb', 'network']; const settingsApplyOrder = ['qos', 'tcpaccel', 'dpdi', 'rxtx', 'buclnb', 'network'];
@@ -198,8 +198,8 @@
// 4. Перезагрузка страницы // 4. Перезагрузка страницы
location.reload(); location.reload();
}, },
async dumpAllSettings() { async dumpAllSettings() {
function downloadAsFile(data, filename) { function downloadAsFile(data, filename) {
let a = document.createElement("a"); let a = document.createElement("a");
let file = new Blob([data], {type: 'application/json'}); let file = new Blob([data], {type: 'application/json'});
@@ -214,4 +214,5 @@
downloadAsFile(JSON.stringify(jres["settings"], null, 4), "backup-" + this.about.firmwareVersion + "-" + this.about.modemSn + ".json") downloadAsFile(JSON.stringify(jres["settings"], null, 4), "backup-" + this.about.firmwareVersion + "-" + this.about.modemSn + ".json")
} }
} }
}, },

View File

@@ -1,7 +1,7 @@
{% from 'common/widgets.j2' import build_widget %} {% from 'common/widgets.j2' import build_widget %}
<div class="tabs-body-item" v-if="activeTab === 'admin' && settingFetchComplete"> <div class="tabs-body-item" v-if="activeTab === 'admin' && settingFetchComplete">
{% if 'network' in params %} {% if 'network' in params %}
{% for w in params['network'] %}{{ build_widget('network', w) | indent(12, true) }}{% endfor %} {% for w in params['network'] %}{{ build_widget('network', w) | indent(4, true) }}{% endfor %}
{% endif %} {% endif %}
{% raw %} {% raw %}
<h2>Система</h2> <h2>Система</h2>
@@ -49,4 +49,5 @@
<button class="dangerous-button" v-show="statDevice.upgradePercent >= 100" @click="settingsPerformFirmwareUpgradeOta()">Обновить встроенное ПО<span class="submit-spinner" v-show="submitStatus.firmwareUpgradeOta"></span></button> <button class="dangerous-button" v-show="statDevice.upgradePercent >= 100" @click="settingsPerformFirmwareUpgradeOta()">Обновить встроенное ПО<span class="submit-spinner" v-show="submitStatus.firmwareUpgradeOta"></span></button>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View File

@@ -1,7 +1,7 @@
{% for g in paramGroups %} {% for g in paramGroups %}
param{{ g['group'] | title }}: { param{{ g['group'] | title }}: {
{% for p in g['params'] %} {% for p in g['params'] %}
{{ p['name'] }}: {{ p['initValue'] }}, {{ p['name'] }}: {{ p['initValue'] }},
{% endfor %} {% endfor %}
}, },
{% endfor %} {% endfor %}

View File

@@ -1,6 +1,6 @@
{% from 'common/widgets.j2' import build_getter_js, build_setter_js %} {% from 'common/widgets.j2' import build_getter_js, build_setter_js %}
{% for g in paramGroups %} {% for g in paramGroups %}
settingsSubmit{{ g['group'] | title }}() { settingsSubmit{{ g['group'] | title }}() {
if (this.submitStatus.{{ g['group'] }}) { return } if (this.submitStatus.{{ g['group'] }}) { return }
{% if g['group'] in dangerousParamGroups %} {% if g['group'] in dangerousParamGroups %}
{ if (!confirm("{{ dangerousParamGroups[g['group']] }}")) return } { if (!confirm("{{ dangerousParamGroups[g['group']] }}")) return }
@@ -17,14 +17,14 @@
.then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.update{{ g['group'] | title }}Settings(vals) }) .then(async (resp) => { let vals = await resp.json(); if (vals['status'] !== 'ok') { throw new Error(vals['error'] ? vals['error'] : "Server returns undefined error") } this.update{{ g['group'] | title }}Settings(vals) })
.catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) }) .catch((reason) => { alert(`Ошибка при применении настроек: ${reason}`) })
.finally(() => { this.submitStatus.{{ g['group'] }} = false }) .finally(() => { this.submitStatus.{{ g['group'] }} = false })
}, },
{% endfor %} {% endfor %}
{% for g in paramGroups %} {% for g in paramGroups %}
update{{ g['group'] | title }}Settings(vals) { update{{ g['group'] | title }}Settings(vals) {
this.submitStatus.{{ g['group'] }} = false this.submitStatus.{{ g['group'] }} = false
{% for p in g['params'] %} {% for p in g['params'] %}
{{ build_setter_js(g['group'], p, "vals[\"settings\"][\"" ~ g['group'] ~ "\"][\"" ~ p['name'] ~ "\"]") }} {{ build_setter_js(g['group'], p, "vals[\"settings\"][\"" ~ g['group'] ~ "\"][\"" ~ p['name'] ~ "\"]") }}
{% endfor %} {% endfor %}
}, },
{% endfor %} {% endfor %}

View File

@@ -0,0 +1,7 @@
paramLogs: {
submitClearLogs: false,
submitUpdateLogs: false,
data: "",
level: ""
},

View File

@@ -0,0 +1,12 @@
logsUpdate() {
if (this.paramLogs.submitUpdateLogs) { return }
this.paramLogs.submitUpdateLogs = true
fetch(`/api/get/manager.log?preview=1`, {method: 'GET', credentials: 'same-origin' })
.then(async (resp) => {
this.paramLogs.data = await resp.text()
})
.catch((reason) => { this.paramLogs.data = `Ошибка при чтении логов: ${reason}` })
.finally(() => { this.paramLogs.submitUpdateLogs = false })
},

View File

@@ -0,0 +1,17 @@
{% from 'common/widgets.j2' import build_widget %}
<div class="tabs-body-item" v-if="activeTab === 'logs'">
<h2>Журнал <code>manager</code></h2>
<div class="settings-set-container">
{{ build_widget('logs', {
"widget": "select", "label": "Фильтрация лога (отображение)", "name": "level",
"values": [{"label": "Без фильтрации", "value": "''"}, {"label": "Информация", "value": "'info'"}, {"label": "Предупреждение", "value": "'warning'"}, {"label": "Ошибка", "value": "'error'"}, {"label": "Фатальная ошибка", "value": "'fatal'"}]
}) | indent(8, true) }}
<button class="action-button" @click="logsUpdate()">Обновить (последние 1000 строк) <span class="submit-spinner" v-show="paramLogs.submitUpdateLogs"></span></button></button>
<a href="/api/get/manager.log" class="action-button" download>Скачать все</a>
<pre style="overflow-x: auto">{{ '{{ paramLogs.data }}' }}</pre>
<div><button class="action-button" v-if="paramLogs.data !== ''" @click="logsUpdate()">Обновить (последние 1000 строк) <span class="submit-spinner" v-show="paramLogs.submitUpdateLogs"></span></button></div>
</div>
<div class="settings-set-container statistics-container">
<div><button class="dangerous-button" @click="logsClear()">Очистить логи <span class="submit-spinner" v-show="paramLogs.submitClearLogs"></span></button></div>
</div>
</div>

View File

@@ -1,4 +1,4 @@
statRx: { statRx: {
// индикаторы // индикаторы
state: '?', // общее состояние state: '?', // общее состояние
sym_sync_lock: '?', // захват символьной sym_sync_lock: '?', // захват символьной
@@ -19,8 +19,8 @@
// статистика пакетов // статистика пакетов
packetsOk: '?', packetsBad: '?', packetsDummy: '?', packetsOk: '?', packetsBad: '?', packetsDummy: '?',
}, },
statTx: { statTx: {
// состояние // состояние
state: '?', state: '?',
@@ -30,21 +30,19 @@
{% else %} {% else %}
modcod: '?', speedOnTxKbit: '?', speedOnIifKbit: '?', centerFreq: '?', symSpeed: '?' modcod: '?', speedOnTxKbit: '?', speedOnIifKbit: '?', centerFreq: '?', symSpeed: '?'
{% endif %} {% endif %}
}, },
{% if modem == 'scpc' %} {% if modem == 'scpc' %}
statCinc: { statCinc: {
occ: '?', occ: '?',
correlator: null, correlator: null,
correlatorFails: '?', correlatorFails: '?',
freqErr: '?', freqErrAcc: '?', freqErr: '?', freqErrAcc: '?',
channelDelay: '?' channelDelay: '?'
}, },
{% endif %} {% endif %}
statDevice: { // температурные датчики statDevice: { // температурные датчики
adrv: 0, zynq: 0, fpga: 0 adrv: 0, zynq: 0, fpga: 0{% if modem == 'tdma' %}, upgradeStatus: "", upgradePercent: 0, upgradeImage: ""{% endif %}
{% if modem == 'tdma' %},
upgradeStatus: "", upgradePercent: 0, upgradeImage: "" },
{% endif %} statOs: {uptime: '?', load1: '?', load5: '?', load15: '?', totalram: '?', freeram: '?'},
},
statOs: {uptime: '?', load1: '?', load5: '?', load15: '?', totalram: '?', freeram: '?'},

View File

@@ -1,4 +1,4 @@
updateStatistics(vals) { updateStatistics(vals) {
function modcodToStr(modcod) { function modcodToStr(modcod) {
// модкоды из раздела 5.5.2.2 https://www.etsi.org/deliver/etsi_en/302300_302399/302307/01.01.02_60/en_302307v010102p.pdf // модкоды из раздела 5.5.2.2 https://www.etsi.org/deliver/etsi_en/302300_302399/302307/01.01.02_60/en_302307v010102p.pdf
const modcods = [ const modcods = [
@@ -96,9 +96,9 @@
this.statOs.load15 = Math.round(vals["state"]["device"]["load15min"] * 100) / 100 this.statOs.load15 = Math.round(vals["state"]["device"]["load15min"] * 100) / 100
this.statOs.totalram = vals["state"]["device"]["totalram"] this.statOs.totalram = vals["state"]["device"]["totalram"]
this.statOs.freeram = vals["state"]["device"]["freeram"] this.statOs.freeram = vals["state"]["device"]["freeram"]
}, },
resetPacketsStatistics() { resetPacketsStatistics() {
fetch('/api/resetPacketStatistics', { fetch('/api/resetPacketStatistics', {
method: 'POST', credentials: 'same-origin' method: 'POST', credentials: 'same-origin'
}).then(() => { }).then(() => {
@@ -106,5 +106,5 @@
this.statRx.packetsBad = 0 this.statRx.packetsBad = 0
this.statRx.packetsDummy = 0 this.statRx.packetsDummy = 0
}) })
}, },

View File

@@ -1,5 +1,4 @@
{% raw %} {% raw %}<div class="tabs-body-item tabs-item-flex-container" v-if="activeTab === 'monitoring'">
<div class="tabs-body-item tabs-item-flex-container" v-if="activeTab === 'monitoring'">
<div class="settings-set-container statistics-container"> <div class="settings-set-container statistics-container">
<h2>Статистика приема</h2> <h2>Статистика приема</h2>
<table> <table>
@@ -99,5 +98,5 @@
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
{% endraw %} {% endraw %}

View File

@@ -1,8 +1,9 @@
submitStatusQos: false, submitStatusQos: false,
paramQos: { paramQos: {
en: false, en: false,
rt1: [], rt1: [],
rt2: [], rt2: [],
rt3: [], rt3: [],
cd: [], cd: [],
}, },

View File

@@ -1,4 +1,4 @@
settingsSubmitQoS() { settingsSubmitQoS() {
if (this.submitStatusQos) { return } if (this.submitStatusQos) { return }
this.submitStatusQos = true this.submitStatusQos = true
function _translateQosClass(trafficClass, qc) { function _translateQosClass(trafficClass, qc) {
@@ -71,9 +71,9 @@
this.submitStatusQos = false this.submitStatusQos = false
alert(`Ошибка при применении настроек: ${reason}`) alert(`Ошибка при применении настроек: ${reason}`)
}) })
}, },
updateQosSettings(vals) { updateQosSettings(vals) {
this.submitStatusQos = false this.submitStatusQos = false
this.paramQos.en = vals["settings"]["qos"]["en"] this.paramQos.en = vals["settings"]["qos"]["en"]
@@ -126,9 +126,9 @@
} }
} }
} }
}, },
qosAddClass(name) { qosAddClass(name) {
let res = { let res = {
isEnabled: true, isEnabled: true,
cir: 0, cir: 0,
@@ -142,9 +142,9 @@
case 'rt3': this.paramQos.rt3.push(res); break case 'rt3': this.paramQos.rt3.push(res); break
case 'cd': this.paramQos.cd.push(res); break case 'cd': this.paramQos.cd.push(res); break
} }
}, },
qosClassAddRule(name, index) { qosClassAddRule(name, index) {
let rule = { let rule = {
isEnabled: true, isEnabled: true,
vlan: "", vlan: "",
@@ -161,27 +161,27 @@
case 'rt3': this.paramQos.rt3[index].filters.push(rule); break case 'rt3': this.paramQos.rt3[index].filters.push(rule); break
case 'cd': this.paramQos.cd[index].filters.push(rule); break case 'cd': this.paramQos.cd[index].filters.push(rule); break
} }
}, },
qosDelClass(name, index) { qosDelClass(name, index) {
switch (name) { switch (name) {
case 'rt1': this.paramQos.rt1.splice(index, 1); break case 'rt1': this.paramQos.rt1.splice(index, 1); break
case 'rt2': this.paramQos.rt2.splice(index, 1); break case 'rt2': this.paramQos.rt2.splice(index, 1); break
case 'rt3': this.paramQos.rt3.splice(index, 1); break case 'rt3': this.paramQos.rt3.splice(index, 1); break
case 'cd': this.paramQos.cd.splice(index, 1); break case 'cd': this.paramQos.cd.splice(index, 1); break
} }
}, },
qosDelFilter(name, index, filterIndex) { qosDelFilter(name, index, filterIndex) {
switch (name) { switch (name) {
case 'rt1': this.paramQos.rt1[index].filters.splice(filterIndex, 1); break case 'rt1': this.paramQos.rt1[index].filters.splice(filterIndex, 1); break
case 'rt2': this.paramQos.rt2[index].filters.splice(filterIndex, 1); break case 'rt2': this.paramQos.rt2[index].filters.splice(filterIndex, 1); break
case 'rt3': this.paramQos.rt3[index].filters.splice(filterIndex, 1); break case 'rt3': this.paramQos.rt3[index].filters.splice(filterIndex, 1); break
case 'cd': this.paramQos.cd[index].filters.splice(filterIndex, 1); break case 'cd': this.paramQos.cd[index].filters.splice(filterIndex, 1); break
} }
}, },
qosGenerateRuleDescription(filter) { qosGenerateRuleDescription(filter) {
// попытка 1: просто отобразить все фильтры // попытка 1: просто отобразить все фильтры
let result = "" let result = ""
let isFirst = true; let isFirst = true;
@@ -220,5 +220,5 @@
} }
return result return result
}, },

View File

@@ -1,6 +1,6 @@
{% from 'common/widgets.j2' import build_widget %} {% from 'common/widgets.j2' import build_widget %}
{% raw %} {% raw %}
<div class="tabs-body-item" v-if="activeTab === 'qos' && settingFetchComplete"> <div class="tabs-body-item" v-if="activeTab === 'qos' && settingFetchComplete">
<h2>Настройки QoS</h2> <h2>Настройки QoS</h2>
<div class="settings-set-container"> <div class="settings-set-container">
<label> <label>
@@ -102,6 +102,6 @@
<button class="action-button" @click="settingsSubmitQoS()">Применить <span class="submit-spinner" v-show="submitStatusQos"></span></button> <button class="action-button" @click="settingsSubmitQoS()">Применить <span class="submit-spinner" v-show="submitStatusQos"></span></button>
{% endraw %}{% if 'tcpaccel' in params %} {% endraw %}{% if 'tcpaccel' in params %}
{% for w in params['tcpaccel'] %}{{ build_widget('tcpaccel', w) | indent(12, true) }}{% endfor %} {% for w in params['tcpaccel'] %}{{ build_widget('tcpaccel', w) | indent(4, true) }}{% endfor %}
{% endif %} {% endif %}
</div> </div>

View File

@@ -1,5 +1,5 @@
{% if 'rxtx' in params and modem == 'scpc' %} {% if 'rxtx' in params and modem == 'scpc' %}
calcRequiredSnr(frameSizeNormal, modulation, speed) { calcRequiredSnr(frameSizeNormal, modulation, speed) {
const snrValues = [ const snrValues = [
{fs: true, mod: 'qpsk', speed: '1/4', snr: 2.6}, {fs: true, mod: 'qpsk', speed: '1/3', snr: 2.6}, {fs: true, mod: 'qpsk', speed: '2/5', snr: 2.6}, {fs: true, mod: 'qpsk', speed: '1/2', snr: 2.6}, {fs: true, mod: 'qpsk', speed: '3/5', snr: 3.1}, {fs: true, mod: 'qpsk', speed: '2/3', snr: 3.8}, {fs: true, mod: 'qpsk', speed: '3/4', snr: 4.5}, {fs: true, mod: 'qpsk', speed: '4/5', snr: 5.2}, {fs: true, mod: 'qpsk', speed: '5/6', snr: 5.5}, {fs: true, mod: 'qpsk', speed: '8/9', snr: 6.4}, {fs: true, mod: 'qpsk', speed: '9/10', snr: 6.7}, {fs: true, mod: 'qpsk', speed: '1/4', snr: 2.6}, {fs: true, mod: 'qpsk', speed: '1/3', snr: 2.6}, {fs: true, mod: 'qpsk', speed: '2/5', snr: 2.6}, {fs: true, mod: 'qpsk', speed: '1/2', snr: 2.6}, {fs: true, mod: 'qpsk', speed: '3/5', snr: 3.1}, {fs: true, mod: 'qpsk', speed: '2/3', snr: 3.8}, {fs: true, mod: 'qpsk', speed: '3/4', snr: 4.5}, {fs: true, mod: 'qpsk', speed: '4/5', snr: 5.2}, {fs: true, mod: 'qpsk', speed: '5/6', snr: 5.5}, {fs: true, mod: 'qpsk', speed: '8/9', snr: 6.4}, {fs: true, mod: 'qpsk', speed: '9/10', snr: 6.7},
{fs: true, mod: '8psk', speed: '3/5', snr: 7.4}, {fs: true, mod: '8psk', speed: '2/3', snr: 8.4}, {fs: true, mod: '8psk', speed: '3/4', snr: 8.7}, {fs: true, mod: '8psk', speed: '5/6', snr: 10}, {fs: true, mod: '8psk', speed: '8/9', snr: 10.9}, {fs: true, mod: '8psk', speed: '9/10', snr: 11.1}, {fs: true, mod: '8psk', speed: '3/5', snr: 7.4}, {fs: true, mod: '8psk', speed: '2/3', snr: 8.4}, {fs: true, mod: '8psk', speed: '3/4', snr: 8.7}, {fs: true, mod: '8psk', speed: '5/6', snr: 10}, {fs: true, mod: '8psk', speed: '8/9', snr: 10.9}, {fs: true, mod: '8psk', speed: '9/10', snr: 11.1},
@@ -15,8 +15,8 @@
if (snrValues[i].fs === frameSizeNormal && snrValues[i].mod === modulation && snrValues[i].speed === speed) { return snrValues[i].snr } if (snrValues[i].fs === frameSizeNormal && snrValues[i].mod === modulation && snrValues[i].speed === speed) { return snrValues[i].snr }
} }
return '?' return '?'
}, },
calcInterfaceSpeedKb(baud, modulation, speed, frameSizeNormal) { calcInterfaceSpeedKb(baud, modulation, speed, frameSizeNormal) {
const mBaud = parseInt(baud.replace(/[^0-9]/g, '')) const mBaud = parseInt(baud.replace(/[^0-9]/g, ''))
const mMod = Math.max(2, ['', '', 'qpsk', '8psk', '16apsk', '32apsk'].indexOf(modulation)) const mMod = Math.max(2, ['', '', 'qpsk', '8psk', '16apsk', '32apsk'].indexOf(modulation))
const speedVals = {'1/4': 0.25, '1/3': 0.333, '2/5': 0.4, '1/2': 0.5, '3/5': 0.6, '2/3': 0.666, '3/4': 0.75, '4/5': 0.8, '5/6': 0.833, '8/9': 0.888, '9/10': 0.9} const speedVals = {'1/4': 0.25, '1/3': 0.333, '2/5': 0.4, '1/2': 0.5, '3/5': 0.6, '2/3': 0.666, '3/4': 0.75, '4/5': 0.8, '5/6': 0.833, '8/9': 0.888, '9/10': 0.9}
@@ -30,5 +30,5 @@
} else { } else {
return toLocaleStringWithSpaces(result) + ' кбит/с; ' + snr return toLocaleStringWithSpaces(result) + ' кбит/с; ' + snr
} }
}, },
{% endif %} {% endif %}

View File

@@ -1,8 +1,8 @@
{% from 'common/widgets.j2' import build_widget %} {% from 'common/widgets.j2' import build_widget %}
<div class="tabs-body-item" v-if="activeTab === 'setup' && settingFetchComplete"> <div class="tabs-body-item" v-if="activeTab === 'setup' && settingFetchComplete">
{% for cat in ['rxtx', 'dpdi', 'buclnb'] %} {% for cat in ['rxtx', 'dpdi', 'buclnb'] %}
{% if cat in params %} {% if cat in params %}
{% for w in params[cat] %}{{ build_widget(cat, w) | indent(12, true) }}{% endfor %} {% for w in params[cat] %}{{ build_widget(cat, w) | indent(4, true) }}{% endfor %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</div> </div>

View File

@@ -1,5 +1,18 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
{% macro include_macro(file) %}
// ========== include from '{{ file }}'
{% include file %}
// ========== include end from '{{ file }}'
{% endmacro %}
{% macro include_macro_no_comment(file) %}
{% include file %}
{% endmacro %}
{% macro include_macro_ignore_missing(file) %}
// ========== include from '{{ file }}'
{% include file ignore missing %}
// ========== include end from '{{ file }}'
{% endmacro %}
<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">
@@ -53,7 +66,7 @@
</div> </div>
</header> </header>
<div id="content"> <div id="content">
{% for tab in header_tabs %}{% include 'common/' ~ tab.name ~ '.html.j2' %}{% endfor %} {% for tab in header_tabs %}{{ include_macro_no_comment('common/' ~ tab.name ~ '.html.j2') | indent(8, true) }}{% endfor %}
{% raw %} {% raw %}
<p>Последнее обновление статистики: {{ lastUpdateTime }}</p> <p>Последнее обновление статистики: {{ lastUpdateTime }}</p>
</div> </div>
@@ -108,15 +121,9 @@
cesPasswordValue: '', cesPasswordValue: '',
{% endif %} {% endif %}
// ========== include from 'common/all-params-data.js.j2' {{ include_macro('common/all-params-data.js.j2') | indent(16, true) }}
{% include 'common/all-params-data.js.j2' %}
// ========== include end from 'common/all-params-data.js.j2'
{% for tab in header_tabs %} {% for tab in header_tabs %}
// ========== include from '{{ 'common/' ~ tab.name ~ '-data.js.j2' }}' {{ include_macro_ignore_missing('common/' ~ tab.name ~ '-data.js.j2') | indent(16, true) }}
{% include 'common/' ~ tab.name ~ '-data.js.j2' ignore missing %}
// ========== include end from '{{ 'common/' ~ tab.name ~ '-data.js.j2' }}'
{% endfor %} {% endfor %}
uploadFw: { uploadFw: {
progress: null, progress: null,
@@ -183,15 +190,9 @@
return toLocaleStringWithSpaces(result) return toLocaleStringWithSpaces(result)
}, },
// ========== include from 'common/all-params-methods.js.j2' {{ include_macro('common/all-params-methods.js.j2') | indent(12, true) }}
{% include 'common/all-params-methods.js.j2' %}
// ========== include end from 'common/all-params-methods.js.j2'
{% for tab in header_tabs %} {% for tab in header_tabs %}
// ========== include from '{{ 'common/' ~ tab.name ~ '-methods.js.j2' }}' {{ include_macro_ignore_missing('common/' ~ tab.name ~ '-methods.js.j2') | indent(12, true) }}
{% include 'common/' ~ tab.name ~ '-methods.js.j2' ignore missing %}
// ========== include end from '{{ 'common/' ~ tab.name ~ '-methods.js.j2' }}'
{% endfor %} {% endfor %}
performUpdateSettings() { performUpdateSettings() {

View File

@@ -26,5 +26,9 @@
#define API_OBJECT_BUCLNB_SETTINGS_ENABLE #define API_OBJECT_BUCLNB_SETTINGS_ENABLE
#endif #endif
#if defined(MODEM_IS_TDMA)
#define API_OBJECT_MANAGER_LOGS_ENABLE
#endif
#endif //API_DRIVER_STRICTS_ENABLE_H #endif //API_DRIVER_STRICTS_ENABLE_H

View File

@@ -52,7 +52,6 @@
</div> </div>
</header> </header>
<div id="content"> <div id="content">
<div class="tabs-body-item tabs-item-flex-container" v-if="activeTab === 'monitoring'"> <div class="tabs-body-item tabs-item-flex-container" v-if="activeTab === 'monitoring'">
<div class="settings-set-container statistics-container"> <div class="settings-set-container statistics-container">
<h2>Статистика приема</h2> <h2>Статистика приема</h2>
@@ -454,7 +453,7 @@
<label class="l3-proto-label"><span>DCCP:</span><input type="checkbox" value="dccp" v-model="filter.proto"></label> <label class="l3-proto-label"><span>DCCP:</span><input type="checkbox" value="dccp" v-model="filter.proto"></label>
<label class="l3-proto-label"><span>ESP:</span><input type="checkbox" value="esp" v-model="filter.proto"></label> <label class="l3-proto-label"><span>ESP:</span><input type="checkbox" value="esp" v-model="filter.proto"></label>
<label class="l3-proto-label"><span>ICMP:</span><input type="checkbox" value="icmp" v-model="filter.proto"></label> <label class="l3-proto-label"><span>ICMP:</span><input type="checkbox" value="icmp" v-model="filter.proto"></label>
<!-- <label class="l3-proto-label"><span>ICMPV6:</span><input type="checkbox" value="icmpv6" v-model="filter.proto"></label>--> <!-- <label class="l3-proto-label"><span>ICMPV6:</span><input type="checkbox" value="icmpv6" v-model="filter.proto"></label>-->
<label class="l3-proto-label"><span>SCTP:</span><input type="checkbox" value="sctp" v-model="filter.proto"></label> <label class="l3-proto-label"><span>SCTP:</span><input type="checkbox" value="sctp" v-model="filter.proto"></label>
<label class="l3-proto-label"><span>TCP:</span><input type="checkbox" value="tcp" v-model="filter.proto"></label> <label class="l3-proto-label"><span>TCP:</span><input type="checkbox" value="tcp" v-model="filter.proto"></label>
<label class="l3-proto-label"><span>UDP:</span><input type="checkbox" value="udp" v-model="filter.proto"></label> <label class="l3-proto-label"><span>UDP:</span><input type="checkbox" value="udp" v-model="filter.proto"></label>
@@ -549,6 +548,7 @@
<button class="dangerous-button" v-show="uploadFw.sha256 !== null" @click="settingsPerformFirmwareUpgrade()">Обновить встроенное ПО<span class="submit-spinner" v-show="submitStatus.firmwareUpgrade"></span></button> <button class="dangerous-button" v-show="uploadFw.sha256 !== null" @click="settingsPerformFirmwareUpgrade()">Обновить встроенное ПО<span class="submit-spinner" v-show="submitStatus.firmwareUpgrade"></span></button>
</div> </div>
</div> </div>
<p>Последнее обновление статистики: {{ lastUpdateTime }}</p> <p>Последнее обновление статистики: {{ lastUpdateTime }}</p>
</div> </div>
</div> </div>
@@ -710,7 +710,8 @@
rt2: [], rt2: [],
rt3: [], rt3: [],
cd: [], cd: [],
}, // ========== include end from 'common/qos-data.js.j2' },
// ========== include end from 'common/qos-data.js.j2'
// ========== include from 'common/admin-data.js.j2' // ========== include from 'common/admin-data.js.j2'
// ========== include end from 'common/admin-data.js.j2' // ========== include end from 'common/admin-data.js.j2'
@@ -1492,7 +1493,8 @@
downloadAsFile(JSON.stringify(jres["settings"], null, 4), "backup-" + this.about.firmwareVersion + "-" + this.about.modemSn + ".json") downloadAsFile(JSON.stringify(jres["settings"], null, 4), "backup-" + this.about.firmwareVersion + "-" + this.about.modemSn + ".json")
} }
} }
}, // ========== include end from 'common/admin-methods.js.j2' },
// ========== include end from 'common/admin-methods.js.j2'
performUpdateSettings() { performUpdateSettings() {

View File

@@ -51,7 +51,6 @@
</div> </div>
</header> </header>
<div id="content"> <div id="content">
<div class="tabs-body-item tabs-item-flex-container" v-if="activeTab === 'monitoring'"> <div class="tabs-body-item tabs-item-flex-container" v-if="activeTab === 'monitoring'">
<div class="settings-set-container statistics-container"> <div class="settings-set-container statistics-container">
<h2>Статистика приема</h2> <h2>Статистика приема</h2>
@@ -310,6 +309,7 @@
<button class="dangerous-button" v-show="uploadFw.sha256 !== null" @click="settingsPerformFirmwareUpgrade()">Обновить встроенное ПО<span class="submit-spinner" v-show="submitStatus.firmwareUpgrade"></span></button> <button class="dangerous-button" v-show="uploadFw.sha256 !== null" @click="settingsPerformFirmwareUpgrade()">Обновить встроенное ПО<span class="submit-spinner" v-show="submitStatus.firmwareUpgrade"></span></button>
</div> </div>
</div> </div>
<p>Последнее обновление статистики: {{ lastUpdateTime }}</p> <p>Последнее обновление статистики: {{ lastUpdateTime }}</p>
</div> </div>
</div> </div>
@@ -903,7 +903,8 @@
downloadAsFile(JSON.stringify(jres["settings"], null, 4), "backup-" + this.about.firmwareVersion + "-" + this.about.modemSn + ".json") downloadAsFile(JSON.stringify(jres["settings"], null, 4), "backup-" + this.about.firmwareVersion + "-" + this.about.modemSn + ".json")
} }
} }
}, // ========== include end from 'common/admin-methods.js.j2' },
// ========== include end from 'common/admin-methods.js.j2'
performUpdateSettings() { performUpdateSettings() {

View File

@@ -47,11 +47,11 @@
<a href="#monitoring" class="tabs-btn" @click="activeTab = 'monitoring'" :class="{ active: activeTab === 'monitoring' }">Мониторинг</a> <a href="#monitoring" class="tabs-btn" @click="activeTab = 'monitoring'" :class="{ active: activeTab === 'monitoring' }">Мониторинг</a>
<a href="#setup" class="tabs-btn" @click="activeTab = 'setup'" :class="{ active: activeTab === 'setup' }">Настройки</a> <a href="#setup" class="tabs-btn" @click="activeTab = 'setup'" :class="{ active: activeTab === 'setup' }">Настройки</a>
<a href="#admin" class="tabs-btn" @click="activeTab = 'admin'" :class="{ active: activeTab === 'admin' }">Администрирование</a> <a href="#admin" class="tabs-btn" @click="activeTab = 'admin'" :class="{ active: activeTab === 'admin' }">Администрирование</a>
<a href="#logs" class="tabs-btn" @click="activeTab = 'logs'" :class="{ active: activeTab === 'logs' }">Журнал</a>
<a href="/logout" class="tabs-btn">Выход</a> <a href="/logout" class="tabs-btn">Выход</a>
</div> </div>
</header> </header>
<div id="content"> <div id="content">
<div class="tabs-body-item tabs-item-flex-container" v-if="activeTab === 'monitoring'"> <div class="tabs-body-item tabs-item-flex-container" v-if="activeTab === 'monitoring'">
<div class="settings-set-container statistics-container"> <div class="settings-set-container statistics-container">
<h2>Статистика приема</h2> <h2>Статистика приема</h2>
@@ -297,12 +297,35 @@
<button class="dangerous-button" v-show="statDevice.upgradePercent >= 100" @click="settingsPerformFirmwareUpgradeOta()">Обновить встроенное ПО<span class="submit-spinner" v-show="submitStatus.firmwareUpgradeOta"></span></button> <button class="dangerous-button" v-show="statDevice.upgradePercent >= 100" @click="settingsPerformFirmwareUpgradeOta()">Обновить встроенное ПО<span class="submit-spinner" v-show="submitStatus.firmwareUpgradeOta"></span></button>
</div> </div>
</div> </div>
<div class="tabs-body-item" v-if="activeTab === 'logs'">
<h2>Журнал <code>manager</code></h2>
<div class="settings-set-container">
<label>
<span>Фильтрация лога (отображение)</span>
<select v-model="paramLogs.level">
<option :value="''">Без фильтрации</option>
<option :value="'info'">Информация</option>
<option :value="'warning'">Предупреждение</option>
<option :value="'error'">Ошибка</option>
<option :value="'fatal'">Фатальная ошибка</option>
</select>
</label>
<button class="action-button" @click="logsUpdate()">Обновить (последние 1000 строк) <span class="submit-spinner" v-show="paramLogs.submitUpdateLogs"></span></button></button>
<a href="/api/get/manager.log" class="action-button" download>Скачать все</a>
<pre style="overflow-x: auto">{{ paramLogs.data }}</pre>
<div><button class="action-button" v-if="paramLogs.data !== ''" @click="logsUpdate()">Обновить (последние 1000 строк) <span class="submit-spinner" v-show="paramLogs.submitUpdateLogs"></span></button></div>
</div>
<div class="settings-set-container statistics-container">
<div><button class="dangerous-button" @click="logsClear()">Очистить логи <span class="submit-spinner" v-show="paramLogs.submitClearLogs"></span></button></div>
</div>
</div>
<p>Последнее обновление статистики: {{ lastUpdateTime }}</p> <p>Последнее обновление статистики: {{ lastUpdateTime }}</p>
</div> </div>
</div> </div>
<script src="/js/vue.js?v=3.5.13"></script> <script src="/js/vue.js?v=3.5.13"></script>
<script> <script>
const availableTabs = ['monitoring', 'setup', 'admin'] const availableTabs = ['monitoring', 'setup', 'admin', 'logs']
// для обновления высоты хидера // для обновления высоты хидера
function updateHeaderHeight() { const header = document.querySelector('header'); document.body.style.setProperty('--header-height', `${header.offsetHeight}px`); } function updateHeaderHeight() { const header = document.querySelector('header'); document.body.style.setProperty('--header-height', `${header.offsetHeight}px`); }
@@ -414,9 +437,7 @@
modcod: '?', speedOnTxKbit: '?', speedOnIifKbit: '?', centerFreq: '?', symSpeed: '?' modcod: '?', speedOnTxKbit: '?', speedOnIifKbit: '?', centerFreq: '?', symSpeed: '?'
}, },
statDevice: { // температурные датчики statDevice: { // температурные датчики
adrv: 0, zynq: 0, fpga: 0 adrv: 0, zynq: 0, fpga: 0, upgradeStatus: "", upgradePercent: 0, upgradeImage: ""
,
upgradeStatus: "", upgradePercent: 0, upgradeImage: ""
}, },
statOs: {uptime: '?', load1: '?', load5: '?', load15: '?', totalram: '?', freeram: '?'}, statOs: {uptime: '?', load1: '?', load5: '?', load15: '?', totalram: '?', freeram: '?'},
// ========== include end from 'common/monitoring-data.js.j2' // ========== include end from 'common/monitoring-data.js.j2'
@@ -427,6 +448,15 @@
// ========== include from 'common/admin-data.js.j2' // ========== include from 'common/admin-data.js.j2'
// ========== include end from 'common/admin-data.js.j2' // ========== include end from 'common/admin-data.js.j2'
// ========== include from 'common/logs-data.js.j2'
paramLogs: {
submitClearLogs: false,
submitUpdateLogs: false,
data: "",
level: ""
},
// ========== include end from 'common/logs-data.js.j2'
uploadFw: { uploadFw: {
progress: null, progress: null,
filename: null, filename: null,
@@ -918,7 +948,22 @@
downloadAsFile(JSON.stringify(jres["settings"], null, 4), "backup-" + this.about.firmwareVersion + "-" + this.about.modemSn + ".json") downloadAsFile(JSON.stringify(jres["settings"], null, 4), "backup-" + this.about.firmwareVersion + "-" + this.about.modemSn + ".json")
} }
} }
}, // ========== include end from 'common/admin-methods.js.j2' },
// ========== include end from 'common/admin-methods.js.j2'
// ========== include from 'common/logs-methods.js.j2'
logsUpdate() {
if (this.paramLogs.submitUpdateLogs) { return }
this.paramLogs.submitUpdateLogs = true
fetch(`/api/get/manager.log?preview=1`, {method: 'GET', credentials: 'same-origin' })
.then(async (resp) => {
this.paramLogs.data = await resp.text()
})
.catch((reason) => { this.paramLogs.data = `Ошибка при чтении логов: ${reason}` })
.finally(() => { this.paramLogs.submitUpdateLogs = false })
},
// ========== include end from 'common/logs-methods.js.j2'
performUpdateSettings() { performUpdateSettings() {