some improvements

This commit is contained in:
Ayzen
2025-09-30 19:02:11 +03:00
parent facff2b4f8
commit 6b30e8a6fd
26 changed files with 12385 additions and 12088 deletions

View File

@ -588,6 +588,88 @@ async def clear_current_reference() -> dict[str, Any]:
raise HTTPException(status_code=500, detail=str(exc))
@router.get("/reference/{reference_name}", response_model=ReferenceModel)
async def get_reference(reference_name: str, preset_filename: str | None = None) -> ReferenceModel:
"""Get information about a specific reference."""
try:
settings_manager = singletons.settings_manager
if preset_filename:
preset = next(
(p for p in settings_manager.get_available_presets() if p.filename == preset_filename),
None
)
if not preset:
raise HTTPException(status_code=404, detail=f"Preset '{preset_filename}' not found")
else:
preset = settings_manager.get_current_preset()
if not preset:
raise HTTPException(status_code=400, detail="No current preset set and no preset_filename provided")
info = settings_manager.get_reference_info(reference_name, preset)
if not info:
raise HTTPException(status_code=404, detail=f"Reference '{reference_name}' not found")
return ReferenceModel(
name=info.name,
timestamp=info.timestamp.isoformat(),
preset_filename=info.preset_filename,
description=info.description,
metadata=info.metadata,
)
except HTTPException:
raise
except Exception as exc: # noqa: BLE001
logger.error("Failed to get reference info", name=reference_name, error=repr(exc))
raise HTTPException(status_code=500, detail=str(exc))
@router.get("/reference/{reference_name}/plot")
async def get_reference_plot(reference_name: str, preset_filename: str | None = None) -> dict[str, Any]:
"""Get magnitude plot for a reference measurement."""
try:
settings_manager = singletons.settings_manager
if preset_filename:
preset = next(
(p for p in settings_manager.get_available_presets() if p.filename == preset_filename),
None
)
if not preset:
raise HTTPException(status_code=404, detail=f"Preset '{preset_filename}' not found")
else:
preset = settings_manager.get_current_preset()
if not preset:
raise HTTPException(status_code=400, detail="No current preset set and no preset_filename provided")
reference_info = settings_manager.get_reference_info(reference_name, preset)
if not reference_info:
raise HTTPException(status_code=404, detail=f"Reference '{reference_name}' not found")
sweep_data = settings_manager.reference_manager.get_reference_sweep(reference_name, preset)
if not sweep_data:
raise HTTPException(status_code=404, detail=f"Reference data not found for '{reference_name}'")
from vna_system.core.visualization.magnitude_chart import generate_magnitude_plot_from_sweep_data
plot_fig = generate_magnitude_plot_from_sweep_data(sweep_data, preset)
if "error" in plot_fig:
raise HTTPException(status_code=500, detail=plot_fig["error"])
return {
"reference_name": reference_name,
"preset": {"filename": preset.filename, "mode": preset.mode.value},
"plot": plot_fig,
"timestamp": reference_info.timestamp.isoformat(),
"description": reference_info.description
}
except HTTPException:
raise
except Exception as exc: # noqa: BLE001
logger.error("Failed to get reference plot", name=reference_name, error=repr(exc))
raise HTTPException(status_code=500, detail=str(exc))
@router.delete("/reference/{reference_name}")
async def delete_reference(reference_name: str, preset_filename: str | None = None) -> dict[str, Any]:
"""Delete a reference."""
@ -622,6 +704,17 @@ async def delete_reference(reference_name: str, preset_filename: str | None = No
raise HTTPException(status_code=500, detail=str(exc))
@router.delete("/calibration/current")
async def clear_current_calibration() -> dict[str, Any]:
"""Clear the current calibration selection."""
try:
singletons.settings_manager.calibration_manager.clear_current_calibration()
return {"success": True, "message": "Current calibration cleared"}
except Exception as exc: # noqa: BLE001
logger.error("Failed to clear current calibration", error=repr(exc))
raise HTTPException(status_code=500, detail=str(exc))
@router.delete("/calibration/{calibration_name}")
async def delete_calibration(calibration_name: str, preset_filename: str | None = None) -> dict[str, Any]:
"""Delete a calibration set."""

View File

@ -23,6 +23,7 @@ class SettingsStatusModel(BaseModel):
working_calibration: Dict[str, Any] | None
available_presets: int
available_calibrations: int
available_references: int
class SetPresetRequest(BaseModel):

View File

@ -1 +1 @@
s11_start100_stop8800_points1000_bw1khz/еуыеуые
s11_start100_stop8800_points1000_bw1khz/вфыввф

View File

@ -7,12 +7,12 @@
"points": 1000,
"bandwidth": 1000.0
},
"calibration_name": "tuncTuncTuncSahur",
"calibration_name": "вфыввф",
"standards": [
"open",
"short",
"load"
],
"created_timestamp": "2025-09-24T17:55:12.053850",
"created_timestamp": "2025-09-30T18:55:17.618157",
"is_complete": true
}

View File

@ -7,10 +7,10 @@
"points": 1000,
"bandwidth": 1000.0
},
"calibration_name": "tuncTuncTuncSahur",
"calibration_name": "вфыввф",
"standard": "load",
"sweep_number": 1221,
"sweep_timestamp": 1758725709.366605,
"created_timestamp": "2025-09-24T17:55:12.053789",
"sweep_number": 33,
"sweep_timestamp": 1759247715.1613321,
"created_timestamp": "2025-09-30T18:55:17.618031",
"total_points": 1000
}

View File

@ -7,10 +7,10 @@
"points": 1000,
"bandwidth": 1000.0
},
"calibration_name": "tuncTuncTuncSahur",
"calibration_name": "вфыввф",
"standard": "open",
"sweep_number": 1217,
"sweep_timestamp": 1758725700.971026,
"created_timestamp": "2025-09-24T17:55:12.049478",
"sweep_number": 18,
"sweep_timestamp": 1759247683.8793015,
"created_timestamp": "2025-09-30T18:55:17.612864",
"total_points": 1000
}

View File

@ -7,10 +7,10 @@
"points": 1000,
"bandwidth": 1000.0
},
"calibration_name": "tuncTuncTuncSahur",
"calibration_name": "вфыввф",
"standard": "short",
"sweep_number": 1219,
"sweep_timestamp": 1758725705.1671622,
"created_timestamp": "2025-09-24T17:55:12.051603",
"sweep_number": 19,
"sweep_timestamp": 1759247685.9623504,
"created_timestamp": "2025-09-30T18:55:17.615536",
"total_points": 1000
}

View File

@ -4,7 +4,7 @@
"data_limitation": "ph_only_1",
"cut": 2.0,
"max": 5.0,
"gain": 2.2,
"gain": 0.0,
"start_freq": 100.0,
"stop_freq": 8800.0,
"clear_history": false,

View File

@ -188,6 +188,13 @@ class VNASettingsManager:
raise ValueError("No current preset available")
return self.reference_manager.delete_reference(reference_name, current_preset)
def get_reference_info(self, reference_name: str, preset: ConfigPreset | None = None) -> ReferenceInfo | None:
"""Get detailed information about a reference."""
current_preset = preset or self.get_current_preset()
if not current_preset:
raise ValueError("No current preset available")
return self.reference_manager.get_reference_info(reference_name, current_preset)
def delete_calibration(self, preset: ConfigPreset, calibration_name: str) -> None:
"""Delete a saved calibration for the given preset."""
self.calibration_manager.delete_calibration(preset, calibration_name)
@ -309,7 +316,8 @@ class VNASettingsManager:
"current_calibration": None,
"working_calibration": None,
"available_presets": len(self.get_available_presets()),
"available_calibrations": 0
"available_calibrations": 0,
"available_references": 0,
}
if current_preset:
@ -322,6 +330,7 @@ class VNASettingsManager:
"bandwidth": current_preset.bandwidth
}
summary["available_calibrations"] = len(self.get_available_calibrations(current_preset))
summary["available_references"] = len(self.get_available_references(current_preset))
if current_calibration:
summary["current_calibration"] = {

View File

@ -1 +1 @@
s21_start100_stop8800_points1000_bw1khz/ффывфы
s11_start100_stop8800_points1000_bw1khz/asd

View File

@ -431,7 +431,7 @@
/* Notifications */
.notifications {
position: fixed;
top: var(--space-4);
top: calc(var(--header-height) + var(--space-2));
right: var(--space-4);
z-index: var(--z-toast);
display: flex;

View File

@ -518,6 +518,11 @@
margin-top: var(--space-3);
}
#currentReferenceInfo {
display: none;
flex-direction: column;
}
/* Reference details */
.reference-details {
margin-top: var(--space-3);

View File

@ -85,7 +85,7 @@ class VNADashboard {
this.notifications.show({
type: 'info',
title: 'Панель готова',
message: 'Соединение с системой ВНА установлено. Ожидание данных свипа...'
message: 'Соединение с системой установлено. Ожидание данных свипа...'
});
} catch (error) {

View File

@ -75,6 +75,7 @@ export const API = {
CALIBRATION_START: `${API_BASE}/settings/calibration/start`,
CALIBRATION_SAVE: `${API_BASE}/settings/calibration/save`,
CALIBRATION_SET: `${API_BASE}/settings/calibration/set`,
CALIBRATION_CLEAR_CURRENT: `${API_BASE}/settings/calibration/current`,
CALIBRATION_DELETE: (name) => `${API_BASE}/settings/calibration/${encodeURIComponent(name)}`,
CALIBRATION_ADD_STANDARD: `${API_BASE}/settings/calibration/add-standard`,
CALIBRATION_STANDARDS_PLOTS: (name) => `${API_BASE}/settings/calibration/${encodeURIComponent(name)}/standards-plots`,
@ -86,7 +87,8 @@ export const API = {
REFERENCE_CREATE: `${API_BASE}/settings/reference/create`,
REFERENCE_SET: `${API_BASE}/settings/reference/set`,
REFERENCE_CURRENT: `${API_BASE}/settings/reference/current`,
REFERENCE_ITEM: (name) => `${API_BASE}/settings/reference/${encodeURIComponent(name)}`
REFERENCE_ITEM: (name) => `${API_BASE}/settings/reference/${encodeURIComponent(name)}`,
REFERENCE_PLOT: (name) => `${API_BASE}/settings/reference/${encodeURIComponent(name)}/plot`
}
};

View File

@ -136,10 +136,9 @@ const ICONS = {
'eye-off': {
viewBox: '0 0 24 24',
elements: [
{ type: 'path', attrs: { d: 'M3 3l18 18' } },
{ type: 'path', attrs: { d: 'M9.5 9.5A4 4 0 0 0 12 16a4 4 0 0 0 3.5-6.5' } },
{ type: 'path', attrs: { d: 'M7.2 7.5C4.8 8.9 3 12 3 12s2.7 5 9 5c1.5 0 2.8-.3 3.9-.8' } },
{ type: 'path', attrs: { d: 'M17.2 10.2C18.7 9.2 21 12 21 12s-2.7 5-9 5' } }
{ type: 'path', attrs: { d: 'M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12z' } },
{ type: 'circle', attrs: { cx: 12, cy: 12, r: 3 } },
{ type: 'line', attrs: { x1: 4, y1: 4, x2: 20, y2: 20 } }
]
},
'alert-circle': {

View File

@ -77,6 +77,7 @@ export class SettingsManager {
setCalibrationBtn: document.getElementById('setCalibrationBtn'),
viewPlotsBtn: document.getElementById('viewPlotsBtn'),
deleteCalibrationBtn: document.getElementById('deleteCalibrationBtn'),
clearCalibrationBtn: document.getElementById('clearCalibrationBtn'),
viewCurrentPlotsBtn: document.getElementById('viewCurrentPlotsBtn'),
// Modal
@ -139,6 +140,10 @@ export class SettingsManager {
this.referenceManager.onReferenceUpdated = (reference) => {
this.updateReferenceSummary(reference);
};
this.referenceManager.onShowPlots = (plotData) => {
this.showPlotsModal(plotData);
};
}
setupEventHandlers() {
@ -163,6 +168,9 @@ export class SettingsManager {
if (this.elements.systemStatus) {
this.elements.systemStatus.textContent = 'Не подключено';
}
if (this.elements.referenceCount) {
this.elements.referenceCount.textContent = '-';
}
this.notify(ERROR, 'Ошибка статуса', 'Не удалось получить текущее состояние системы');
return null;
}
@ -198,8 +206,9 @@ export class SettingsManager {
}
const effectiveStatus = {
...(status ?? { available_presets: 0, available_calibrations: 0 }),
...(status ?? { available_presets: 0, available_calibrations: 0, available_references: 0 }),
current_preset: fallbackPreset,
available_references: this.referenceManager?.availableReferences?.length ?? 0,
};
if (!status) {
@ -210,7 +219,7 @@ export class SettingsManager {
this.elements.calibrationCount.textContent = effectiveStatus.available_calibrations ?? '-';
}
if (this.elements.referenceCount) {
this.elements.referenceCount.textContent = '-';
this.elements.referenceCount.textContent = effectiveStatus.available_references ?? '-';
}
}
@ -234,7 +243,9 @@ export class SettingsManager {
this.elements.calibrationCount.textContent = status?.available_calibrations ?? '-';
}
if (this.elements.referenceCount) {
const count = this.referenceManager?.availableReferences?.length;
const count = typeof status?.available_references === 'number'
? status.available_references
: this.referenceManager?.availableReferences?.length;
this.elements.referenceCount.textContent = typeof count === 'number' ? count : '-';
}
this.updateHeaderSummary(status);
@ -294,14 +305,25 @@ export class SettingsManager {
this.currentPlotsData = plotsData;
if (plotsData.reference_name) {
this.renderReferencePlot(plotsData);
} else {
this.renderCalibrationPlots(plotsData.individual_plots, plotsData.preset);
}
const title = modal.querySelector('.modal__title');
if (title) {
if (plotsData.reference_name) {
title.innerHTML = `
<span data-icon="target"></span>
${plotsData.reference_name} - ${plotsData.preset.mode.toUpperCase()}
`;
} else {
title.innerHTML = `
<span data-icon="bar-chart-3"></span>
${plotsData.calibration_name} - ${plotsData.preset.mode.toUpperCase()} Standards
`;
}
renderIcons(title);
}
@ -311,6 +333,60 @@ export class SettingsManager {
document.body.style.overflow = 'hidden';
}
renderReferencePlot(plotsData) {
const container = this.elements.plotsGrid;
if (!container) return;
container.innerHTML = '';
if (!plotsData.plot || plotsData.plot.error) {
container.innerHTML = '<div class="plot-error">Не удалось загрузить график эталона</div>';
return;
}
const card = document.createElement('div');
card.className = 'chart-card';
card.innerHTML = `
<div class="chart-card__header">
<div class="chart-card__title">
<span data-icon="target" class="chart-card__icon"></span>
${plotsData.reference_name}
</div>
<div class="chart-card__actions">
<button class="chart-card__action" data-action="fullscreen" title="На весь экран">
<span data-icon="expand"></span>
</button>
<button class="chart-card__action" data-action="download" title="Скачать">
<span data-icon="download"></span>
</button>
</div>
</div>
<div class="chart-card__content">
<div class="chart-card__plot" id="reference-plot"></div>
</div>
<div class="chart-card__meta">
<div class="chart-card__timestamp">Дата: ${new Date(plotsData.timestamp).toLocaleString()}</div>
<div class="chart-card__sweep">${plotsData.description || 'Эталон открытого пространства'}</div>
</div>
`;
card.addEventListener('click', (e) => {
const action = e.target.closest?.('[data-action]')?.dataset.action;
if (!action) return;
e.stopPropagation();
const plotEl = card.querySelector('.chart-card__plot');
if (action === 'fullscreen') this.toggleFullscreen(card);
if (action === 'download') this.downloadReferencePlot(plotsData, plotEl);
});
container.appendChild(card);
renderIcons(card);
const plotEl = card.querySelector('.chart-card__plot');
this.renderPlotly(plotEl, plotsData.plot, plotsData.reference_name);
}
renderCalibrationPlots(individualPlots, preset) {
const container = this.elements.plotsGrid;
if (!container) return;
@ -473,6 +549,33 @@ export class SettingsManager {
this.currentPlotsData = null;
}
async downloadReferencePlot(plotsData, plotContainer) {
try {
const ts = new Date().toISOString().replace(/[:.]/g, '-');
const base = `${plotsData.reference_name}_${ts}`;
if (plotContainer) {
await downloadPlotlyImage(plotContainer, `${base}_plot`);
}
const data = {
reference_info: {
name: plotsData.reference_name,
timestamp: plotsData.timestamp,
description: plotsData.description,
preset: plotsData.preset
},
plot_data: plotsData.plot
};
downloadJSON(data, `${base}_data.json`);
this.notify(SUCCESS, 'Скачивание завершено', `Скачаны график и данные эталона ${plotsData.reference_name}`);
} catch (e) {
console.error('Download reference failed:', e);
this.notify(ERROR, 'Ошибка скачивания', 'Не удалось скачать данные эталона');
}
}
async downloadCalibrationStandard(standardName, plotContainer) {
try {
const ts = new Date().toISOString().replace(/[:.]/g, '-');

View File

@ -26,6 +26,7 @@ export class CalibrationManager {
this.handleSetCalibration = this.handleSetCalibration.bind(this);
this.handleCalibrationChange = this.handleCalibrationChange.bind(this);
this.handleDeleteCalibration = this.handleDeleteCalibration.bind(this);
this.handleClearCalibration = this.handleClearCalibration.bind(this);
}
init(elements) {
@ -35,6 +36,7 @@ export class CalibrationManager {
this.elements.calibrationDropdown?.addEventListener('change', this.handleCalibrationChange);
this.elements.setCalibrationBtn?.addEventListener('click', this.handleSetCalibration);
this.elements.deleteCalibrationBtn?.addEventListener('click', this.handleDeleteCalibration);
this.elements.clearCalibrationBtn?.addEventListener('click', this.handleClearCalibration);
this.elements.calibrationNameInput?.addEventListener('input', () => {
const hasName = this.elements.calibrationNameInput.value.trim().length > 0;
@ -49,6 +51,7 @@ export class CalibrationManager {
this.elements.calibrationDropdown?.removeEventListener('change', this.handleCalibrationChange);
this.elements.setCalibrationBtn?.removeEventListener('click', this.handleSetCalibration);
this.elements.deleteCalibrationBtn?.removeEventListener('click', this.handleDeleteCalibration);
this.elements.clearCalibrationBtn?.removeEventListener('click', this.handleClearCalibration);
this.resetCaptureState();
}
@ -97,6 +100,9 @@ export class CalibrationManager {
if (this.elements.deleteCalibrationBtn) {
this.elements.deleteCalibrationBtn.disabled = true;
}
if (this.elements.clearCalibrationBtn) {
this.elements.clearCalibrationBtn.disabled = true;
}
return;
}
@ -114,6 +120,9 @@ export class CalibrationManager {
if (this.elements.deleteCalibrationBtn) {
this.elements.deleteCalibrationBtn.disabled = true;
}
if (this.elements.clearCalibrationBtn) {
this.elements.clearCalibrationBtn.disabled = !this.currentCalibration;
}
}
updateWorking(working) {
@ -385,6 +394,53 @@ export class CalibrationManager {
);
}
async handleClearCalibration() {
if (!this.currentPreset || !this.currentCalibration) {
return;
}
if (!confirm(`Сбросить текущую калибровку «${this.currentCalibration.calibration_name}»? Вы сможете выбрать её снова позже.`)) {
return;
}
this.debouncer.debounce('clear-calibration', () =>
this.reqGuard.runExclusive('clear-calibration', async () => {
try {
if (this.elements.clearCalibrationBtn) {
ButtonState.set(this.elements.clearCalibrationBtn, { state: 'loading', icon: 'loader', text: 'Сброс...' });
}
const result = await apiDelete(API.SETTINGS.CALIBRATION_CLEAR_CURRENT);
this.notify(INFO, 'Калибровка сброшена', result.message);
this.currentCalibration = null;
if (this.elements.currentCalibration) {
this.elements.currentCalibration.textContent = 'Нет';
}
if (this.elements.calibrationDropdown) {
this.elements.calibrationDropdown.value = '';
}
await this.loadCalibrations();
await this.loadWorkingCalibration();
if (this.onCalibrationSet) {
await this.onCalibrationSet();
}
} catch (e) {
console.error('Clear calibration failed:', e);
this.notify(ERROR, 'Ошибка калибровки', 'Не удалось сбросить калибровку');
} finally {
if (this.elements.clearCalibrationBtn) {
ButtonState.set(this.elements.clearCalibrationBtn, { state: 'normal', icon: 'square', text: 'Сбросить' });
}
}
}), TIMING.DEBOUNCE_CALIBRATION
);
}
updateStatus(status) {
if (status.current_calibration) {
this.currentCalibration = status.current_calibration;
@ -393,6 +449,10 @@ export class CalibrationManager {
this.currentCalibration = null;
this.elements.currentCalibration.textContent = 'Нет';
}
if (this.elements.clearCalibrationBtn) {
this.elements.clearCalibrationBtn.disabled = !this.currentCalibration;
}
}
resetCaptureState(standard = null) {
@ -413,6 +473,7 @@ export class CalibrationManager {
}
if (this.elements.saveCalibrationBtn) this.elements.saveCalibrationBtn.disabled = true;
if (this.elements.progressText) this.elements.progressText.textContent = '0/0';
if (this.elements.clearCalibrationBtn) this.elements.clearCalibrationBtn.disabled = true;
}
getWorkingCalibration() {

View File

@ -7,7 +7,7 @@ import { Debouncer, RequestGuard, ButtonState } from '../utils.js';
import { apiGet, apiPost, apiDelete, buildUrl } from '../api-client.js';
import { API, TIMING, NOTIFICATION_TYPES } from '../constants.js';
const { SUCCESS, ERROR, WARNING, INFO } = NOTIFICATION_TYPES;
const { SUCCESS, ERROR, WARNING } = NOTIFICATION_TYPES;
export class ReferenceManager {
constructor(notifications) {
@ -15,6 +15,7 @@ export class ReferenceManager {
this.availableReferences = [];
this.currentReference = null;
this.currentPreset = null;
this.lastPresetFilename = null;
this.elements = {};
this.debouncer = new Debouncer();
this.reqGuard = new RequestGuard();
@ -54,12 +55,23 @@ export class ReferenceManager {
}
async setCurrentPreset(preset) {
const previousPreset = this.currentPreset?.filename ?? null;
const newPreset = preset?.filename ?? null;
this.currentPreset = preset;
if (preset) {
await this.loadReferences();
} else {
this.lastPresetFilename = newPreset;
if (!preset) {
await this.clearReferenceCache();
this.reset();
return;
}
if (previousPreset !== newPreset) {
await this.clearServerCurrentReference();
}
await this.loadReferences();
}
async loadReferences() {
@ -67,6 +79,7 @@ export class ReferenceManager {
if (!this.currentPreset) {
this.renderDropdown([]);
this.updateInfo(null);
this.updateReferenceCount(null);
return;
}
@ -241,34 +254,21 @@ export class ReferenceManager {
this.debouncer.debounce('preview-reference', () =>
this.reqGuard.runExclusive('preview-reference', async () => {
try {
const url = buildUrl(API.SETTINGS.REFERENCE_ITEM(targetName), {
ButtonState.set(this.elements.previewReferenceBtn, { state: 'loading', icon: 'loader', text: 'Загрузка...' });
const url = buildUrl(API.SETTINGS.REFERENCE_PLOT(targetName), {
preset_filename: this.currentPreset?.filename
});
const details = await apiGet(url);
const plotData = await apiGet(url);
const timestamp = details?.timestamp
? new Date(details.timestamp).toLocaleString()
: '—';
const description = details?.description ? details.description : '—';
const lines = [
`Имя: ${details?.name ?? targetName}`,
`Дата: ${timestamp}`,
`Описание: ${description}`
];
if (details?.metadata && Object.keys(details.metadata).length) {
const metaPairs = Object.entries(details.metadata)
.map(([key, value]) => `${key}: ${JSON.stringify(value)}`)
.join('; ');
lines.push(`Метаданные: ${metaPairs}`);
if (this.onShowPlots) {
this.onShowPlots(plotData);
}
const message = lines.join(' • ');
this.notify(INFO, 'Просмотр эталона', message);
} catch (error) {
console.error('Preview reference failed:', error);
this.notify(ERROR, 'Ошибка эталона', 'Не удалось загрузить данные эталона');
} finally {
ButtonState.set(this.elements.previewReferenceBtn, { state: 'normal', icon: 'eye', text: 'Просмотр' });
}
})
);
@ -361,6 +361,23 @@ export class ReferenceManager {
this.updateReferenceCount(0);
}
async clearReferenceCache() {
this.availableReferences = [];
this.updateReferenceCount(0);
this.updateInfo(null);
}
async clearServerCurrentReference() {
try {
await apiDelete(API.SETTINGS.REFERENCE_CURRENT);
} catch (error) {
if (!error?.status || error.status !== 404) {
console.error('Failed to clear current reference', error);
}
}
await this.clearReferenceCache();
}
notify(type, title, message) {
this.notifications?.show?.({ type, title, message });
}

View File

@ -3,10 +3,9 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Панель управления системой ВНА для сбора и обработки данных в реальном времени">
<meta name="keywords" content="ВНА, Vector Network Analyzer, РФ, микроволны, сбор данных, реальное время">
<meta name="description" content="Панель управления системой для сбора и обработки данных в реальном времени">
<meta name="author" content="VNA System">
<title>Система ВНА — Панель управления</title>
<title>Радар</title>
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="/static/assets/favicon.svg">
@ -129,7 +128,7 @@
<span data-icon="activity" class="empty-state__icon"></span>
<h3 class="empty-state__title">Нет данных</h3>
<p class="empty-state__description">
Ожидаем данные свипа от системы ВНА...
Ожидаем данные свипа от системы...
</p>
<div class="loading-spinner">
<div class="spinner"></div>
@ -223,7 +222,7 @@
Графики текущей калибровки
</button>
<input type="text" class="calibration-name-input settings-input" id="calibrationNameInput" placeholder="Введите имя калибровки" disabled>
<button class="btn btn--success" id="saveCalibrationBtn" disabled>
<button class="btn btn--primary" id="saveCalibrationBtn" disabled>
<span data-icon="save"></span>
Сохранить калибровку
</button>
@ -242,6 +241,10 @@
<span data-icon="check"></span>
Сделать активной
</button>
<button class="btn btn--secondary" id="clearCalibrationBtn" disabled>
<span data-icon="square"></span>
Сбросить
</button>
<button class="btn btn--secondary" id="viewPlotsBtn" disabled>
<span data-icon="bar-chart-3"></span>
Показать графики
@ -286,6 +289,10 @@
<span data-icon="check"></span>
Использовать
</button>
<button class="btn btn--secondary" id="clearReferenceBtn" disabled>
<span data-icon="square"></span>
Сбросить
</button>
<button class="btn btn--secondary" id="previewReferenceBtn" disabled>
<span data-icon="bar-chart-3"></span>
Просмотр