Files
Aislo/B06_Section/B06_Section_Section_Canon.ts
T
eomsangdonandClaude Opus 5.5 77be773c07 refactor(472-5 5-B 값 주인 하나): 횡단 상세 객체 = 상세 가게 하나 — 계산 묶음 페이지는 지문(노선 · 표본)만
- 상세 가게 칸 셋(B06_Section_Section_Canon.ts · 가벼운 자리): 저장 정본 상세(받을 때 · 세션 · 판 바뀜 · 되돌릴 때 · 갈아끼움 제외) · 초안 전 hold 상세(판 바뀌면 새 판) · 노선 초안 상세(= pipelineDetail)
- 페이지: canon.detail · held.detail · shown 사본 걷음 — 저장 기준 셈(savedEdits · storedPvi · crossNow)은 가게 칸을 읽음 · 초안 버림 = 가게 hold 상세로 정본 잡음
- 떠남(forgetSectionMemory) · invalidate 때 칸 셋 비움 · savedStations 는 sub2 관 · 구조물 줄로 셈 → 남김
- 시험: test_472_5b_detail_owner(옛 코드 빨강 3) · 묶음 헬퍼 넷(365 · 346_2 · 349_0j · 451_drop)이 종단도 흉내 정본을 가게 칸에도 채움
- 위키 design_page 값 · 규칙 · 층 줄

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 10:07:30 +09:00

144 lines
6.2 KiB
TypeScript

/* =============================================================================
* B06_Section_Section_Canon.ts
* 세션 상세(`section-detail`) = **저장 정본만**(387-7) — 서버에서 받은 그대로 한 벌 · 갈아끼움 · 되돌림 ·
* 다시 셈 결과는 안 씀(메모리 캐시 몫). 세션 글은 **한 번만** 풂 — 상세 가게(`_Section_Store`)와 계산 묶음
* cross 입력(`design_pipeline_cross`)이 이 파일을 함께 봄(예전에는 둘이 따로 1.5 MB 를 풂).
* 가벼운 자리 — 상세 가게의 화면 줄기를 안 끌고 묶음 입력이 부름.
* ========================================================================== */
import { readState } from "../A00_Common/b_page_state";
import type { CrossSection, SectionDetailResponse } from "./B06_Section_Api_Fetch";
/** 정본 측점 설계 — 받을 때 뜬 사본(다시 셈 · 초안 얹기가 고치는 캐시 객체와 따로) · 자리 · 표본(제자리로 돌아온
* 측점이 정본 표본을 다시 씀 · 467 · 배열은 받은 것 그대로 · 다시 셈은 새 배열로 갈아 끼움). */
export type CanonRow = { chainage_m: number; design: CrossSection["design"] } & Partial<
Pick<
CrossSection,
| "center_x"
| "center_y"
| "center_z"
| "frame"
| "samples"
| "azimuth_deg"
| "plan_radius_m"
| "curve_outer_side"
| "curve_widening_m"
| "uphill_side"
>
>;
const canon = new Map<string, CanonRow[]>();
/** 묶음이 먼저 푼 세션 상세 — 상세 가게가 처음 읽을 때 가져감(다시 안 풂). */
const parsed = new Map<string, SectionDetailResponse>();
const keyOf = (projectId: string, routeId: number): string => `${projectId}:${routeId}`;
/** 서버에서 받은(또는 세션에서 푼) 정본의 설계 사본을 떠 둠. */
export function keepCanon(projectId: string, routeId: number, detail: SectionDetailResponse): void {
const key = keyOf(projectId, routeId);
parsed.delete(key);
canon.set(
key,
detail.cross_sections.map((section) => ({
chainage_m: section.chainage_m,
design: section.design ? { ...section.design } : section.design,
center_x: section.center_x,
center_y: section.center_y,
center_z: section.center_z,
frame: section.frame,
samples: section.samples,
azimuth_deg: section.azimuth_deg,
plan_radius_m: section.plan_radius_m,
curve_outer_side: section.curve_outer_side,
curve_widening_m: section.curve_widening_m,
uphill_side: section.uphill_side,
})),
);
}
/** 새로고침 — 세션 상세(묶음이 먼저 풀었으면 그 객체) · 정본 사본도 뜸. */
export function sessionDetail(projectId: string, routeId: number): SectionDetailResponse | null {
const key = keyOf(projectId, routeId);
const ready = parsed.get(key);
if (ready) {
parsed.delete(key);
return ready;
}
const stored = readState<SectionDetailResponse>("section-detail", projectId, routeId);
if (stored) keepCanon(projectId, routeId, stored);
return stored;
}
/** 정본 측점 설계(묶음 cross 입력 · 365-5) — 아직 안 읽었으면 세션에서 한 번(상세 가게가 그 객체를 이어 씀). */
export function savedDesigns(projectId: string, routeId: number): CanonRow[] | null {
const key = keyOf(projectId, routeId);
if (!canon.has(key)) {
const stored = readState<SectionDetailResponse>("section-detail", projectId, routeId);
if (!stored) return null;
keepCanon(projectId, routeId, stored);
parsed.set(key, stored);
}
return canon.get(key) ?? null;
}
/** 정본이 바뀜(새로 받을 것) — 사본을 버림. 노선을 안 주면 그 프로젝트 전부. */
export function forgetCanon(projectId: string, routeId?: number): void {
for (const map of [canon, parsed])
for (const key of [...map.keys()])
if (
routeId === undefined ? key.startsWith(`${projectId}:`) : key === keyOf(projectId, routeId)
)
map.delete(key);
}
/* ── 상세 가게 칸 셋(472-5 5-B) — 상세 객체의 주인은 상세 가게 하나 · 계산 묶음 페이지는 지문(노선 · 표본)만 쥠 ──
* 저장 정본 상세 = 가게가 받거나(새로 받음 · 세션) 되돌린 객체(측점 갈아끼움 제외 — 그것은 정본 아님)
* 초안 전 상세 = 노선 초안을 시작할 때 떠 둔 객체(버리면 이것으로 · 판이 바뀌면 새 판)
* 노선 초안 상세 = 계산 묶음이 새 노선으로 지은 객체(초안 동안 두 보기가 그림) */
const savedDetails = new Map<string, SectionDetailResponse>();
const heldDetails = new Map<string, SectionDetailResponse>();
const draftDetails = new Map<string, SectionDetailResponse>();
const put = (
map: Map<string, SectionDetailResponse>,
key: string,
detail: SectionDetailResponse | null,
) => void (detail ? map.set(key, detail) : map.delete(key));
export const keepSavedDetail = (
projectId: string,
routeId: number,
detail: SectionDetailResponse | null,
): void => put(savedDetails, keyOf(projectId, routeId), detail);
export const savedSectionDetail = (
projectId: string,
routeId: number,
): SectionDetailResponse | null => savedDetails.get(keyOf(projectId, routeId)) ?? null;
export const keepHeldDetail = (
projectId: string,
routeId: number,
detail: SectionDetailResponse | null,
): void => put(heldDetails, keyOf(projectId, routeId), detail);
export const heldSectionDetail = (
projectId: string,
routeId: number,
): SectionDetailResponse | null => heldDetails.get(keyOf(projectId, routeId)) ?? null;
export const setDraftSectionDetail = (
projectId: string,
detail: SectionDetailResponse | null,
): void => put(draftDetails, projectId, detail);
export const draftDetailOf = (projectId: string): SectionDetailResponse | null =>
draftDetails.get(projectId) ?? null;
/** 메모리 상세를 버림(페이지 떠남 · 정본 바뀜) — 칸 셋. 노선을 안 주면 그 프로젝트 전부. */
export function forgetDetailSlots(projectId: string, routeId?: number): void {
for (const map of [savedDetails, heldDetails])
for (const key of [...map.keys()])
if (
routeId === undefined ? key.startsWith(`${projectId}:`) : key === keyOf(projectId, routeId)
)
map.delete(key);
if (routeId === undefined) draftDetails.delete(projectId);
}