Merge remote-tracking branch 'origin/main_laptop_1' into sub_laptop_1

This commit is contained in:
2026-09-07 02:06:54 +09:00
5 changed files with 60 additions and 6 deletions
@@ -205,6 +205,12 @@ async def run_auto_design_chain(
if not expected_path.is_file():
write_route_csv(expected_path, points)
# 2.6) 계획노선 **초기 폴리라인**도 여기서 세운다(2026-09-06 PLAN 0-10).
# 예상노선은 점 묶음이라 그대로는 설계선이 못 된다 — 지식DB 의 R 기준으로
# 곡선을 끼운 폴리라인이 불변 초기 데이터다. 화면이 처음 열릴 때 만들면
# 「화면을 안 열면 값이 없다」가 되므로 초기값은 서버가 낸다(0-4 원칙).
await _ensure_initial_polyline(project_id, project_root, points)
# 3) B05 경로 계산
request = RouteSolveRequest(
filter_key=str(defaults["source_filter"]),
@@ -327,6 +333,30 @@ async def run_auto_design_chain(
clear_designing(project_root)
async def _ensure_initial_polyline(
project_id: UUID, project_root: Path, points: list[dict[str, float]]
) -> None:
"""계획노선 초기 폴리라인을 세운다 — 이미 있으면 그대로 둔다. 실패는 비치명적."""
import asyncio
from B05_Profile.B05_Profile_Router_Replan import _ensure_planned_initial, _min_plan_radius_m
try:
radius_m = await _min_plan_radius_m(project_id)
summary = await asyncio.to_thread(_ensure_planned_initial, project_root, radius_m)
if summary:
logger.info(
"초기 계획노선 폴리라인: project_id=%s 노드 %d · 곡선 %d · 위반 %d (R %.1fm)",
project_id,
summary["nodes"],
summary["curves"],
summary["violations"],
radius_m,
)
except Exception: # noqa: BLE001 — 없으면 화면이 처음 열릴 때 만든다
logger.exception("초기 계획노선 폴리라인 생성 실패(계속 진행): %s", project_id)
async def run_redesign_chain(
project_id: UUID,
surface_model_id: int,
+10 -3
View File
@@ -175,11 +175,18 @@ def _write_planned_polyline(path: Path, points: list[tuple[float, float]], radiu
def _ensure_planned_initial(project_root: Path, radius_m: float) -> dict | None:
"""계획노선 **초기 폴리라인**이 없으면 예상노선을 폴리라인화해 세운다."""
"""계획노선 **초기 폴리라인**이 없거나 낡았으면 예상노선을 폴리라인화해 세운다.
「낡았다」 = 예상노선 파일이 더 나중에 쓰였다. 파일을 다시 올리면 예상노선이 새로
깔리는데 초기본이 옛 노선인 채로 남으면 노선 초기화가 옛 자리로 돌아간다.
"""
target = planned_route_initial_path(project_root)
source = expected_route_csv_path(project_root)
if target.is_file():
return None
points = [(x, y) for x, y in _vertices_of(expected_route_csv_path(project_root))]
if not source.is_file() or source.stat().st_mtime <= target.stat().st_mtime:
return None
logger.info("예상노선이 새로 깔려 초기 폴리라인을 다시 만듭니다: %s", target)
points = [(x, y) for x, y in _vertices_of(source)]
if len(points) < 2:
return None
summary = _write_planned_polyline(target, points, radius_m)
+10 -2
View File
@@ -224,8 +224,16 @@ function markDirty(projectId: string, routeId: number): void {
export async function saveCorridorIfDirty(projectId: string, routeId: number): Promise<void> {
const entry = cache.get(keyOf(projectId, routeId));
if (!entry || !entry.dirty) return;
const ok = await putStored(projectId, routeId, serialize(entry.build, entry.hash));
if (ok) entry.dirty = false;
const envelope = serialize(entry.build, entry.hash);
const ok = await putStored(projectId, routeId, envelope);
if (!ok) return;
entry.dirty = false;
// 방금 올린 것을 **보관함에도** 담는다(2026-09-06) — 그러지 않으면 다음 진입이 열쇠가
// 바뀐 주소로 한 번 더 받아 온다. 담아 두면 네트워크 없이 선다.
const url = `${corridorUrlPrefix(projectId, routeId)}?hash=${encodeURIComponent(entry.hash)}`;
const bytes = new TextEncoder().encode(JSON.stringify(envelope)).buffer as ArrayBuffer;
await purgeAssetsWithPrefix(projectId, corridorUrlPrefix(projectId, routeId));
await writeCachedBytes(projectId, url, bytes);
}
/** Page 훅 — 현재 종횡단 정본 그대로 코리도를 확보해 뷰어에 반영(실패 시 제거).
+4
View File
@@ -322,6 +322,10 @@ export async function renderB05Route(root: HTMLElement): Promise<void> {
);
// 측점 바·라벨·램프도 계획고를 따라 움직여야 한다(2026-08-23 지적 ③).
renderStationLines(currentSectionDetail);
// 만든 것을 **바로 올려 둔다**(2026-09-06) — 예전에는 B05→B06 이동 때만 올려서,
// 누른 뒤 새로고침하면 저장본이 낡은 채라 다음 사람이 또 만들어야 했다.
// 브라우저 보관함이 생긴 지금은 올려 두면 다음 진입이 네트워크 없이 선다.
void saveCorridorIfDirty(activeProjectId, latest.route.id);
},
onMovePoint: viewer.beginMoveSelected,
onDeletePoint: viewer.markers.deleteSelected,
@@ -186,8 +186,13 @@ export function structurePanelDeps(ctx: StructurePanelContext): StructurePanelDe
const role = spanRoleOf(key, ctx.inletIsBasin());
if (role) ctx.structureSpan?.update(ctx.section, role, patch);
},
// 벽을 **그리지 않는 카드에는 [연동]을 내지 않는다**(2026-09-06). 링크 카드는 소유
// 측점의 벽을 빌려 그리는데, 빌릴 벽이 없는 카드에도 버튼이 떠 눌러도 아무 일이
// 없었다 — `revetlink` 에 `detached` 만 쌓였다(보조 창 실측: 0·20·40·60·80m 카드).
linkState: () =>
ctx.isLinked ? { linked: ctx.revetLink?.linkedFor(ctx.section) ?? true } : null,
ctx.isLinked && ctx.drawnWallKeys().length
? { linked: ctx.revetLink?.linkedFor(ctx.section) ?? true }
: null,
setLinked: (linked) => {
// 연동을 **푸는 순간** 지금 그려진 높이를 이 측점 값으로 굳힌다(2026-09-06 사용자
// 지시). 예전에는 위치(4축)만 갈리고 높이는 소유 측점 값을 계속 따라가, 소유