Files
vna_system/vna_system/api/main.py
2025-09-23 18:42:55 +03:00

143 lines
4.1 KiB
Python

from __future__ import annotations
import json
import logging
import sys
from contextlib import asynccontextmanager
from typing import Any, Dict
import uvicorn
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
from pathlib import Path
import vna_system.core.singletons as singletons
from vna_system.core.processing.sweep_processor import SweepProcessingManager
from vna_system.api.websockets.websocket_handler import WebSocketManager
from vna_system.api.endpoints import health, processing, web_ui
from vna_system.api.websockets import processing as ws_processing
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Disable noisy third-party loggers
logging.getLogger('kaleido').setLevel(logging.ERROR)
logging.getLogger('choreographer').setLevel(logging.ERROR)
logging.getLogger('kaleido.kaleido').setLevel(logging.ERROR)
logging.getLogger('choreographer.browsers.chromium').setLevel(logging.ERROR)
logging.getLogger('choreographer.browser_async').setLevel(logging.ERROR)
logging.getLogger('choreographer.utils._tmpfile').setLevel(logging.ERROR)
logging.getLogger('kaleido._kaleido_tab').setLevel(logging.ERROR)
logger = logging.getLogger(__name__)
def load_config(config_path: str = "vna_system/api/api_config.json") -> Dict[str, Any]:
"""Load API configuration from file."""
try:
with open(config_path, 'r') as f:
config = json.load(f)
logger.info(f"Loaded API config from {config_path}")
return config
except Exception as e:
logger.error(f"Failed to load config: {e}")
sys.exit(1)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""FastAPI lifespan events."""
# Startup
logger.info("Starting VNA API Server...")
try:
# Load config
config = load_config()
# Set log level
log_level = config.get("logging", {}).get("level", "INFO")
logging.getLogger().setLevel(getattr(logging, log_level))
# Start acquisition
logger.info("Starting data acquisition...")
singletons.vna_data_acquisition_instance.start()
# Connect processing to acquisition
singletons.processing_manager.set_sweep_buffer(singletons.vna_data_acquisition_instance.sweep_buffer)
singletons.processing_manager.start()
logger.info("Sweep processing started")
logger.info("VNA API Server started successfully")
yield
except Exception as e:
logger.error(f"Error during startup: {e}")
raise
# Shutdown
logger.info("Shutting down VNA API Server...")
if singletons.processing_manager:
singletons.processing_manager.stop()
logger.info("Processing stopped")
if singletons.vna_data_acquisition_instance and singletons.vna_data_acquisition_instance.is_running:
singletons.vna_data_acquisition_instance.stop()
logger.info("Acquisition stopped")
logger.info("VNA API Server shutdown complete")
# Create FastAPI app
app = FastAPI(
title="VNA System API",
description="Real-time VNA data acquisition and processing API",
version="1.0.0",
lifespan=lifespan
)
# Mount static files for web UI
WEB_UI_DIR = Path(__file__).parent.parent / "web_ui"
STATIC_DIR = WEB_UI_DIR / "static"
if STATIC_DIR.exists():
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
logger.info(f"Mounted static files from: {STATIC_DIR}")
else:
logger.warning(f"Static directory not found: {STATIC_DIR}")
# Include routers
app.include_router(web_ui.router) # Web UI should be first for root path
app.include_router(health.router)
app.include_router(processing.router)
app.include_router(ws_processing.router)
def main():
"""Main entry point."""
config = load_config()
# Server configuration
server_config = config.get("server", {})
host = server_config.get("host", "0.0.0.0")
port = server_config.get("port", 8000)
# Start server
uvicorn.run(
"vna_system.api.main:app",
host=host,
port=port,
log_level="info",
reload=False
)
if __name__ == "__main__":
main()