feat(횡단): 좌측 [소단] 패널 — 구간에 놓고 빼기 + 확정 뒤에도 남게 함 (계획서 3-9)
사용자가 소단을 **직접 놓는** 화면을 만들었음. 프로그램이 「붕괴 우려 지역」을 판정하지 않고, 법정 기준 셋 중 무엇도 자동 적용하지 않음(2026-09-07 사용자 확정). 폼은 C군 구간형 구조물과 **같은 꼴** — 기준 측점 + 전·후 거리로 종단 범위를 잡고 폭·간격·기울기를 함께 받음. 기본값 폭 0.5m · 간격(사면길이) 3.0m · 안쪽 2°. 새 모듈 `B06_Section_UI_Berm_Panel.ts` 로 뺐음 — 좌측 페이지가 이미 700줄을 넘어 있어 거기 더 넣지 않고 두 줄만 꽂음(그 파일 분리는 별건). 세션에는 **구간 목록**으로 둠. 측점별로 펴서 저장하면 「어디부터 어디까지 놓았나」를 되짚을 수 없음. 읽는 자리에서 펴고, 서버에는 측점키 dict 로 실어 보냄. **확정 뒤에도 계단이 남게** — 소단 제원을 설계 결과에 되싣고(`berm`), `USER_TOUCHED_KEYS` 에 넣어 재계산이 지우지 않게 함. 서버 재계산은 세션값이 없으면 저장분을 씀(`stored_berm`). ⚠ 소단은 사용자 조작값이면서 **기하 입력**이라 다른 사용자 키와 다름 — 계산 뒤에 키만 베껴 붙이면 설계선·면적은 계단 없이 나오고 `berm` 값만 남아 서로 어긋남. 그래서 단측점 설계 경로는 저장분을 **계산 전에** 읽도록 순서를 바꿨고, 포장 강제·세월교 노면 하강 재계산 경로에도 저장분 소단을 실었음. `extra_spans` 가 그렇게 빠져 있던 자리와 같음. 자체검증 — 새 시험 2건(저장분만으로 같은 설계가 나오는지 · 값이 없거나 손상되면 None). 전체 541 passed · 18 skipped. TS 타입 검사·ruff 통과. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,7 @@ from B06_Section.B06_Section_Router_Design import (
|
||||
USER_TOUCHED_KEYS,
|
||||
ford_drop_at,
|
||||
ford_surface_drops,
|
||||
stored_berm,
|
||||
)
|
||||
from B06_Section.B06_Section_Router_Design import (
|
||||
attach_default_designs as _attach_default_designs,
|
||||
@@ -607,6 +608,16 @@ async def compute_cross_section_design(
|
||||
content={"status": "error", "message": "종횡단 상세 결과가 없습니다."},
|
||||
)
|
||||
stored_path = await get_project_storage_relative_path(connection, project_id)
|
||||
# 저장분을 **계산 전에** 읽는다 — 소단은 사용자 조작값이면서 **기하 입력**이라,
|
||||
# 나중에 키만 베껴 붙이면 설계선·면적은 계단 없이 나오고 `berm` 값만 남아
|
||||
# 서로 어긋난다(2026-09-07). 값을 나르는 다른 사용자 키와 다른 점이다.
|
||||
stored_designs = await get_cross_section_designs(connection, route_id)
|
||||
stored_design: dict[str, Any] | None = None
|
||||
for record in stored_designs:
|
||||
if abs(float(record["chainage_m"]) - request.chainage_m) < 0.01:
|
||||
candidate = record.get("design")
|
||||
stored_design = candidate if isinstance(candidate, dict) else None
|
||||
break
|
||||
project_root = Path(resolve_stored_project_path(stored_path))
|
||||
samples, design_elevation, pavement_suggested, cross_record = await asyncio.to_thread(
|
||||
_read_cross_design_inputs,
|
||||
@@ -627,27 +638,22 @@ async def compute_cross_section_design(
|
||||
two_stage_slope=request.two_stage_slope,
|
||||
ditch_enabled=request.ditch_enabled,
|
||||
surface_drop_m=ford_drop_at(request.chainage_m, ford_surface_drops(project_root)),
|
||||
berm=stored_berm(stored_design or {}),
|
||||
**curve_widening_args(cross_record),
|
||||
)
|
||||
# B06 지정 시점은 잠정치. B07 도면 확정 시 동일 엔진으로 재계산해 confirmed로 승격한다.
|
||||
design["status"] = "provisional"
|
||||
# 법정 근거 문구 표기용 — 사용자가 포장을 바꿔도 제안 여부는 그대로 남긴다.
|
||||
design["pavement_suggested"] = pavement_suggested
|
||||
# 표시 설정(측점 개별 반폭)은 계산 입력이 아니다 — 저장분에서 이월해 재계산이
|
||||
# 지우지 않게 한다(2026-08-06). 목록을 여기 다시 적지 않는다 — 서버의 한 벌은
|
||||
# `B06_Section_Router_Design.USER_TOUCHED_KEYS` 다(2026-09-07). 예전에는 여기에
|
||||
# 따로 적어 두어 `extra_spans` 가 빠져 있었다.
|
||||
if stored_design is not None:
|
||||
for key in USER_TOUCHED_KEYS:
|
||||
if stored_design.get(key) is not None:
|
||||
design[key] = stored_design[key]
|
||||
async with pool.acquire() as connection:
|
||||
# 표시 설정(측점 개별 반폭)은 계산 입력이 아니다 — 저장분에서 이월해 재계산이
|
||||
# 지우지 않게 한다(2026-08-06).
|
||||
stored_designs = await get_cross_section_designs(connection, route_id)
|
||||
for record in stored_designs:
|
||||
if abs(float(record["chainage_m"]) - request.chainage_m) < 0.01:
|
||||
stored_design = record.get("design")
|
||||
if isinstance(stored_design, dict):
|
||||
# 목록을 여기 다시 적지 않는다 — 서버의 한 벌은
|
||||
# `B06_Section_Router_Design.USER_TOUCHED_KEYS` 다(2026-09-07).
|
||||
# 예전에는 여기에 따로 적어 두어 `extra_spans` 가 빠져 있었다.
|
||||
for key in USER_TOUCHED_KEYS:
|
||||
if stored_design.get(key) is not None:
|
||||
design[key] = stored_design[key]
|
||||
break
|
||||
await connection.begin()
|
||||
try:
|
||||
updated = await update_cross_section_design(
|
||||
|
||||
Reference in New Issue
Block a user