"""FastAPI application factory.""" import logging from contextlib import asynccontextmanager from fastapi import FastAPI from gps_denied import __version__ from gps_denied.api.routers import flights from gps_denied.config import get_settings logger = logging.getLogger(__name__) @asynccontextmanager async def lifespan(app: FastAPI): """Initialise core pipeline components on startup.""" from gps_denied.core.chunk_manager import RouteChunkManager from gps_denied.core.coordinates import CoordinateTransformer from gps_denied.core.gpr import GlobalPlaceRecognition from gps_denied.core.graph import FactorGraphOptimizer from gps_denied.core.mavlink import MAVLinkBridge from gps_denied.core.metric import MetricRefinement from gps_denied.core.models import ModelManager from gps_denied.core.recovery import FailureRecoveryCoordinator from gps_denied.core.rotation import ImageRotationManager from gps_denied.core.satellite import SatelliteDataManager from gps_denied.core.vo import create_vo_backend from gps_denied.schemas.eskf import ESKFConfig from gps_denied.schemas.graph import FactorGraphConfig settings = get_settings() mm = ModelManager(engine_dir=str(settings.models.weights_dir)) vo = create_vo_backend(model_manager=mm) gpr = GlobalPlaceRecognition(mm) metric = MetricRefinement(mm) graph = FactorGraphOptimizer(FactorGraphConfig()) chunk_mgr = RouteChunkManager(graph) recovery = FailureRecoveryCoordinator(chunk_mgr, gpr, metric) rotation = ImageRotationManager(mm) coord = CoordinateTransformer() satellite = SatelliteDataManager(tile_dir=settings.satellite.tile_dir) mavlink = MAVLinkBridge( connection_string=settings.mavlink.connection, output_hz=settings.mavlink.output_hz, telemetry_hz=settings.mavlink.telemetry_hz, ) # ESKF config from env vars (per-airframe tuning) eskf_config = ESKFConfig(**settings.eskf.model_dump()) # Store on app.state so deps can access them app.state.pipeline_components = { "vo": vo, "gpr": gpr, "metric": metric, "graph": graph, "recovery": recovery, "chunk_mgr": chunk_mgr, "rotation": rotation, "coord": coord, "satellite": satellite, "mavlink": mavlink, } app.state.eskf_config = eskf_config logger.info( "Pipeline ready — MAVLink: %s, tiles: %s", settings.mavlink.connection, settings.satellite.tile_dir, ) yield # Cleanup try: await mavlink.stop() except Exception: pass app.state.pipeline_components = None def create_app() -> FastAPI: """Factory function to create and configure the FastAPI application.""" app = FastAPI( title="GPS-Denied Onboard API", description="REST API for UAV Flight Processing in GPS-denied environments.", version=__version__, lifespan=lifespan, ) app.include_router(flights.router) @app.get("/health", tags=["Health"]) async def health() -> dict[str, str]: """Simple health check endpoint.""" return {"status": "ok"} return app app = create_app()