some refactoring
This commit is contained in:
BIN
reference_data_acquisition/vna_protocol.bin
Normal file
BIN
reference_data_acquisition/vna_protocol.bin
Normal file
Binary file not shown.
@ -1 +1 @@
|
||||
config_inputs/s11_start100_stop8800_points1000_bw1khz.bin
|
||||
config_inputs/s21_start100_stop8800_points1000_bw1khz.bin
|
||||
1
vna_system/calibration/current_calibration
Symbolic link
1
vna_system/calibration/current_calibration
Symbolic link
@ -0,0 +1 @@
|
||||
s21_start100_stop8800_points1000_bw1khz/bambambum
|
||||
@ -1,10 +1,10 @@
|
||||
{
|
||||
"open_air": true,
|
||||
"axis": "phase",
|
||||
"data_limitation": "ph_only_1",
|
||||
"cut": 0.988,
|
||||
"max": 2.4,
|
||||
"gain": 1.2,
|
||||
"open_air": false,
|
||||
"axis": "abs",
|
||||
"data_limitation": "ph_only_2",
|
||||
"cut": 0.417,
|
||||
"max": 0.6,
|
||||
"gain": 0.0,
|
||||
"start_freq": 100.0,
|
||||
"stop_freq": 8800.0,
|
||||
"clear_history": false,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"y_min": -80,
|
||||
"y_max": 40,
|
||||
"show_phase": true
|
||||
"show_phase": false
|
||||
}
|
||||
@ -166,8 +166,8 @@ class BScanProcessor(BaseProcessor):
|
||||
"""
|
||||
try:
|
||||
# Skip B-scan processing for S21 mode - only works with S11
|
||||
if vna_config and vna_config.get("mode") == "s21":
|
||||
return {"error": "B-scan only available in S11 mode"}
|
||||
# if vna_config and vna_config.get("mode") == "s21":
|
||||
# return {"error": "B-scan only available in S11 mode"}
|
||||
|
||||
# Choose calibrated data when provided
|
||||
data_to_process = calibrated_data or sweep_data
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from dataclasses import asdict
|
||||
from vna_system.core.acquisition.sweep_buffer import SweepData
|
||||
from vna_system.core.logging.logger import get_component_logger
|
||||
from vna_system.core.processors.base_processor import BaseProcessor, UIParameter
|
||||
|
||||
@ -30,7 +31,7 @@ class MagnitudeProcessor(BaseProcessor):
|
||||
# ------------------------------------------------------------------ #
|
||||
# Core processing
|
||||
# ------------------------------------------------------------------ #
|
||||
def process_sweep(self, sweep_data: Any, calibrated_data: Any, vna_config: dict[str, Any]) -> dict[str, Any]:
|
||||
def process_sweep(self, sweep_data: SweepData, calibrated_data: SweepData, vna_config: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
Produce magnitude trace (dB) and ancillary info from a calibrated sweep.
|
||||
|
||||
@ -70,15 +71,21 @@ class MagnitudeProcessor(BaseProcessor):
|
||||
# Magnitude in dB (clamp zero magnitude to -120 dB)
|
||||
mags_db: list[float] = []
|
||||
phases_deg: list[float] = []
|
||||
# real_points: list[str] = []
|
||||
# imag_points: list[str] = []
|
||||
|
||||
for real, imag in points:
|
||||
complex_val = complex(real, imag)
|
||||
# real_points.append(str(real))
|
||||
# imag_points.append(str(imag))
|
||||
mag = abs(complex_val)
|
||||
mags_db.append(20.0 * np.log10(mag) if mag > 0.0 else -120.0)
|
||||
phases_deg.append(np.degrees(np.angle(complex_val)))
|
||||
|
||||
result = {
|
||||
"frequencies": freqs,
|
||||
# "real_points" : real_points,
|
||||
# "imag_points" : imag_points,
|
||||
"magnitudes_db": mags_db,
|
||||
"phases_deg": phases_deg,
|
||||
"y_min": float(self._config.get("y_min", -80)),
|
||||
|
||||
@ -270,9 +270,10 @@ class ProcessorManager:
|
||||
from .implementations.magnitude_processor import MagnitudeProcessor
|
||||
from .implementations.bscan_processor import BScanProcessor
|
||||
|
||||
self.register_processor(MagnitudeProcessor(self.config_dir))
|
||||
|
||||
# self.register_processor(PhaseProcessor(self.config_dir))
|
||||
self.register_processor(BScanProcessor(self.config_dir))
|
||||
self.register_processor(MagnitudeProcessor(self.config_dir))
|
||||
# self.register_processor(SmithChartProcessor(self.config_dir))
|
||||
|
||||
logger.info("Default processors registered", count=len(self._processors))
|
||||
|
||||
@ -1 +1 @@
|
||||
s11_start100_stop8800_points1000_bw1khz/test1
|
||||
s21_start100_stop8800_points1000_bw1khz/testet
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "asd",
|
||||
"timestamp": "2025-09-30T15:20:21.546972",
|
||||
"preset_filename": "s11_start100_stop8800_points1000_bw1khz.bin",
|
||||
"description": "asd",
|
||||
"metadata": {}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -38,6 +38,7 @@ body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.header__icon {
|
||||
@ -52,12 +53,57 @@ body {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.header__subtitle {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--text-tertiary);
|
||||
padding: var(--space-1) var(--space-2);
|
||||
.header__summary {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
padding: var(--space-2) var(--space-4);
|
||||
background-color: var(--bg-tertiary);
|
||||
border-radius: var(--radius-default);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.header-summary__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
.header-summary__label {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--text-tertiary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.header-summary__value {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
|
||||
.header-summary__divider {
|
||||
width: 1px;
|
||||
height: 32px;
|
||||
background-color: var(--border-primary);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.header__summary {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
row-gap: var(--space-2);
|
||||
}
|
||||
|
||||
.header-summary__divider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.header-summary__item {
|
||||
flex: 1 1 45%;
|
||||
}
|
||||
}
|
||||
|
||||
.header__status {
|
||||
|
||||
@ -131,40 +131,43 @@
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.preset-dropdown,
|
||||
.calibration-dropdown {
|
||||
:is(.settings-select, .preset-dropdown, .calibration-dropdown, .reference-dropdown) {
|
||||
flex: 1;
|
||||
min-width: 220px;
|
||||
padding: var(--space-3) var(--space-4);
|
||||
padding-right: var(--space-10);
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: var(--radius-lg);
|
||||
color: var(--text-primary);
|
||||
font-size: var(--font-size-sm);
|
||||
min-height: 3rem;
|
||||
font-weight: var(--font-weight-medium);
|
||||
transition: all var(--transition-fast);
|
||||
box-shadow: var(--shadow-sm);
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' stroke-width='1.5' stroke='%2394a3b8' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right var(--space-4) center;
|
||||
background-size: 14px 14px;
|
||||
}
|
||||
|
||||
.preset-dropdown:focus,
|
||||
.calibration-dropdown:focus {
|
||||
:is(.settings-select, .preset-dropdown, .calibration-dropdown, .reference-dropdown):focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary-500);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
background: var(--bg-surface);
|
||||
}
|
||||
|
||||
.preset-dropdown:hover,
|
||||
.calibration-dropdown:hover {
|
||||
:is(.settings-select, .preset-dropdown, .calibration-dropdown, .reference-dropdown):hover:not(:disabled) {
|
||||
border-color: var(--color-primary-500);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.preset-dropdown:disabled,
|
||||
.calibration-dropdown:disabled {
|
||||
:is(.settings-select, .preset-dropdown, .calibration-dropdown, .reference-dropdown):disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.preset-info {
|
||||
@ -301,14 +304,18 @@
|
||||
}
|
||||
|
||||
/* Calibration Actions */
|
||||
.calibration-actions {
|
||||
:is(.settings-action-group, .calibration-actions, .reference-actions) {
|
||||
display: flex;
|
||||
gap: var(--space-3);
|
||||
align-items: stretch;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.calibration-actions {
|
||||
margin-top: var(--space-4);
|
||||
}
|
||||
|
||||
.calibration-name-input {
|
||||
:is(.settings-input, .calibration-name-input, .reference-name-input, .reference-description-input) {
|
||||
flex: 1;
|
||||
padding: var(--space-3) var(--space-4);
|
||||
background: var(--bg-primary);
|
||||
@ -316,30 +323,33 @@
|
||||
border-radius: var(--radius-lg);
|
||||
color: var(--text-primary);
|
||||
font-size: var(--font-size-sm);
|
||||
min-height: 2.5rem;
|
||||
min-height: 2.75rem;
|
||||
transition: all var(--transition-fast);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
.calibration-name-input:focus {
|
||||
:is(.settings-input, .calibration-name-input, .reference-name-input, .reference-description-input):focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary-500);
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.calibration-name-input:disabled {
|
||||
:is(.settings-input, .calibration-name-input, .reference-name-input, .reference-description-input):disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.calibration-name-input::placeholder {
|
||||
:is(.settings-input, .calibration-name-input, .reference-name-input, .reference-description-input)::placeholder {
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
/* Existing Calibrations */
|
||||
.existing-calibrations {
|
||||
.existing-calibrations,
|
||||
.existing-references {
|
||||
display: flex;
|
||||
gap: var(--space-3);
|
||||
align-items: stretch;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
/* Status Grid */
|
||||
@ -390,7 +400,8 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.existing-calibrations {
|
||||
.existing-calibrations,
|
||||
.existing-references {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@ -503,53 +514,8 @@
|
||||
margin-bottom: var(--space-4);
|
||||
}
|
||||
|
||||
.reference-name-input,
|
||||
.reference-description-input {
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--text-sm);
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.reference-name-input:focus,
|
||||
.reference-description-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary-500);
|
||||
box-shadow: 0 0 0 3px var(--color-primary-100);
|
||||
}
|
||||
|
||||
.reference-name-input::placeholder,
|
||||
.reference-description-input::placeholder {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
/* Reference Actions */
|
||||
.reference-actions {
|
||||
display: flex;
|
||||
gap: var(--space-3);
|
||||
align-items: stretch;
|
||||
margin-top: var(--space-3);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.reference-dropdown {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--text-sm);
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.reference-dropdown:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
background: var(--color-gray-50);
|
||||
}
|
||||
|
||||
/* Current Reference Info */
|
||||
|
||||
@ -26,6 +26,7 @@ export class SettingsManager {
|
||||
|
||||
this.isInitialized = false;
|
||||
this.elements = {};
|
||||
this.headerElements = {};
|
||||
this.debouncer = new Debouncer();
|
||||
|
||||
// Sub-managers
|
||||
@ -99,6 +100,12 @@ export class SettingsManager {
|
||||
calibrationCount: document.getElementById('calibrationCount'),
|
||||
systemStatus: document.getElementById('systemStatus')
|
||||
};
|
||||
|
||||
this.headerElements = {
|
||||
preset: document.getElementById('headerPresetSummary'),
|
||||
calibration: document.getElementById('headerCalibrationSummary'),
|
||||
reference: document.getElementById('headerReferenceSummary')
|
||||
};
|
||||
}
|
||||
|
||||
initSubManagers() {
|
||||
@ -109,10 +116,9 @@ export class SettingsManager {
|
||||
// Setup callbacks
|
||||
this.presetManager.onPresetChanged = async () => {
|
||||
await this.loadStatus();
|
||||
const preset = this.presetManager.getCurrentPreset();
|
||||
this.calibrationManager.setCurrentPreset(preset);
|
||||
this.calibrationManager.reset();
|
||||
this.referenceManager.setCurrentPreset(preset);
|
||||
await this.calibrationManager.loadWorkingCalibration();
|
||||
this.updateReferenceSummary(this.referenceManager.getCurrentReference());
|
||||
};
|
||||
|
||||
this.calibrationManager.onCalibrationSaved = async () => {
|
||||
@ -122,6 +128,10 @@ export class SettingsManager {
|
||||
this.calibrationManager.onCalibrationSet = async () => {
|
||||
await this.loadStatus();
|
||||
};
|
||||
|
||||
this.referenceManager.onReferenceUpdated = (reference) => {
|
||||
this.updateReferenceSummary(reference);
|
||||
};
|
||||
}
|
||||
|
||||
setupEventHandlers() {
|
||||
@ -130,12 +140,9 @@ export class SettingsManager {
|
||||
}
|
||||
|
||||
async loadInitialData() {
|
||||
await Promise.all([
|
||||
this.presetManager.loadPresets(),
|
||||
this.loadStatus(),
|
||||
this.calibrationManager.loadWorkingCalibration(),
|
||||
this.referenceManager.loadReferences()
|
||||
]);
|
||||
await this.presetManager.loadPresets();
|
||||
await this.loadStatus();
|
||||
await this.calibrationManager.loadWorkingCalibration();
|
||||
}
|
||||
|
||||
async loadStatus() {
|
||||
@ -158,6 +165,9 @@ export class SettingsManager {
|
||||
this.elements.presetCount.textContent = status.available_presets || 0;
|
||||
this.elements.calibrationCount.textContent = status.available_calibrations || 0;
|
||||
this.elements.systemStatus.textContent = 'Ready';
|
||||
|
||||
this.updateHeaderSummary(status);
|
||||
this.updateReferenceSummary(this.referenceManager.getCurrentReference());
|
||||
}
|
||||
|
||||
async handleViewPlots() {
|
||||
@ -475,6 +485,108 @@ export class SettingsManager {
|
||||
console.log('Settings Manager destroyed');
|
||||
}
|
||||
|
||||
formatFrequency(value) {
|
||||
if (value === null || value === undefined) return null;
|
||||
const numeric = Number(value);
|
||||
if (!Number.isFinite(numeric)) return null;
|
||||
const abs = Math.abs(numeric);
|
||||
const units = [
|
||||
{ divider: 1e9, suffix: 'GHz' },
|
||||
{ divider: 1e6, suffix: 'MHz' },
|
||||
{ divider: 1e3, suffix: 'kHz' }
|
||||
];
|
||||
|
||||
for (const unit of units) {
|
||||
if (abs >= unit.divider) {
|
||||
const scaled = numeric / unit.divider;
|
||||
const formatted = scaled >= 10 ? scaled.toFixed(0) : scaled.toFixed(2);
|
||||
return `${formatted} ${unit.suffix}`;
|
||||
}
|
||||
}
|
||||
|
||||
return `${numeric.toFixed(0)} Hz`;
|
||||
}
|
||||
|
||||
formatPresetSummary(preset) {
|
||||
if (!preset) return 'Not selected';
|
||||
|
||||
const parts = [];
|
||||
if (preset.mode) {
|
||||
parts.push(preset.mode.toUpperCase());
|
||||
}
|
||||
|
||||
const start = this.formatFrequency(preset.start_freq);
|
||||
const stop = this.formatFrequency(preset.stop_freq);
|
||||
if (start && stop) {
|
||||
parts.push(`${start} – ${stop}`);
|
||||
} else if (start || stop) {
|
||||
parts.push(start || stop);
|
||||
}
|
||||
|
||||
if (preset.points) {
|
||||
parts.push(`${preset.points} pts`);
|
||||
}
|
||||
|
||||
if (preset.bandwidth) {
|
||||
const bw = this.formatFrequency(preset.bandwidth);
|
||||
if (bw) parts.push(`BW ${bw}`);
|
||||
}
|
||||
|
||||
return parts.join(' • ') || 'Preset configured';
|
||||
}
|
||||
|
||||
formatCalibrationSummary(status) {
|
||||
const active = status?.current_calibration;
|
||||
if (active?.calibration_name) {
|
||||
return `Active • ${active.calibration_name}`;
|
||||
}
|
||||
|
||||
const working = status?.working_calibration;
|
||||
if (working?.progress) {
|
||||
const missingCount = working.missing_standards?.length || 0;
|
||||
const missingText = missingCount ? ` • ${missingCount} missing` : '';
|
||||
return `In progress • ${working.progress}${missingText}`;
|
||||
}
|
||||
|
||||
return 'Not set';
|
||||
}
|
||||
|
||||
updateHeaderSummary(status) {
|
||||
if (!this.headerElements) return;
|
||||
const { preset, calibration } = this.headerElements;
|
||||
if (preset) {
|
||||
preset.textContent = this.formatPresetSummary(status?.current_preset);
|
||||
}
|
||||
if (calibration) {
|
||||
calibration.textContent = this.formatCalibrationSummary(status);
|
||||
}
|
||||
}
|
||||
|
||||
updateReferenceSummary(reference) {
|
||||
const target = this.headerElements?.reference;
|
||||
if (!target) return;
|
||||
|
||||
if (!reference) {
|
||||
target.textContent = 'Not captured';
|
||||
return;
|
||||
}
|
||||
|
||||
const name = reference.name || 'Reference';
|
||||
let timestampText = '';
|
||||
if (reference.timestamp) {
|
||||
const timestamp = new Date(reference.timestamp);
|
||||
if (!Number.isNaN(timestamp.getTime())) {
|
||||
timestampText = timestamp.toLocaleString(undefined, { dateStyle: 'short', timeStyle: 'short' });
|
||||
}
|
||||
}
|
||||
|
||||
const summaryParts = ['Active', name];
|
||||
if (timestampText) {
|
||||
summaryParts.push(timestampText);
|
||||
}
|
||||
target.textContent = summaryParts.join(' • ');
|
||||
}
|
||||
|
||||
notify(type, title, message) {
|
||||
this.notifications?.show?.({ type, title, message });
|
||||
}
|
||||
|
||||
@ -5,7 +5,9 @@
|
||||
|
||||
import { Debouncer, RequestGuard, ButtonState } from '../utils.js';
|
||||
import { apiGet, apiPost } from '../api-client.js';
|
||||
import { API, TIMING } from '../constants.js';
|
||||
import { API, TIMING, NOTIFICATION_TYPES } from '../constants.js';
|
||||
|
||||
const { SUCCESS, ERROR } = NOTIFICATION_TYPES;
|
||||
|
||||
export class PresetManager {
|
||||
constructor(notifications) {
|
||||
@ -36,7 +38,7 @@ export class PresetManager {
|
||||
this.populateDropdown(presets);
|
||||
} catch (e) {
|
||||
console.error('Presets load failed:', e);
|
||||
this.notify('error', 'Load Error', 'Failed to load configuration presets');
|
||||
this.notify(ERROR, 'Load Error', 'Failed to load configuration presets');
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,7 +49,9 @@ export class PresetManager {
|
||||
if (!presets.length) {
|
||||
dd.innerHTML = '<option value="">No presets available</option>';
|
||||
dd.disabled = true;
|
||||
if (this.elements.setPresetBtn) {
|
||||
this.elements.setPresetBtn.disabled = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -60,7 +64,7 @@ export class PresetManager {
|
||||
});
|
||||
|
||||
dd.disabled = false;
|
||||
this.elements.setPresetBtn.disabled = true;
|
||||
this.syncSelectedPreset();
|
||||
}
|
||||
|
||||
formatDisplay(p) {
|
||||
@ -75,8 +79,7 @@ export class PresetManager {
|
||||
}
|
||||
|
||||
handlePresetChange() {
|
||||
const v = this.elements.presetDropdown.value;
|
||||
this.elements.setPresetBtn.disabled = !v;
|
||||
this.updateSetButtonState();
|
||||
}
|
||||
|
||||
async handleSetPreset() {
|
||||
@ -90,17 +93,19 @@ export class PresetManager {
|
||||
|
||||
const result = await apiPost(API.SETTINGS.PRESET_SET, { filename });
|
||||
|
||||
this.notify('success', 'Preset Set', result.message);
|
||||
this.notify(SUCCESS, 'Preset Set', result.message);
|
||||
this.currentPreset = { filename };
|
||||
|
||||
if (this.onPresetChanged) {
|
||||
await this.onPresetChanged();
|
||||
}
|
||||
this.syncSelectedPreset();
|
||||
} catch (e) {
|
||||
console.error('Set preset failed:', e);
|
||||
this.notify('error', 'Preset Error', 'Failed to set configuration preset');
|
||||
this.notify(ERROR, 'Preset Error', 'Failed to set configuration preset');
|
||||
} finally {
|
||||
ButtonState.set(this.elements.setPresetBtn, { state: 'normal', icon: 'check', text: 'Set Active' });
|
||||
this.updateSetButtonState();
|
||||
}
|
||||
}), TIMING.DEBOUNCE_PRESET
|
||||
);
|
||||
@ -114,12 +119,39 @@ export class PresetManager {
|
||||
this.currentPreset = null;
|
||||
this.elements.currentPreset.textContent = 'None';
|
||||
}
|
||||
this.syncSelectedPreset();
|
||||
}
|
||||
|
||||
getCurrentPreset() {
|
||||
return this.currentPreset;
|
||||
}
|
||||
|
||||
syncSelectedPreset() {
|
||||
const dd = this.elements.presetDropdown;
|
||||
if (!dd) return;
|
||||
|
||||
const active = this.currentPreset?.filename || '';
|
||||
if (active) {
|
||||
const hasOption = Array.from(dd.options).some(opt => opt.value === active);
|
||||
dd.value = hasOption ? active : '';
|
||||
} else {
|
||||
dd.value = '';
|
||||
}
|
||||
|
||||
this.updateSetButtonState();
|
||||
}
|
||||
|
||||
updateSetButtonState() {
|
||||
const dd = this.elements.presetDropdown;
|
||||
const btn = this.elements.setPresetBtn;
|
||||
if (!dd || !btn) return;
|
||||
|
||||
const selected = dd.value;
|
||||
const hasSelection = selected !== '';
|
||||
const isActive = this.currentPreset?.filename === selected;
|
||||
btn.disabled = !hasSelection || isActive;
|
||||
}
|
||||
|
||||
notify(type, title, message) {
|
||||
this.notifications?.show?.({ type, title, message });
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ export class ReferenceManager {
|
||||
this.elements = {};
|
||||
this.debouncer = new Debouncer();
|
||||
this.reqGuard = new RequestGuard();
|
||||
this.onReferenceUpdated = null;
|
||||
|
||||
this.handleCreateReference = this.handleCreateReference.bind(this);
|
||||
this.handleReferenceChange = this.handleReferenceChange.bind(this);
|
||||
@ -79,6 +80,10 @@ export class ReferenceManager {
|
||||
}
|
||||
}
|
||||
|
||||
getCurrentReference() {
|
||||
return this.currentReference;
|
||||
}
|
||||
|
||||
renderDropdown(references) {
|
||||
if (!this.elements.referenceDropdown) return;
|
||||
|
||||
@ -120,10 +125,16 @@ export class ReferenceManager {
|
||||
}
|
||||
|
||||
updateInfo(reference) {
|
||||
if (!this.elements.currentReferenceInfo) return;
|
||||
this.currentReference = reference;
|
||||
|
||||
if (!this.elements.currentReferenceInfo) {
|
||||
this.onReferenceUpdated?.(reference || null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reference) {
|
||||
this.elements.currentReferenceInfo.style.display = 'none';
|
||||
this.onReferenceUpdated?.(null);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -134,12 +145,17 @@ export class ReferenceManager {
|
||||
}
|
||||
|
||||
if (this.elements.currentReferenceTimestamp) {
|
||||
this.elements.currentReferenceTimestamp.textContent = new Date(reference.timestamp).toLocaleString();
|
||||
const timestamp = new Date(reference.timestamp);
|
||||
this.elements.currentReferenceTimestamp.textContent = Number.isNaN(timestamp.getTime())
|
||||
? '-'
|
||||
: timestamp.toLocaleString();
|
||||
}
|
||||
|
||||
if (this.elements.currentReferenceDescription) {
|
||||
this.elements.currentReferenceDescription.textContent = reference.description || '';
|
||||
}
|
||||
|
||||
this.onReferenceUpdated?.(reference);
|
||||
}
|
||||
|
||||
async handleCreateReference() {
|
||||
|
||||
@ -41,7 +41,22 @@
|
||||
<div class="header__brand">
|
||||
<i data-lucide="activity" class="header__icon"></i>
|
||||
<h1 class="header__title">VNA System</h1>
|
||||
<span class="header__subtitle">Real-time Analysis</span>
|
||||
<div class="header__summary" id="headerSummary">
|
||||
<div class="header-summary__item">
|
||||
<span class="header-summary__label">Preset</span>
|
||||
<span class="header-summary__value" id="headerPresetSummary">Not selected</span>
|
||||
</div>
|
||||
<span class="header-summary__divider" aria-hidden="true"></span>
|
||||
<div class="header-summary__item">
|
||||
<span class="header-summary__label">Calibration</span>
|
||||
<span class="header-summary__value" id="headerCalibrationSummary">Not set</span>
|
||||
</div>
|
||||
<span class="header-summary__divider" aria-hidden="true"></span>
|
||||
<div class="header-summary__item">
|
||||
<span class="header-summary__label">Reference</span>
|
||||
<span class="header-summary__value" id="headerReferenceSummary">Not captured</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header__status">
|
||||
@ -149,7 +164,7 @@
|
||||
<div class="control-group">
|
||||
<label class="control-label">Available Presets:</label>
|
||||
<div class="preset-selector" id="presetSelector">
|
||||
<select class="preset-dropdown" id="presetDropdown" disabled>
|
||||
<select class="preset-dropdown settings-select" id="presetDropdown" disabled>
|
||||
<option value="">Loading presets...</option>
|
||||
</select>
|
||||
<button class="btn btn--primary" id="setPresetBtn" disabled>
|
||||
@ -209,12 +224,12 @@
|
||||
<!-- Standards buttons will be dynamically generated -->
|
||||
</div>
|
||||
|
||||
<div class="calibration-actions">
|
||||
<div class="calibration-actions settings-action-group">
|
||||
<button class="btn btn--secondary" id="viewCurrentPlotsBtn" disabled>
|
||||
<i data-lucide="bar-chart-3"></i>
|
||||
View Current Plots
|
||||
</button>
|
||||
<input type="text" class="calibration-name-input" id="calibrationNameInput" placeholder="Enter calibration name" disabled>
|
||||
<input type="text" class="calibration-name-input settings-input" id="calibrationNameInput" placeholder="Enter calibration name" disabled>
|
||||
<button class="btn btn--success" id="saveCalibrationBtn" disabled>
|
||||
<i data-lucide="save"></i>
|
||||
Save Calibration
|
||||
@ -226,10 +241,10 @@
|
||||
<div class="workflow-section">
|
||||
<h4 class="workflow-title">Existing Calibrations</h4>
|
||||
<div class="existing-calibrations" id="existingCalibrations">
|
||||
<select class="calibration-dropdown" id="calibrationDropdown" disabled>
|
||||
<select class="calibration-dropdown settings-select" id="calibrationDropdown" disabled>
|
||||
<option value="">No calibrations available</option>
|
||||
</select>
|
||||
<div class="calibration-actions">
|
||||
<div class="calibration-actions settings-action-group">
|
||||
<button class="btn btn--primary" id="setCalibrationBtn" disabled>
|
||||
<i data-lucide="check"></i>
|
||||
Set Active
|
||||
@ -253,8 +268,8 @@
|
||||
<div class="workflow-section">
|
||||
<h4 class="workflow-title">Create Reference</h4>
|
||||
<div class="reference-creation">
|
||||
<input type="text" class="reference-name-input" id="referenceNameInput" placeholder="Enter reference name">
|
||||
<input type="text" class="reference-description-input" id="referenceDescriptionInput" placeholder="Description (optional)">
|
||||
<input type="text" class="reference-name-input settings-input" id="referenceNameInput" placeholder="Enter reference name">
|
||||
<input type="text" class="reference-description-input settings-input" id="referenceDescriptionInput" placeholder="Description (optional)">
|
||||
<button class="btn btn--primary" id="createReferenceBtn">
|
||||
<i data-lucide="target"></i>
|
||||
Capture Reference
|
||||
@ -266,10 +281,10 @@
|
||||
<div class="workflow-section">
|
||||
<h4 class="workflow-title">Existing References</h4>
|
||||
<div class="existing-references" id="existingReferences">
|
||||
<select class="reference-dropdown" id="referenceDropdown" disabled>
|
||||
<select class="reference-dropdown settings-select" id="referenceDropdown" disabled>
|
||||
<option value="">No references available</option>
|
||||
</select>
|
||||
<div class="reference-actions">
|
||||
<div class="reference-actions settings-action-group">
|
||||
<button class="btn btn--primary" id="setReferenceBtn" disabled>
|
||||
<i data-lucide="check"></i>
|
||||
Set Active
|
||||
|
||||
Reference in New Issue
Block a user