134 lines
5.3 KiB
Python
134 lines
5.3 KiB
Python
"""B05 경로 설계 FastAPI 라우터."""
|
|
|
|
import asyncio
|
|
import logging
|
|
from pathlib import Path
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
|
|
from B05_wf2_Route.B05_wf2_Route_Engine import run_route_design
|
|
from B05_wf2_Route.B05_wf2_Route_Repository import (
|
|
confirm_route,
|
|
create_route,
|
|
create_route_statistics,
|
|
get_latest_route,
|
|
insert_route_points,
|
|
)
|
|
from B05_wf2_Route.B05_wf2_Route_Schema import (
|
|
RouteConfirmResponse,
|
|
RouteSolveRequest,
|
|
RouteSolveResponse,
|
|
)
|
|
from common_util.common_util_storage import resolve_stored_project_path
|
|
from config.config_db import get_db_pool
|
|
|
|
logger = logging.getLogger(__name__)
|
|
router = APIRouter(prefix="/api/projects", tags=["B05 Route Design"])
|
|
|
|
|
|
@router.post("/{project_id}/route/solve", response_model=RouteSolveResponse)
|
|
async def solve_route(
|
|
project_id: UUID, request: RouteSolveRequest
|
|
) -> RouteSolveResponse | JSONResponse:
|
|
"""경로 탐색을 실행하고 결과를 GeoJSON 저장 + DB 기록한다."""
|
|
pool = get_db_pool()
|
|
try:
|
|
async with pool.acquire() as connection:
|
|
stored_path = await get_project_storage_relative_path(connection, project_id)
|
|
project_root = Path(resolve_stored_project_path(stored_path))
|
|
|
|
# 무거운 경로 탐색은 이벤트 루프를 막지 않도록 별도 스레드에서 실행.
|
|
design = await asyncio.to_thread(
|
|
run_route_design,
|
|
project_root,
|
|
request.filter_key,
|
|
request.method,
|
|
request.smooth,
|
|
request.points_data(),
|
|
request.options(),
|
|
request.algorithm,
|
|
)
|
|
solver = design["solver_result"]
|
|
metrics = solver["metrics"]
|
|
|
|
await connection.begin()
|
|
try:
|
|
route_id = await create_route(
|
|
connection,
|
|
project_id=project_id,
|
|
surface_model_id=request.surface_model_id,
|
|
total_length_m=metrics.get("length_m"),
|
|
start_chainage_m=0.0,
|
|
end_chainage_m=metrics.get("length_m"),
|
|
grade_percent=design["grade_percent"],
|
|
constraints=design["constraints"],
|
|
algorithm_params=design["algorithm_params"],
|
|
route_data_path=design["route_data_path"],
|
|
)
|
|
await insert_route_points(connection, route_id, design["render_points"])
|
|
stats = design["statistics"]
|
|
await create_route_statistics(
|
|
connection,
|
|
route_id=route_id,
|
|
min_slope=stats["min_slope"],
|
|
max_slope=stats["max_slope"],
|
|
mean_slope=stats["mean_slope"],
|
|
cost_score=stats["cost_score"],
|
|
)
|
|
await connection.commit()
|
|
except Exception:
|
|
await connection.rollback()
|
|
raise
|
|
|
|
return RouteSolveResponse(
|
|
project_id=str(project_id),
|
|
route_id=route_id,
|
|
total_length_m=metrics.get("length_m", 0.0),
|
|
metrics=metrics,
|
|
required_points_ok=solver["required_points_ok"],
|
|
route_data_path=design["route_data_path"],
|
|
)
|
|
except LookupError as exc:
|
|
return JSONResponse(status_code=404, content={"status": "error", "message": str(exc)})
|
|
except FileNotFoundError as exc:
|
|
return JSONResponse(status_code=404, content={"status": "error", "message": str(exc)})
|
|
except (OSError, ValueError) as exc:
|
|
return JSONResponse(status_code=400, content={"status": "error", "message": str(exc)})
|
|
except Exception:
|
|
logger.exception("B05 경로 탐색 실패: project_id=%s", project_id)
|
|
return JSONResponse(
|
|
status_code=500,
|
|
content={"status": "error", "message": "경로 탐색 처리 중 오류가 발생했습니다."},
|
|
)
|
|
|
|
|
|
@router.post("/{project_id}/route/confirm", response_model=RouteConfirmResponse)
|
|
async def confirm_latest_route(project_id: UUID) -> RouteConfirmResponse | JSONResponse:
|
|
"""프로젝트의 최신 경로를 확정(CONFIRMED)한다."""
|
|
pool = get_db_pool()
|
|
try:
|
|
async with pool.acquire() as connection:
|
|
latest = await get_latest_route(connection, project_id)
|
|
if not latest:
|
|
return JSONResponse(
|
|
status_code=404,
|
|
content={"status": "error", "message": "확정할 경로가 없습니다."},
|
|
)
|
|
await connection.begin()
|
|
try:
|
|
await confirm_route(connection, latest["id"])
|
|
await connection.commit()
|
|
except Exception:
|
|
await connection.rollback()
|
|
raise
|
|
return RouteConfirmResponse(project_id=str(project_id), route_id=latest["id"])
|
|
except Exception:
|
|
logger.exception("B05 경로 확정 실패: project_id=%s", project_id)
|
|
return JSONResponse(
|
|
status_code=500,
|
|
content={"status": "error", "message": "경로 확정 처리 중 오류가 발생했습니다."},
|
|
)
|