some russian translations and S button functios as single sweep now
This commit is contained in:
@ -26,7 +26,7 @@ async def get_acquisition_status() -> dict[str, Any]:
|
||||
acquisition = singletons.vna_data_acquisition_instance
|
||||
if acquisition is None:
|
||||
logger.error("Acquisition singleton is not initialized")
|
||||
raise HTTPException(status_code=500, detail="Acquisition not initialized")
|
||||
raise HTTPException(status_code=500, detail="Сбор данных не инициализирован")
|
||||
|
||||
return {
|
||||
"running": acquisition.is_running,
|
||||
@ -45,14 +45,14 @@ async def start_acquisition() -> dict[str, Any]:
|
||||
acquisition = singletons.vna_data_acquisition_instance
|
||||
if acquisition is None:
|
||||
logger.error("Acquisition singleton is not initialized")
|
||||
raise HTTPException(status_code=500, detail="Acquisition not initialized")
|
||||
raise HTTPException(status_code=500, detail="Сбор данных не инициализирован")
|
||||
|
||||
if not acquisition.is_running:
|
||||
acquisition.start()
|
||||
logger.info("Acquisition thread started via API")
|
||||
|
||||
acquisition.set_continuous_mode(True)
|
||||
return {"success": True, "message": "Acquisition started"}
|
||||
return {"success": True, "message": "Сбор данных запущен"}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as exc: # noqa: BLE001
|
||||
@ -69,14 +69,14 @@ async def stop_acquisition() -> dict[str, Any]:
|
||||
acquisition = singletons.vna_data_acquisition_instance
|
||||
if acquisition is None:
|
||||
logger.error("Acquisition singleton is not initialized")
|
||||
raise HTTPException(status_code=500, detail="Acquisition not initialized")
|
||||
raise HTTPException(status_code=500, detail="Сбор данных не инициализирован")
|
||||
|
||||
if not acquisition.is_running:
|
||||
return {"success": True, "message": "Acquisition already stopped"}
|
||||
return {"success": True, "message": "Сбор данных уже остановлен"}
|
||||
|
||||
acquisition.pause()
|
||||
logger.info("Acquisition paused via API")
|
||||
return {"success": True, "message": "Acquisition paused"}
|
||||
return {"success": True, "message": "Сбор данных приостановлен"}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as exc: # noqa: BLE001
|
||||
@ -94,14 +94,14 @@ async def trigger_single_sweep() -> dict[str, Any]:
|
||||
acquisition = singletons.vna_data_acquisition_instance
|
||||
if acquisition is None:
|
||||
logger.error("Acquisition singleton is not initialized")
|
||||
raise HTTPException(status_code=500, detail="Acquisition not initialized")
|
||||
raise HTTPException(status_code=500, detail="Сбор данных не инициализирован")
|
||||
|
||||
if not acquisition.is_running:
|
||||
acquisition.start()
|
||||
logger.info("Acquisition thread started (single-sweep request)")
|
||||
|
||||
acquisition.trigger_single_sweep()
|
||||
return {"success": True, "message": "Single sweep triggered"}
|
||||
return {"success": True, "message": "Одиночный свип запущен"}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as exc: # noqa: BLE001
|
||||
@ -111,7 +111,7 @@ async def trigger_single_sweep() -> dict[str, Any]:
|
||||
|
||||
@router.get("/acquisition/latest-sweep")
|
||||
async def get_latest_sweep(
|
||||
limit: int = Query(10, ge=1, le=1000, description="Max number of points to include in response"),
|
||||
limit: int = Query(10, ge=1, le=1000, description="Максимальное число точек в ответе"),
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Return the latest sweep metadata and a limited subset of points.
|
||||
@ -125,11 +125,11 @@ async def get_latest_sweep(
|
||||
acquisition = singletons.vna_data_acquisition_instance
|
||||
if acquisition is None:
|
||||
logger.error("Acquisition singleton is not initialized")
|
||||
raise HTTPException(status_code=500, detail="Acquisition not initialized")
|
||||
raise HTTPException(status_code=500, detail="Сбор данных не инициализирован")
|
||||
|
||||
latest_sweep = acquisition.sweep_buffer.get_latest_sweep()
|
||||
if not latest_sweep:
|
||||
return {"sweep": None, "message": "No sweep data available"}
|
||||
return {"sweep": None, "message": "Данные свипа недоступны"}
|
||||
|
||||
points = latest_sweep.points[:limit]
|
||||
return {
|
||||
@ -139,7 +139,7 @@ async def get_latest_sweep(
|
||||
"total_points": latest_sweep.total_points,
|
||||
"points": points,
|
||||
},
|
||||
"message": f"Latest sweep #{latest_sweep.sweep_number} with {latest_sweep.total_points} points",
|
||||
"message": f"Последний свип №{latest_sweep.sweep_number}, точек: {latest_sweep.total_points}",
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
|
||||
@ -14,4 +14,4 @@ router = APIRouter(prefix="/api/v1", tags=["health"])
|
||||
@router.get("/")
|
||||
async def root():
|
||||
"""Root endpoint."""
|
||||
return {"message": "VNA System API", "version": "1.0.0"}
|
||||
return {"message": "API системы VNA", "version": "1.0.0"}
|
||||
|
||||
@ -67,14 +67,14 @@ async def set_preset(request: SetPresetRequest) -> dict[str, Any]:
|
||||
presets = singletons.settings_manager.get_available_presets()
|
||||
preset = next((p for p in presets if p.filename == request.filename), None)
|
||||
if preset is None:
|
||||
raise HTTPException(status_code=404, detail=f"Preset not found: {request.filename}")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет не найден: {request.filename}")
|
||||
|
||||
# Changing preset invalidates active calibration selection.
|
||||
singletons.settings_manager.calibration_manager.clear_current_calibration()
|
||||
singletons.settings_manager.set_current_preset(preset)
|
||||
|
||||
logger.info("Preset selected via API", filename=preset.filename, mode=preset.mode.value)
|
||||
return {"success": True, "message": f"Preset set to {request.filename}"}
|
||||
return {"success": True, "message": f"Пресет установлен: {request.filename}"}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as exc: # noqa: BLE001
|
||||
@ -112,7 +112,7 @@ async def get_calibrations(preset_filename: str | None = None) -> list[Calibrati
|
||||
presets = singletons.settings_manager.get_available_presets()
|
||||
preset = next((p for p in presets if p.filename == preset_filename), None)
|
||||
if preset is None:
|
||||
raise HTTPException(status_code=404, detail=f"Preset not found: {preset_filename}")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет не найден: {preset_filename}")
|
||||
|
||||
calibrations = singletons.settings_manager.get_available_calibrations(preset)
|
||||
details: list[CalibrationModel] = []
|
||||
@ -153,14 +153,14 @@ async def start_calibration(request: StartCalibrationRequest) -> dict[str, Any]:
|
||||
presets = singletons.settings_manager.get_available_presets()
|
||||
preset = next((p for p in presets if p.filename == request.preset_filename), None)
|
||||
if preset is None:
|
||||
raise HTTPException(status_code=404, detail=f"Preset not found: {request.preset_filename}")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет не найден: {request.preset_filename}")
|
||||
|
||||
calib = singletons.settings_manager.start_new_calibration(preset)
|
||||
required = singletons.settings_manager.get_required_standards(calib.preset.mode)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Calibration started",
|
||||
"message": "Калибровка запущена",
|
||||
"preset": calib.preset.filename,
|
||||
"required_standards": [s.value for s in required],
|
||||
}
|
||||
@ -178,7 +178,7 @@ async def add_calibration_standard(request: CalibrateStandardRequest) -> dict[st
|
||||
try:
|
||||
standard = CalibrationStandard(request.standard)
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=400, detail=f"Invalid calibration standard: {request.standard}")
|
||||
raise HTTPException(status_code=400, detail=f"Недопустимый стандарт калибровки: {request.standard}")
|
||||
|
||||
sweep_no = singletons.settings_manager.capture_calibration_standard_from_acquisition(
|
||||
standard, singletons.vna_data_acquisition_instance
|
||||
@ -189,7 +189,7 @@ async def add_calibration_standard(request: CalibrateStandardRequest) -> dict[st
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Added {standard.value} standard from sweep {sweep_no}",
|
||||
"message": f"Добавлен стандарт {standard.value} из свипа {sweep_no}",
|
||||
"sweep_number": sweep_no,
|
||||
"progress": f"{progress[0]}/{progress[1]}",
|
||||
"is_complete": working.is_complete() if working else False,
|
||||
@ -208,7 +208,7 @@ async def save_calibration(request: SaveCalibrationRequest) -> dict[str, Any]:
|
||||
saved = singletons.settings_manager.save_calibration_set(request.name)
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Calibration '{request.name}' saved successfully",
|
||||
"message": f"Калибровка \"{request.name}\" успешно сохранена",
|
||||
"preset": saved.preset.filename,
|
||||
"standards": [s.value for s in saved.standards.keys()],
|
||||
}
|
||||
@ -226,10 +226,10 @@ async def set_calibration(request: SetCalibrationRequest) -> dict[str, Any]:
|
||||
presets = singletons.settings_manager.get_available_presets()
|
||||
preset = next((p for p in presets if p.filename == request.preset_filename), None)
|
||||
if preset is None:
|
||||
raise HTTPException(status_code=404, detail=f"Preset not found: {request.preset_filename}")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет не найден: {request.preset_filename}")
|
||||
|
||||
singletons.settings_manager.set_current_calibration(request.name, preset)
|
||||
return {"success": True, "message": f"Calibration set to '{request.name}'"}
|
||||
return {"success": True, "message": f"Калибровка установлена: \"{request.name}\""}
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as exc: # noqa: BLE001
|
||||
@ -266,7 +266,7 @@ async def remove_calibration_standard(request: RemoveStandardRequest) -> dict[st
|
||||
try:
|
||||
standard = CalibrationStandard(request.standard)
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=400, detail=f"Invalid calibration standard: {request.standard}")
|
||||
raise HTTPException(status_code=400, detail=f"Недопустимый стандарт калибровки: {request.standard}")
|
||||
|
||||
singletons.settings_manager.remove_calibration_standard(standard)
|
||||
|
||||
@ -275,7 +275,7 @@ async def remove_calibration_standard(request: RemoveStandardRequest) -> dict[st
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Removed {standard.value} standard",
|
||||
"message": f"Стандарт {standard.value} удалён",
|
||||
"progress": f"{progress[0]}/{progress[1]}",
|
||||
"is_complete": working.is_complete() if working else False,
|
||||
}
|
||||
@ -319,18 +319,18 @@ async def get_calibration_standards_plots(
|
||||
presets = singletons.settings_manager.get_available_presets()
|
||||
preset = next((p for p in presets if p.filename == preset_filename), None)
|
||||
if preset is None:
|
||||
raise HTTPException(status_code=404, detail=f"Preset not found: {preset_filename}")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет не найден: {preset_filename}")
|
||||
else:
|
||||
preset = singletons.settings_manager.get_current_preset()
|
||||
if preset is None:
|
||||
raise HTTPException(status_code=400, detail="No current preset selected")
|
||||
raise HTTPException(status_code=400, detail="Текущий пресет не выбран")
|
||||
|
||||
# Resolve calibration directory (uses manager's internal layout)
|
||||
calibration_manager = singletons.settings_manager.calibration_manager
|
||||
calibration_dir = calibration_manager._get_preset_calibration_dir(preset) / calibration_name # noqa: SLF001
|
||||
|
||||
if not calibration_dir.exists():
|
||||
raise HTTPException(status_code=404, detail=f"Calibration not found: {calibration_name}")
|
||||
raise HTTPException(status_code=404, detail=f"Калибровка не найдена: {calibration_name}")
|
||||
|
||||
individual_plots = generate_standards_magnitude_plots(calibration_dir, preset)
|
||||
|
||||
@ -352,9 +352,9 @@ async def get_working_calibration_standards_plots() -> dict[str, Any]:
|
||||
try:
|
||||
working = singletons.settings_manager.get_current_working_calibration()
|
||||
if not working:
|
||||
raise HTTPException(status_code=404, detail="No working calibration active")
|
||||
raise HTTPException(status_code=404, detail="Рабочая калибровка не активна")
|
||||
if not working.standards:
|
||||
raise HTTPException(status_code=404, detail="No standards captured in working calibration")
|
||||
raise HTTPException(status_code=404, detail="В рабочей калибровке нет сохранённых стандартов")
|
||||
|
||||
from vna_system.core.visualization.magnitude_chart import generate_magnitude_plot_from_sweep_data
|
||||
|
||||
@ -371,8 +371,8 @@ async def get_working_calibration_standards_plots() -> dict[str, Any]:
|
||||
fig = generate_magnitude_plot_from_sweep_data(sweep, working.preset)
|
||||
if "error" not in fig and fig.get("data"):
|
||||
fig["data"][0]["line"]["color"] = standard_colors.get(standard.value, "#1f77b4")
|
||||
fig["data"][0]["name"] = f"{standard.value.upper()} Standard"
|
||||
fig["layout"]["title"] = f"{standard.value.upper()} Standard Magnitude (Working)"
|
||||
fig["data"][0]["name"] = f"Стандарт {standard.value.upper()}"
|
||||
fig["layout"]["title"] = f"Амплитуда стандарта {standard.value.upper()} (рабочая)"
|
||||
|
||||
fig["raw_sweep_data"] = {
|
||||
"sweep_number": sweep.sweep_number,
|
||||
@ -390,13 +390,13 @@ async def get_working_calibration_standards_plots() -> dict[str, Any]:
|
||||
}
|
||||
individual[standard.value] = fig
|
||||
except Exception as exc: # noqa: BLE001
|
||||
individual[standard.value] = {"error": f"Failed to generate plot for {standard.value}: {exc}"}
|
||||
individual[standard.value] = {"error": f"Не удалось построить график для {standard.value}: {exc}"}
|
||||
|
||||
if not individual:
|
||||
raise HTTPException(status_code=404, detail="No valid plots generated for working calibration")
|
||||
raise HTTPException(status_code=404, detail="Не удалось создать графики для рабочей калибровки")
|
||||
|
||||
return {
|
||||
"calibration_name": "Working Calibration",
|
||||
"calibration_name": "Рабочая калибровка",
|
||||
"preset": {"filename": working.preset.filename, "mode": working.preset.mode.value},
|
||||
"individual_plots": individual,
|
||||
"is_working": True,
|
||||
@ -425,11 +425,11 @@ async def get_references(preset_filename: str | None = None) -> List[dict[str, A
|
||||
None
|
||||
)
|
||||
if not preset:
|
||||
raise HTTPException(status_code=404, detail=f"Preset '{preset_filename}' not found")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет \"{preset_filename}\" не найден")
|
||||
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")
|
||||
raise HTTPException(status_code=400, detail="Текущий пресет не выбран и параметр preset_filename не указан")
|
||||
|
||||
references = settings_manager.get_available_references(preset)
|
||||
|
||||
@ -462,11 +462,11 @@ async def get_current_reference(preset_filename: str | None = None) -> dict[str,
|
||||
None
|
||||
)
|
||||
if not preset:
|
||||
raise HTTPException(status_code=404, detail=f"Preset '{preset_filename}' not found")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет \"{preset_filename}\" не найден")
|
||||
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")
|
||||
raise HTTPException(status_code=400, detail="Текущий пресет не выбран и параметр preset_filename не указан")
|
||||
|
||||
current_ref = settings_manager.get_current_reference(preset)
|
||||
|
||||
@ -500,11 +500,11 @@ async def create_reference(request: CreateReferenceRequest) -> dict[str, Any]:
|
||||
None
|
||||
)
|
||||
if not preset:
|
||||
raise HTTPException(status_code=404, detail=f"Preset '{request.preset_filename}' not found")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет \"{request.preset_filename}\" не найден")
|
||||
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")
|
||||
raise HTTPException(status_code=400, detail="Текущий пресет не выбран и параметр preset_filename не указан")
|
||||
|
||||
# Create reference using the new capture method
|
||||
reference_info = settings_manager.capture_reference_from_acquisition(
|
||||
@ -517,7 +517,7 @@ async def create_reference(request: CreateReferenceRequest) -> dict[str, Any]:
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Reference '{request.name}' created successfully",
|
||||
"message": f"Эталон \"{request.name}\" успешно создан",
|
||||
"reference": {
|
||||
"name": reference_info.name,
|
||||
"timestamp": reference_info.timestamp.isoformat(),
|
||||
@ -545,20 +545,20 @@ async def set_current_reference(request: SetReferenceRequest) -> dict[str, Any]:
|
||||
None
|
||||
)
|
||||
if not preset:
|
||||
raise HTTPException(status_code=404, detail=f"Preset '{request.preset_filename}' not found")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет \"{request.preset_filename}\" не найден")
|
||||
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")
|
||||
raise HTTPException(status_code=400, detail="Текущий пресет не выбран и параметр preset_filename не указан")
|
||||
|
||||
success = settings_manager.set_current_reference(request.name, preset)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail=f"Reference '{request.name}' not found")
|
||||
raise HTTPException(status_code=404, detail=f"Эталон \"{request.name}\" не найден")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Reference '{request.name}' set as current"
|
||||
"message": f"Эталон \"{request.name}\" выбран текущим"
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
@ -575,11 +575,11 @@ async def clear_current_reference() -> dict[str, Any]:
|
||||
success = settings_manager.clear_current_reference()
|
||||
|
||||
if not success:
|
||||
raise HTTPException(status_code=500, detail="Failed to clear current reference")
|
||||
raise HTTPException(status_code=500, detail="Не удалось сбросить текущий эталон")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": "Current reference cleared"
|
||||
"message": "Текущий эталон сброшен"
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
@ -600,15 +600,15 @@ async def get_reference(reference_name: str, preset_filename: str | None = None)
|
||||
None
|
||||
)
|
||||
if not preset:
|
||||
raise HTTPException(status_code=404, detail=f"Preset '{preset_filename}' not found")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет \"{preset_filename}\" не найден")
|
||||
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")
|
||||
raise HTTPException(status_code=400, detail="Текущий пресет не выбран и параметр preset_filename не указан")
|
||||
|
||||
info = settings_manager.get_reference_info(reference_name, preset)
|
||||
if not info:
|
||||
raise HTTPException(status_code=404, detail=f"Reference '{reference_name}' not found")
|
||||
raise HTTPException(status_code=404, detail=f"Эталон \"{reference_name}\" не найден")
|
||||
|
||||
return ReferenceModel(
|
||||
name=info.name,
|
||||
@ -636,19 +636,19 @@ async def get_reference_plot(reference_name: str, preset_filename: str | None =
|
||||
None
|
||||
)
|
||||
if not preset:
|
||||
raise HTTPException(status_code=404, detail=f"Preset '{preset_filename}' not found")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет \"{preset_filename}\" не найден")
|
||||
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")
|
||||
raise HTTPException(status_code=400, detail="Текущий пресет не выбран и параметр preset_filename не указан")
|
||||
|
||||
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")
|
||||
raise HTTPException(status_code=404, detail=f"Эталон \"{reference_name}\" не найден")
|
||||
|
||||
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}'")
|
||||
raise HTTPException(status_code=404, detail=f"Данные эталона \"{reference_name}\" не найдены")
|
||||
|
||||
from vna_system.core.visualization.magnitude_chart import generate_magnitude_plot_from_sweep_data
|
||||
|
||||
@ -682,20 +682,20 @@ async def delete_reference(reference_name: str, preset_filename: str | None = No
|
||||
None
|
||||
)
|
||||
if not preset:
|
||||
raise HTTPException(status_code=404, detail=f"Preset '{preset_filename}' not found")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет \"{preset_filename}\" не найден")
|
||||
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")
|
||||
raise HTTPException(status_code=400, detail="Текущий пресет не выбран и параметр preset_filename не указан")
|
||||
|
||||
success = settings_manager.delete_reference(reference_name, preset)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail=f"Reference '{reference_name}' not found")
|
||||
raise HTTPException(status_code=404, detail=f"Эталон \"{reference_name}\" не найден")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Reference '{reference_name}' deleted successfully"
|
||||
"message": f"Эталон \"{reference_name}\" успешно удалён"
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
@ -709,7 +709,7 @@ 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"}
|
||||
return {"success": True, "message": "Текущая калибровка сброшена"}
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.error("Failed to clear current calibration", error=repr(exc))
|
||||
raise HTTPException(status_code=500, detail=str(exc))
|
||||
@ -727,17 +727,17 @@ async def delete_calibration(calibration_name: str, preset_filename: str | None
|
||||
None
|
||||
)
|
||||
if not preset:
|
||||
raise HTTPException(status_code=404, detail=f"Preset '{preset_filename}' not found")
|
||||
raise HTTPException(status_code=404, detail=f"Пресет \"{preset_filename}\" не найден")
|
||||
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")
|
||||
raise HTTPException(status_code=400, detail="Текущий пресет не выбран и параметр preset_filename не указан")
|
||||
|
||||
settings_manager.delete_calibration(preset, calibration_name)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Calibration '{calibration_name}' deleted"
|
||||
"message": f"Калибровка \"{calibration_name}\" удалена"
|
||||
}
|
||||
except HTTPException:
|
||||
raise
|
||||
|
||||
@ -28,7 +28,7 @@ async def dashboard():
|
||||
if not index_file.exists():
|
||||
logger.error(f"Dashboard template not found: {index_file}")
|
||||
return HTMLResponse(
|
||||
content="<h1>Dashboard Not Available</h1><p>The web UI template could not be found.</p>",
|
||||
content="<h1>Панель недоступна</h1><p>Не удалось найти шаблон веб-интерфейса.</p>",
|
||||
status_code=404
|
||||
)
|
||||
|
||||
@ -40,7 +40,7 @@ async def dashboard():
|
||||
except Exception as e:
|
||||
logger.error(f"Error serving dashboard: {e}")
|
||||
return HTMLResponse(
|
||||
content=f"<h1>Error</h1><p>Unable to load dashboard: {e}</p>",
|
||||
content=f"<h1>Ошибка</h1><p>Не удалось загрузить панель: {e}</p>",
|
||||
status_code=500
|
||||
)
|
||||
|
||||
@ -48,8 +48,8 @@ async def dashboard():
|
||||
async def health_ui():
|
||||
"""Health check endpoint for web UI."""
|
||||
return {
|
||||
"service": "VNA Web UI",
|
||||
"status": "healthy",
|
||||
"service": "Веб-интерфейс VNA",
|
||||
"status": "работает",
|
||||
"web_ui_dir": str(WEB_UI_DIR),
|
||||
"static_dir_exists": STATIC_DIR.exists(),
|
||||
"templates_dir_exists": TEMPLATES_DIR.exists(),
|
||||
|
||||
@ -4,5 +4,5 @@
|
||||
"autoscale": true,
|
||||
"show_magnitude": true,
|
||||
"show_phase": false,
|
||||
"open_air": false
|
||||
"open_air": true
|
||||
}
|
||||
@ -94,6 +94,23 @@
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.btn__shortcut {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 var(--space-2);
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border-secondary);
|
||||
background-color: var(--bg-tertiary);
|
||||
color: var(--text-tertiary);
|
||||
font-size: var(--font-size-xs);
|
||||
font-weight: var(--font-weight-medium);
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
line-height: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* Processor Toggles */
|
||||
.processor-toggles {
|
||||
display: flex;
|
||||
|
||||
@ -397,6 +397,10 @@ body {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.header__controls .btn__shortcut {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.header__controls .btn {
|
||||
min-width: auto;
|
||||
padding: var(--space-2);
|
||||
|
||||
@ -198,8 +198,25 @@ class VNADashboard {
|
||||
* Handle keyboard shortcuts
|
||||
*/
|
||||
handleKeyboardShortcuts(event) {
|
||||
const activeElement = document.activeElement;
|
||||
const activeTag = activeElement?.tagName?.toLowerCase();
|
||||
const isEditable = activeElement?.isContentEditable ||
|
||||
activeTag === 'input' ||
|
||||
activeTag === 'textarea' ||
|
||||
activeTag === 'select';
|
||||
const key = event.key?.toLowerCase();
|
||||
|
||||
if (!isEditable && !event.ctrlKey && !event.metaKey && !event.altKey && key === 's') {
|
||||
if (event.repeat) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
this.acquisition?.triggerSingleSweep?.();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ctrl/Cmd + Shift + R: Reconnect WebSocket
|
||||
if ((event.ctrlKey || event.metaKey) && event.key === 'r' && event.shiftKey) {
|
||||
if ((event.ctrlKey || event.metaKey) && key === 'r' && event.shiftKey) {
|
||||
event.preventDefault();
|
||||
this.websocket.reconnect();
|
||||
}
|
||||
|
||||
@ -51,9 +51,10 @@
|
||||
<span data-icon="square"></span>
|
||||
<span class="btn__text">Стоп</span>
|
||||
</button>
|
||||
<button class="btn btn--accent btn--sm" id="singleSweepBtn" title="Запустить одиночный свип">
|
||||
<button class="btn btn--accent btn--sm" id="singleSweepBtn" title="Запустить одиночный свип (клавиша S)" aria-keyshortcuts="S">
|
||||
<span data-icon="zap"></span>
|
||||
<span class="btn__text">Одиночный</span>
|
||||
<span class="btn__shortcut" aria-hidden="true">S</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user