/* 411-B · 411-D 헬퍼 — vite ssr 로 묶은 진짜 모듈(helper_411_b_entry.ts)을 부르고 결과를 JSON 한 벌로. * node helper_411_b_pipe_id_pairing.mjs <묶음.mjs> * 사용자 길: 관 p2 를 앞 관 p1 을 지나 옮김(150 → 80) · 노선 따라감이 두 관을 함께 밂. */ import { pathToFileURL } from "node:url"; const store = () => { const m = new Map(); return { getItem: (k) => (m.has(k) ? m.get(k) : null), setItem: (k, v) => m.set(k, String(v)), removeItem: (k) => m.delete(k), key: (i) => [...m.keys()][i] ?? null, get length() { return m.size; }, clear: () => m.clear(), }; }; globalThis.window = globalThis; globalThis.sessionStorage = store(); globalThis.localStorage = store(); const any = new Proxy(function () {}, { get: (_t, k) => (k === Symbol.toPrimitive ? () => 0 : any), apply: () => any, construct: () => any, }); globalThis.document = any; globalThis.requestAnimationFrame = (run) => setTimeout(run, 0); const m = await import(pathToFileURL(process.argv[2]).href); const out = {}; const P = "p411"; m.usePipeIds(P); /* 1. 알림 목록 ↔ 가게 관 초안 짝 = id — 옮겨 차례가 뒤바뀌어도 그 관 */ const reports = [ { id: "p10000", chainage_m: 100, facility: "pipe" }, { id: "p15000", chainage_m: 150, facility: "pipe", start_m: 145, end_m: 155 }, ]; const draft = [ { id: "p15000", chainage_m: 80 }, { id: "p10000", chainage_m: 100 }, ]; out.reports = m .followPipeReports(reports, draft) .map((r) => [r.id, r.chainage_m, r.start_m ?? null]); /* 2. 세션 id 장부 — 끌어 옮김(followMovedPipe 길) · 그 사이 옛 측점 열쇠가 옛 자리에 헛 id 를 지음 → * 화면 목록 맞춤(shownPipes = 관 고정 id 가 쥔 세션 id 를 새 자리로 · placePipeIds) */ const idA = m.pipeIdAt(100, P); const idB = m.pipeIdAt(150, P); const sessionIds = new Map([ ["p10000", idA], ["p15000", idB], ]); m.movePipeId(150, 80, P); // 끌기 = 옮긴 관 id 그대로 const stray = m.pipeIdAt(150, P); // 옛 측점 열쇠가 옛 자리를 물음 const place = (shown, project) => m.placePipeIds( shown.map((r) => [sessionIds.get(r.id), r.chainage_m]), project, ); place(m.followPipeReports(reports, draft), P); out.ids = { idA, idB, stray, at80: m.pipeIdAt(80, P), at100: m.pipeIdAt(100, P) }; /* 2-1. 되돌림 — 관이 옛 자리로 돌아감 · 그 자리에 헛 id 가 먼저 있어도 그 관 id(헛 기록은 뺌) */ place( [ { id: "p15000", chainage_m: 150 }, { id: "p10000", chainage_m: 100 }, ], P, ); out.back = { at150: m.pipeIdAt(150, P), strayAt: m.pipeChainageOf(stray, P) }; /* 3. 노선 따라감이 두 관을 함께 밂(앞 관이 뒤 관 옛 자리로) — 기록 안 잃음 · 헛 id 안 생김 */ const Q = "q411"; const q1 = m.pipeIdAt(100, Q); const q2 = m.pipeIdAt(103, Q); m.placePipeIds( [ [q1, 103], [q2, 106], ], Q, ); out.batch = { q1, q2, at103: m.pipeIdAt(103, Q), at106: m.pipeIdAt(106, Q), next: m.pipeIdAt(500, Q), }; /* 4. 알약 따라감 — 알약이 이미 따라간 자리면 같은 자리 짝(차례 뒤바뀜에도 그 관) */ const marks = [ { structure_id: idB, chainage_m: 80 }, { structure_id: idA, chainage_m: 100 }, { structure_id: "st-1", chainage_m: 40 }, ]; out.marks = m .followedMarks(marks, [{ structure_id: "st-1", chainage_m: 42 }], draft) .map((mk) => [mk.structure_id, mk.chainage_m]); /* 5. 배수 편집기 — 따라간 관 자리(id 짝)를 받음 · 시설 속성 · 고름도 따라감 */ const editor = m.createPipeEditor(() => {}); editor.setPipes([ { chainage_m: 100, reason: "stream", id: "p10000" }, { chainage_m: 150, reason: "confirmed", id: "p15000" }, ]); editor.select(1); const facilities = m.createFacilityStore(); facilities.set(150, { facility: "ford", options: { 월류높이: 0.3 } }); m.followEditorPipes(editor, facilities, [ { id: "p10000", chainage_m: 103 }, { id: "p15000", chainage_m: 98 }, ]); out.editor = { pipes: editor.pipes().map((p) => [p.id, p.chainage_m, p.reason]), selected: editor.pipes()[editor.selected()]?.id ?? null, ford98: facilities.get(98)?.facility ?? null, at150: facilities.get(150), }; m.followEditorPipes(editor, facilities, [{ id: "p10000", chainage_m: 103 }]); // 같은 값 = 그대로 out.editorSame = editor.pipes().map((p) => p.chainage_m); /* 5-1. 막 놓은 관 — 초안엔 id 없음 · 편집기는 서버 id(재계산 응답) → id 없는 것끼리 차례 짝 */ const fresh = m.createPipeEditor(() => {}); fresh.setPipes([ { chainage_m: 120, reason: "confirmed", id: "p12000" }, { chainage_m: 351, reason: "stream", id: "p35100" }, ]); m.followEditorPipes(fresh, m.createFacilityStore(), [ { chainage_m: 120 }, { id: "p35100", chainage_m: 353 }, ]); const fresh2 = m.createPipeEditor(() => {}); fresh2.setPipes([ { chainage_m: 200, reason: "confirmed", id: "p20000" }, { chainage_m: 351, reason: "stream", id: "p35100" }, ]); m.followEditorPipes(fresh2, m.createFacilityStore(), [ { chainage_m: 204 }, { id: "p35100", chainage_m: 353 }, ]); out.fresh = [fresh.pipes(), fresh2.pipes()].map((list) => list.map((p) => [p.id, p.chainage_m])); /* 6. B06 측점 열쇠 — 서버 관 id 도 관(계산 묶음 `pipe:` 와 같음) · 맨 앞 · 맨 끝 = 시종점(452)이라 BP · EP 사이에 둠 */ const keysOf = (project, list) => m .stationKeysOf( project, [{ chainage_m: 0, kind: "bp" }, ...list, { chainage_m: 400, kind: "ep" }], 20, ) .slice(1, -1); const planned = [ { chainage_m: 80.17, owners: ["p8017"], role: "anchor" }, { chainage_m: 52.5, owners: ["st-1"], role: "anchor" }, ]; const sections = [ { chainage_m: 52.5, kind: "irregular" }, { chainage_m: 80.17, kind: "irregular" }, ]; m.useStationOwners(P, planned, [{ id: "p8017", chainage_m: 80.17 }]); out.keys = keysOf(P, sections).map((k) => k.key); m.useStationOwners(P, planned); out.keysNoPipes = keysOf(P, sections).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 = keysOf(E, staleSections).map((k) => k.key); out.staleBook = bookSize(); m.useStationOwners(E, []); // 관 계획 모름(초안 없음) = 옛 갈래 그대로 지음 out.legacyKey = keysOf(E, [staleSections[1]])[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));