fix(chain): 자동 설계 체인에 배수유역 분석·관 지점 확정 단계 추가
계획선 1차 로직(design_pipe_anchored_profile)은 저장된 관 지점을 변화점으로 쓰는데, 자동 설계 체인이 그 파일을 만들어 주지 않았다. 그래서 초기 계획선이 2차 폴백(지반 추종 직선 분할)으로 산출돼 관 자리 틸팅·R이 통째로 빠지고, B05/B06에 들어와도 배수유역도가 비어 있었다. - _prepare_drainage_pipes_and_reprofile() 신설: 배수유역 분석 → 기본 관 지점 확정 저장 → 배관 정착 계획선으로 종횡단 재생성. 실패해도 체인을 멈추지 않는다. - run_auto_design_chain: B05 경로 확정 다음, B06 확정 전에 호출. - run_redesign_chain: 노선이 바뀌었으므로 refresh=True로 호출 — 저장된 배수유역 분석 응답은 노선 변경을 스스로 알지 못한다. 검증(테스트 프로젝트 fc86f247, 노선 350m): 관 4개(계류 1 + 간격 보충 3) 확정 후 종횡단 재생성 → PVI 6개(BP + 관 4자리 + EP)가 관 측점과 일치하고 각 자리에 R(1108.57/180.79/450.0/580.97m)이 붙는 것을 확인. 반영 전에는 관과 무관한 지반 추종 변화점이었다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,81 @@ def _planned_route_points_in_project_crs(project_root: Path) -> list[dict[str, f
|
||||
return [{"x": x, "y": y} for x, y in points]
|
||||
|
||||
|
||||
async def _prepare_drainage_pipes_and_reprofile(
|
||||
project_id: UUID, route_id: int, *, refresh: bool = False
|
||||
) -> bool:
|
||||
"""배수유역 분석 → 기본 관 지점 확정 저장 → 배관 정착 계획선으로 종횡단 재생성.
|
||||
|
||||
계획선 1차 로직(`design_pipe_anchored_profile`)은 **저장된 관 지점**을 변화점으로 쓴다.
|
||||
자동 체인이 이 파일을 만들어 주지 않으면 초기 계획선이 2차 폴백(지반 추종 직선 분할)으로
|
||||
산출돼 관 자리 틸팅·R이 통째로 빠진다(2026-08-08 사용자 보고). 같은 이유로 B05·B06에
|
||||
들어왔을 때 배수유역도도 비어 있게 된다 — 여기서 미리 계산해 영구저장소에 남긴다.
|
||||
|
||||
실패해도 예외를 던지지 않는다 — 관 없이 만든 계획선이라도 남는 편이 낫다.
|
||||
"""
|
||||
from B04_PreProcess.B04_PreProcess_Router_Basins import put_pipe_points
|
||||
from B04_PreProcess.B04_PreProcess_Router_Watershed import get_primary_region
|
||||
from B06_Section.B06_Section_Repository import get_latest_section_options
|
||||
from B06_Section.B06_Section_Router import regenerate_sections
|
||||
from B06_Section.B06_Section_Schema import SectionRegenerateRequest
|
||||
from config.config_db import get_db_pool
|
||||
from config.config_system import SECTION_CROSS_HALF_WIDTH_M
|
||||
|
||||
try:
|
||||
# 1) 배수유역 분석(30초 내외). 저장분이 있으면 그대로 쓴다.
|
||||
# 저장분은 노선이 바뀌었는지 보지 않으므로, 노선을 다시 푼 뒤에는 refresh로 부른다.
|
||||
region = await get_primary_region(project_id, refresh=refresh)
|
||||
if isinstance(region, JSONResponse):
|
||||
logger.error(
|
||||
"자동 설계 체인 배수유역 분석 실패(계획선 폴백 유지): project_id=%s status=%s",
|
||||
project_id,
|
||||
region.status_code,
|
||||
)
|
||||
return False
|
||||
|
||||
# 2) 기본 관(도로 × 상류 세류선) + 최대 간격 자동 보충을 정본으로 저장한다.
|
||||
pipes = await put_pipe_points(project_id, None)
|
||||
if isinstance(pipes, JSONResponse):
|
||||
logger.error(
|
||||
"자동 설계 체인 관 지점 확정 실패: project_id=%s status=%s",
|
||||
project_id,
|
||||
pipes.status_code,
|
||||
)
|
||||
return False
|
||||
pipe_count = int(pipes.get("saved_count") or 0)
|
||||
|
||||
# 3) 저장된 관 자리를 물려 계획선을 다시 산출한다. 반폭은 이미 쓰던 값을 유지한다.
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection:
|
||||
stored_options = await get_latest_section_options(connection, project_id)
|
||||
half_width = float(
|
||||
(stored_options or {}).get("cross_half_width_m") or SECTION_CROSS_HALF_WIDTH_M
|
||||
)
|
||||
regenerated = await regenerate_sections(
|
||||
project_id, route_id, SectionRegenerateRequest(cross_half_width_m=half_width)
|
||||
)
|
||||
if isinstance(regenerated, JSONResponse):
|
||||
logger.error(
|
||||
"자동 설계 체인 배관 정착 계획선 재산출 실패: project_id=%s status=%s",
|
||||
project_id,
|
||||
regenerated.status_code,
|
||||
)
|
||||
return False
|
||||
logger.info(
|
||||
"자동 설계 체인 배수유역·배관 정착 계획선 완료: project_id=%s route_id=%s 관=%d개",
|
||||
project_id,
|
||||
route_id,
|
||||
pipe_count,
|
||||
)
|
||||
return True
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"자동 설계 체인 배수유역·관 지점 단계 실패(계획선은 폴백으로 유지): project_id=%s",
|
||||
project_id,
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
async def run_auto_design_chain(
|
||||
project_id: UUID, surface_model_id: int | None = None
|
||||
) -> dict[str, Any] | None:
|
||||
@@ -134,6 +209,10 @@ async def run_auto_design_chain(
|
||||
)
|
||||
return None
|
||||
|
||||
# 4.5) 배수유역 분석 → 관 지점 확정 → 배관 정착 계획선 재산출.
|
||||
# B06 확정 전에 끝내야 확정 스냅샷에 배관 정착 계획선이 담긴다.
|
||||
await _prepare_drainage_pipes_and_reprofile(project_id, route_id)
|
||||
|
||||
# 5) B06 횡단 설계 확정 — 미지정 측점을 기본값으로 채워 저장. stage 3은
|
||||
# IN_PROGRESS(스텝바 노란 표시)로 남겨 사용자 검토·확정을 기다린다.
|
||||
sections_result = await confirm_sections(
|
||||
@@ -315,6 +394,10 @@ async def run_redesign_chain(
|
||||
confirm_result.status_code,
|
||||
)
|
||||
return
|
||||
# 노선이 바뀌었으므로 배수유역·관 지점도 새 노선 기준으로 다시 만든다 — 옛 관 자리로
|
||||
# 계획선을 앉히면 전부 어긋나고, 저장분은 노선 변경을 스스로 알지 못한다.
|
||||
await _prepare_drainage_pipes_and_reprofile(project_id, new_route_id, refresh=True)
|
||||
|
||||
old_options = ((old_longitudinal or {}).get("data") or {}).get("options") or {}
|
||||
section_request = (
|
||||
SectionConfirmRequest(standard_cross_section=old_options["standard_cross_section"])
|
||||
|
||||
Reference in New Issue
Block a user