diff --git a/B05_Profile/B05_Profile_UI_RouteEdit_Apply.ts b/B05_Profile/B05_Profile_UI_RouteEdit_Apply.ts index d8795ef57..7f4c985d5 100644 --- a/B05_Profile/B05_Profile_UI_RouteEdit_Apply.ts +++ b/B05_Profile/B05_Profile_UI_RouteEdit_Apply.ts @@ -27,6 +27,8 @@ import { useStationKeys, } from "../B06_Section/B06_Section_Station_Keys"; import { withCarriedEdits, withDraftAdjusts } from "../B06_Section/B06_Section_Section_Adjusts"; +import { fetchDetailPipePoints } from "../B04_PreProcess/B04_PreProcess_Api_Fetch"; +import { pipeChainageOf } from "../A00_Common/design/design_pipe_ids"; import { clearRouteLatestCache } from "./B05_Profile_Api_Fetch"; import { replanRoute, @@ -151,9 +153,19 @@ export async function carryAfterRouteSave( invalidateSectionDetail(projectId, toRouteId); const detail = await loadSectionDetail(projectId, toRouteId); useStationKeys(projectId, toRouteId, detail); + // 관 열쇠 → 새 자리 — 세션 id(`pipe-s3`) = 관 장부 · 저장 관 id(`p8017`) = 서버가 옮긴 관 + const saved = await fetchDetailPipePoints(projectId) + .then((res) => res.pipe_points) + .catch(() => []); + const pipeAt = (key: string): number | null => { + const id = key.startsWith("pipe:") ? key.slice(5) : null; + if (!id) return null; + if (id.startsWith("pipe-")) return pipeChainageOf(id, projectId); + return saved.find((pipe) => pipe.id === id)?.chainage_m ?? null; + }; const outside = carryStationDrafts(projectId, fromRouteId, toRouteId, { quiet: true }) + - carryChainageStates(projectId, fromRouteId, toRouteId); + carryChainageStates(projectId, fromRouteId, toRouteId, pipeAt); // 옮긴 고침을 새 상세 설계 칸에 — 같은 [저장]의 횡단 패치가 실음(사면 쉬프트 · 조절 · 소단 깊이 · 417) withDraftAdjusts(detail, projectId, toRouteId); withCarriedEdits(detail, projectId, toRouteId); diff --git a/B06_Section/B06_Section_Station_Keys.ts b/B06_Section/B06_Section_Station_Keys.ts index 5c43e45bb..9b22107bc 100644 --- a/B06_Section/B06_Section_Station_Keys.ts +++ b/B06_Section/B06_Section_Station_Keys.ts @@ -387,20 +387,28 @@ const CHAINAGE_HEAD = /^(b?)(-?\d+(?:\.\d+)?)(:.*)?$/; * 노선 저장 뒤(새 노선 측점 열쇠를 세운 뒤) 누가거리 열쇠 칸(`ROUTE_CARRY_STATES` 중 측점 열쇠 칸 빼고)을 옛 노선 * → 새 노선 측점으로 옮김 — 옛 누가거리 → 옛 측점 열쇠 → 새 누가거리. 구조물 조절은 관 · 구조물 열쇠만 · * 못 옮긴 고침은 버림. 돌려주는 값 = 버린 측점 수(「노선 밖 고침」 · 측점 단위). + * `facilityAt` = 관 · 구조물 열쇠 → 새 노선 누가거리(관 id 규칙이 갈림 — 세션 `pipe-s3` · 계산 줄 `pipe:p8017`). */ export function carryChainageStates( projectId: string, fromRouteId: number, toRouteId: number, + facilityAt: (key: string) => number | null | undefined = () => null, ): number { const outside = new Set(); const moveAt = (state: string, text: string): string | null => { const stable = keyAt(projectId, fromRouteId, Number(text)); const structural = stable.startsWith("pipe:") || stable.startsWith("structure:"); + // 새 목록에 그 열쇠가 없으면 그 시설의 새 자리 · 없으면 같은 자리 측점(초안 측점 = 저장 측점) const toAt = stable.startsWith("at:") || (STRUCTURE_STATES.has(state) && !structural) ? null - : chainageOfKey(projectId, toRouteId, stable); + : chainageOfKey( + projectId, + toRouteId, + stable, + (structural ? facilityAt(stable) : null) ?? Number(text), + ); if (toAt === null) outside.add(stable); return toAt === null ? null : chainageKey(toAt); }; diff --git a/resources/tester/helper_346_3_route_save.cjs b/resources/tester/helper_346_3_route_save.cjs index 49939ffff..5ae056f50 100644 --- a/resources/tester/helper_346_3_route_save.cjs +++ b/resources/tester/helper_346_3_route_save.cjs @@ -54,6 +54,10 @@ const STUBS = { withDraftAdjusts: (_d, _p, routeId) => calls.push(`adjusts:${routeId}`), withCarriedEdits: (_d, _p, routeId) => calls.push(`edits:${routeId}`), }, + "../B04_PreProcess/B04_PreProcess_Api_Fetch": { + fetchDetailPipePoints: async () => (calls.push("pipes"), { pipe_points: [] }), + }, + "../A00_Common/design/design_pipe_ids": { pipeChainageOf: () => null }, "./B05_Profile_Api_Fetch": { clearRouteLatestCache: () => calls.push("latest:clear") }, "./B05_Profile_Api_Replan": { replanRoute: async (_p, body) => { diff --git a/resources/tester/helper_417_entry.ts b/resources/tester/helper_417_entry.ts index d74ce26ea..080b74230 100644 --- a/resources/tester/helper_417_entry.ts +++ b/resources/tester/helper_417_entry.ts @@ -4,6 +4,7 @@ export { carryChainageStates, carryStationDrafts, + useStationKeyList, useStationKeys, } from "../../B06_Section/B06_Section_Station_Keys"; export { diff --git a/resources/tester/helper_417_route_carry.mjs b/resources/tester/helper_417_route_carry.mjs index 1a641a29a..b22080dc6 100644 --- a/resources/tester/helper_417_route_carry.mjs +++ b/resources/tester/helper_417_route_carry.mjs @@ -123,4 +123,28 @@ const pick = (entry) => out.input40 = pick(source?.input.byKey["grid:2"]); out.input20 = pick(source?.input.byKey["grid:1"]); +/* 5. 관 id 규칙이 갈림(옛 목록 세션 id · 새 목록 저장 관 id) — 시설 자리 풀이로 옮김 · 없으면 버림 */ +const R3 = 9; +const R4 = 10; +const list = [ + { key: "end:BP", chainageM: 0 }, + { key: "grid:1", chainageM: 20 }, + { key: "grid:2", chainageM: 40 }, + { key: "pipe:p9", chainageM: 52 }, + { key: "end:EP", chainageM: 60 }, +]; +m.useStationKeyList(P, R3, list); +m.useStationKeyList(P, R4, list); +m.writeState("pipe-ids", { next: 2, ids: [{ id: "pipe-s1", at: 45 }] }, P); +m.useStationKeys( + P, + R, + detailOf([section(0, "bp"), section(45, "irregular", pipe), section(60, "ep")]), +); +m.writeState("revetx", { "45.00:outlet": wall }, P, R); +m.carryChainageStates(P, R, R3, (key) => (key === "pipe:pipe-s1" ? 52 : null)); +m.carryChainageStates(P, R, R4); +out.splitResolved = raw("revetx", R3); +out.splitDropped = raw("revetx", R4); + process.stdout.write(JSON.stringify(out)); diff --git a/resources/tester/test_346_3_route_save_screen.py b/resources/tester/test_346_3_route_save_screen.py index 34f529b81..edf2a86c1 100644 --- a/resources/tester/test_346_3_route_save_screen.py +++ b/resources/tester/test_346_3_route_save_screen.py @@ -79,6 +79,7 @@ def test_초안_넘김_차례(out): "invalidate:9", "load:9", "keys:9:9", + "pipes", # 관 열쇠 → 새 자리(서버가 옮긴 관) "carry:7->9:quiet", "carryAt:7->9", # 417 — 조절 · 측점 폭 · 상세 표도 새 노선 측점으로 "adjusts:9", diff --git a/resources/tester/test_417_route_carry.py b/resources/tester/test_417_route_carry.py index 18abc4fda..9062fbcb1 100644 --- a/resources/tester/test_417_route_carry.py +++ b/resources/tester/test_417_route_carry.py @@ -98,6 +98,11 @@ def test_조절은_관_따라_옮김_일반_측점_옹벽은_버림(out): assert out["chainageOutside"] == 1 # 40 m 옹벽 조절(일반 측점) +def test_관_id_규칙이_갈려도_시설_자리로_옮김(out): + assert out["splitResolved"] == {"52.00:outlet": {"x": 0.3, "d": None, "h": 1.5, "m": None}} + assert out["splitDropped"] == {} # 풀이 없음 = 같은 자리 측점 없음 → 버림 + + def test_새_상세_설계_칸에_얹힘(out): d40, d47 = out["design40"], out["design47"] assert d40["shoulder_extend_left_m"] == 0.5 and d40["berm_depth_right_m"] == 1.2