from contextlib import asynccontextmanager import logging import platform from fastapi import FastAPI, WebSocket from fastapi.middleware.cors import CORSMiddleware from app.core.database import connect_to_mongo, close_mongo_connection from app.api.routes import health, projects, terrain, coverage, regions, system, gpu from app.api.websocket import websocket_endpoint logger = logging.getLogger("rfcp.startup") def check_gpu_availability(): """Log GPU status on startup for debugging.""" is_wsl = "microsoft" in platform.release().lower() env_note = " (WSL2)" if is_wsl else "" # Check CuPy / CUDA try: import cupy as cp device_count = cp.cuda.runtime.getDeviceCount() if device_count > 0: props = cp.cuda.runtime.getDeviceProperties(0) name = props["name"] if isinstance(name, bytes): name = name.decode() mem_mb = props["totalGlobalMem"] // (1024 * 1024) logger.info(f"GPU detected{env_note}: {name} ({mem_mb} MB VRAM)") logger.info(f"CuPy {cp.__version__}, CUDA devices: {device_count}") else: logger.warning(f"CuPy installed but no CUDA devices found{env_note}") except Exception as e: logger.warning(f"CuPy FAILED {env_note}: {e}") if is_wsl: logger.warning("Install: pip3 install cupy-cuda12x --break-system-packages") else: logger.warning("Install: pip install cupy-cuda12x") except Exception as e: logger.warning(f"CuPy error{env_note}: {e}") # Check PyOpenCL try: import pyopencl as cl platforms = cl.get_platforms() for p in platforms: for d in p.get_devices(): logger.info(f"OpenCL device: {d.name.strip()}") except Exception as e: logger.debug("PyOpenCL not installed (optional)") except Exception: pass @asynccontextmanager async def lifespan(app: FastAPI): # Log GPU status on startup check_gpu_availability() await connect_to_mongo() yield await close_mongo_connection() app = FastAPI( title="RFCP Backend API", description="RF Coverage Planning Backend", version="3.0.0", lifespan=lifespan, ) # CORS for frontend app.add_middleware( CORSMiddleware, allow_origins=["https://rfcp.eliah.one", "http://localhost:5173", "http://127.0.0.1:8888"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # REST routes app.include_router(health.router, prefix="/api/health", tags=["health"]) app.include_router(projects.router, prefix="/api/projects", tags=["projects"]) app.include_router(terrain.router, prefix="/api/terrain", tags=["terrain"]) app.include_router(coverage.router, prefix="/api/coverage", tags=["coverage"]) app.include_router(regions.router, prefix="/api/regions", tags=["regions"]) app.include_router(system.router, prefix="/api/system", tags=["system"]) app.include_router(gpu.router, prefix="/api/gpu", tags=["gpu"]) # WebSocket endpoint for real-time coverage with progress app.websocket("/ws")(websocket_endpoint) @app.get("/") async def root(): return {"message": "RFCP Backend API", "version": "3.0.0"} if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8090)