fix(B06 측점 열쇠): 옛 자리 관 측점 헛 세션 id 걷음 · 헛 id 정리 (411-E)

- 관 계획을 아는 동안(초안 · 관 주인 있음) 계획에 없는 관 같은 측점 = 옮겨 간 옛 자리 — pipeIdAt 대신 pipeIdNear(안 지음) · 장부에 없으면 at: 열쇠
- 초안 없음 = 옛 갈래 그대로(지음)
- 화면 관 목록 맞춤 때 지금 관 자리 근처에 없는 기록 정리(prunePipeIds)
- 시험 test_411_b 덧붙임(헛 id 0 · 옛 갈래 · 정리)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
This commit is contained in:
2026-10-18 09:59:12 +09:00
co-authored by Claude Opus 5.5
parent ddc3250c15
commit 274a0f892e
6 changed files with 57 additions and 5 deletions
+14
View File
@@ -46,6 +46,20 @@ export function pipeIdAt(chainageM: number, projectId: string | null = active):
return id;
}
/** 그 자리 관의 id — 장부에 있을 때만(안 지음 · 411-E). */
export function pipeIdNear(chainageM: number, projectId: string | null = active): string | null {
if (!projectId) return null;
return nearestWithin(bookOf(projectId).ids, chainageM, (entry) => entry.at)?.id ?? null;
}
/** 지금 관 자리(`keep`) 근처에 없는 기록을 뺌 — 옮긴 사이 옛 측점이 지은 헛 id 정리(411-E). */
export function prunePipeIds(keep: ReadonlyArray<number>, projectId: string | null = active): void {
if (!projectId || !keep.length) return; // 빈 목록 = 아직 못 받음 · 다 지움 — 장부 그대로
const book = bookOf(projectId);
const ids = book.ids.filter((entry) => keep.some((at) => isNear(entry.at, at)));
if (ids.length !== book.ids.length) writeState("pipe-ids", { next: book.next, ids }, projectId);
}
/** id 의 관 자리 — 장부 · 옛 자리 이름 순 · 관 id 가 아니면 null. */
export function pipeChainageOf(id: string, projectId: string | null = active): number | null {
if (!id.startsWith("pipe-")) return null;
+5 -1
View File
@@ -66,7 +66,7 @@ import "../../B05_Profile/B05_Profile_UI_Style.css";
import "../../B05_Profile/B05_Profile_UI_Style_Table.css";
import "../../B05_Profile/B05_Profile_UI_Style_Drainage.css";
import "../../B05_Profile/B05_Profile_UI_Style_Structures.css";
import { pipeIdAt, placePipeIds } from "./design_pipe_ids";
import { pipeIdAt, placePipeIds, prunePipeIds } from "./design_pipe_ids";
import { useDesignStore } from "./design_store";
import { setPlusStations } from "../../common_util/common_util_structure_station";
import {
@@ -132,6 +132,10 @@ export async function mountProfileView(
pipe.id ? [[pipe.id, pipeIdAt(pipe.chainage_m, activeProjectId)] as const] : [],
),
);
prunePipeIds(
shown.map((pipe) => pipe.chainage_m),
activeProjectId,
); // 헛 id 정리(411-E)
return shown;
};
const applyPipes = (reported: PipeReport, carryEdits = true): void => {
+7 -4
View File
@@ -16,7 +16,7 @@
* ========================================================================== */
import { readState, writeState } from "../A00_Common/b_page_state";
import { pipeIdAt } from "../A00_Common/design/design_pipe_ids";
import { pipeIdAt, pipeIdNear } from "../A00_Common/design/design_pipe_ids";
import { showToast } from "@ui/ui_template_elements";
import {
chainageKey,
@@ -116,15 +116,18 @@ export function stationKeysOf(
intervalM: number | null,
): Keyed[] {
const known = projectId ? (owners.get(projectId) ?? []) : [];
const planned = known.some((owner) => owner.kind === "pipe");
const keys = sections.map((section) => {
const at = section.chainage_m;
let key: string;
if (section.kind === "irregular") {
const owner = known.find((entry) => Math.abs(entry.at - at) < NEAR_M);
if (owner) key = stationKey("irregular", at, intervalM, owner.id, owner.kind, owner.role);
else if (looksLikePipe(section))
key = stationKey("pipe", at, intervalM, pipeIdAt(at, projectId));
else
else if (looksLikePipe(section)) {
// 관 계획을 아는 동안(초안) 계획에 없는 관 측점 = 옮겨 간 옛 자리 — 세션 id 를 새로 안 지음(411-E)
const id = planned ? pipeIdNear(at, projectId) : pipeIdAt(at, projectId);
key = id ? stationKey("pipe", at, intervalM, id) : stationKey("irregular", at, intervalM);
} else
key = section.grid
? stationKey("regular", at, intervalM)
: stationKey("irregular", at, intervalM);
+2
View File
@@ -5,7 +5,9 @@ export { followPipeReports } from "../../B05_Profile/logic/B05_Profile_Pipeline_
export { followedMarks } from "../../B05_Profile/logic/B05_Profile_Structure_Follow";
export {
movePipeId,
pipeIdNear,
placePipeIds,
prunePipeIds,
pipeChainageOf,
pipeIdAt,
usePipeIds,
@@ -161,4 +161,23 @@ m.useStationOwners(P, planned);
out.keysNoPipes = m.stationKeysOf(P, sections, 20).map((k) => k.key);
out.pipelineKey = m.pipelineStationKey("pipe", "p8017");
/* 7. 411-E — 관 계획을 아는 동안 옛 자리 관 측점(옮겨 간 자리)은 세션 id 를 안 지음 · 헛 id 정리 */
const E = "e411";
m.usePipeIds(E);
const e1 = m.pipeIdAt(80, E); // 옮긴 관(새 자리)
const bookSize = () => JSON.parse(sessionStorage.getItem(`aislo:draft:pipe-ids:${E}`)).ids.length;
m.useStationOwners(E, [{ chainage_m: 80, owners: ["p15000"], role: "anchor" }], [{ id: "p15000" }]);
const staleSections = [
{ chainage_m: 80, kind: "irregular", structure: "파형강관 D800", culvert: {} },
{ chainage_m: 150, kind: "irregular", structure: "파형강관 D800", culvert: {} }, // 옛 자리
];
out.staleKeys = m.stationKeysOf(E, staleSections, 20).map((k) => k.key);
out.staleBook = bookSize();
m.useStationOwners(E, []); // 관 계획 모름(초안 없음) = 옛 갈래 그대로 지음
out.legacyKey = m.stationKeysOf(E, [staleSections[1]], 20)[0].key;
out.legacyBook = bookSize();
m.prunePipeIds([80], E); // 지금 관 자리만 남김
out.pruned = { size: bookSize(), at80: m.pipeIdAt(80, E), e1 };
m.usePipeIds(P);
process.stdout.write(JSON.stringify(out));
@@ -113,3 +113,13 @@ def test_B06_열쇠는_서버_관_id_도_pipe(out):
assert out["keys"] == ["structure:st-1", "pipe:p8017"]
assert out["keys"][1] == out["pipelineKey"] # 계산 묶음 측점 열쇠와 같음
assert out["keysNoPipes"][1] == "structure:p8017" # 관 목록을 안 주면 옛 갈래(대조)
def test_411_E_옛_자리_관_측점은_헛_id_안_지음(out):
assert out["staleKeys"] == ["pipe:p15000", "at:150.00"]
assert out["staleBook"] == 1 # 세션 id 표에 헛 id 0
assert out["legacyKey"] == "pipe:pipe-s2" and out["legacyBook"] == 2 # 초안 없음 = 옛 갈래
def test_411_E_헛_id_정리(out):
assert out["pruned"] == {"size": 1, "at80": out["pruned"]["e1"], "e1": "pipe-s1"}