mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 22:11:13 +00:00
feat(01-08): rewire app.py lifespan and deps.py to use build_pipeline
- app.py: replace inline component wiring with build_pipeline(env=cfg.env) - Store processor as app.state.processor (and backwards-compat pipeline_components) - RuntimeConfig replaces get_settings(); MAVLink stop() on shutdown - deps.py: get_flight_processor prefers app.state.processor from lifespan - Falls back to build_pipeline() for test contexts without lifespan - Per-request repo/streamer swap preserved
This commit is contained in:
@@ -5,7 +5,7 @@ from fastapi import Depends, HTTPException, Request
|
|||||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from gps_denied.config import get_settings
|
from gps_denied.config import RuntimeConfig, get_settings
|
||||||
from gps_denied.core.processor import FlightProcessor
|
from gps_denied.core.processor import FlightProcessor
|
||||||
from gps_denied.core.sse import SSEEventStreamer
|
from gps_denied.core.sse import SSEEventStreamer
|
||||||
from gps_denied.db.engine import get_session
|
from gps_denied.db.engine import get_session
|
||||||
@@ -65,14 +65,23 @@ async def get_flight_processor(
|
|||||||
) -> FlightProcessor:
|
) -> FlightProcessor:
|
||||||
global _processor
|
global _processor
|
||||||
if _processor is None:
|
if _processor is None:
|
||||||
eskf_config = getattr(request.app.state, "eskf_config", None)
|
# Prefer the processor already built by lifespan (via build_pipeline)
|
||||||
_processor = FlightProcessor(repo, sse, eskf_config=eskf_config)
|
lifespan_processor = getattr(request.app.state, "processor", None)
|
||||||
# Підключаємо pipeline компоненти з lifespan
|
if lifespan_processor is not None:
|
||||||
components = getattr(request.app.state, "pipeline_components", None)
|
_processor = lifespan_processor
|
||||||
if components:
|
else:
|
||||||
_processor.attach_components(**components)
|
# Fallback: build pipeline directly (e.g. in tests without lifespan)
|
||||||
# Оновлюємо repo (нова сесія на кожен запит)
|
from gps_denied.pipeline import build_pipeline
|
||||||
|
_settings = RuntimeConfig()
|
||||||
|
_processor = build_pipeline(
|
||||||
|
env=_settings.env,
|
||||||
|
config=_settings,
|
||||||
|
repository=repo,
|
||||||
|
streamer=sse,
|
||||||
|
)
|
||||||
|
# Оновлюємо repo та streamer (нова сесія на кожен запит)
|
||||||
_processor.repository = repo
|
_processor.repository = repo
|
||||||
|
_processor.streamer = sse
|
||||||
return _processor
|
return _processor
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+28
-48
@@ -7,70 +7,50 @@ from fastapi import FastAPI
|
|||||||
|
|
||||||
from gps_denied import __version__
|
from gps_denied import __version__
|
||||||
from gps_denied.api.routers import flights
|
from gps_denied.api.routers import flights
|
||||||
from gps_denied.config import get_settings
|
from gps_denied.config import RuntimeConfig
|
||||||
|
from gps_denied.pipeline import build_pipeline
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI):
|
async def lifespan(app: FastAPI):
|
||||||
"""Initialise core pipeline components on startup."""
|
"""Initialise core pipeline components on startup via build_pipeline."""
|
||||||
from gps_denied.core.chunk_manager import RouteChunkManager
|
cfg = RuntimeConfig()
|
||||||
from gps_denied.core.coordinates import CoordinateTransformer
|
processor = build_pipeline(env=cfg.env, config=cfg)
|
||||||
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()
|
# Retrieve MAVLink bridge from processor internals for lifecycle management
|
||||||
|
mavlink = processor._mavlink
|
||||||
|
|
||||||
mm = ModelManager(engine_dir=str(settings.models.weights_dir))
|
app.state.processor = processor
|
||||||
vo = create_vo_backend(model_manager=mm)
|
app.state.config = cfg
|
||||||
gpr = GlobalPlaceRecognition(mm)
|
# Keep backwards-compat key so any code reading pipeline_components still works
|
||||||
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 = {
|
app.state.pipeline_components = {
|
||||||
"vo": vo, "gpr": gpr, "metric": metric,
|
"vo": processor._vo,
|
||||||
"graph": graph, "recovery": recovery,
|
"gpr": processor._gpr,
|
||||||
"chunk_mgr": chunk_mgr, "rotation": rotation,
|
"metric": processor._metric,
|
||||||
"coord": coord, "satellite": satellite,
|
"graph": processor._graph,
|
||||||
|
"recovery": processor._recovery,
|
||||||
|
"chunk_mgr": processor._chunk_mgr,
|
||||||
|
"rotation": processor._rotation,
|
||||||
|
"coord": processor._coord,
|
||||||
|
"satellite": processor._satellite,
|
||||||
"mavlink": mavlink,
|
"mavlink": mavlink,
|
||||||
}
|
}
|
||||||
app.state.eskf_config = eskf_config
|
app.state.eskf_config = processor._eskf_config
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Pipeline ready — MAVLink: %s, tiles: %s",
|
"Pipeline ready — env=%s, MAVLink: %s, tiles: %s",
|
||||||
settings.mavlink.connection, settings.satellite.tile_dir,
|
cfg.env, cfg.mavlink.connection, cfg.satellite.tile_dir,
|
||||||
)
|
)
|
||||||
yield
|
yield
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup MAVLink on shutdown
|
||||||
try:
|
if mavlink is not None:
|
||||||
await mavlink.stop()
|
try:
|
||||||
except Exception:
|
await mavlink.stop()
|
||||||
pass
|
except Exception:
|
||||||
|
pass
|
||||||
app.state.pipeline_components = None
|
app.state.pipeline_components = None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user