- `design_pipe_ids.ts` — 장부(세션 pipe-ids) · pipe-s{차례} · 자리 → id(찾는 거리 안 가장 가까운 것) · followMovedPipe 가 id 를 새 자리로(고름 id 그대로 · 순환 import 없게 design_selection 에서 옮김) · 설계 페이지가 프로젝트를 정함(usePipeIds) · 자리 이름 만들기 열 곳 · 되읽기 둘 걷음 · 측점 계획 주인 = 세션 id(withPipeIds)
- `design_pick_match.ts` — pickLights(① id ② 같은 자리 ③ 고른 부분) · paintPicked(카드 통째 형상 · 부분 · 관 계열 · 포장층은 제 손잡이) · tagShape — 관 세트 벽 · 집수정 · 관 · 세월교 측벽 · 몸체 · BOX 끝 · C군 벽 · 물넘이 콘크리트에 신원표 · 형상 판정면은 통째 형상 · 콘크리트만
- 3D — 부재 맞춤 = pickLights(옛 0.01 걷음) · C군 벽 솔리드 = 구조물 id(덮는 측점 어디서 골라도 한 번 스윕한 벽이 켜짐)
- 322-0 이 깬 것 고침 — 노면공 흐름 도움 파일(165 · 231-1) 정의 판 확인 흉내 · design_view_profile 700줄(322-0 이 704)
- 시험: test_323_5_pick(세션 id · 옮김 · 강조 규칙 · 칠하기) · 옛 시험 도움 파일 흉내 · 기대 글 고침
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
242 lines
9.1 KiB
JavaScript
242 lines
9.1 KiB
JavaScript
/* PLAN 322-3 카드 목록 흐름 헬퍼 — 진짜 `B06_Section_Section_Drafts.ts`(withDraftStations) + 측점 계획 ·
|
|
* 구조물 표 · 관 세트 산식을 그 자리에서 트랜스파일해 Node 로 돌림(서버 · 세션만 흉내) · 결과 = 표준출력 JSON.
|
|
* 흐름 일곱 = 놓기 · 일반 측점에 놓기(219) · 옮기기 · 같은 군 바꿈 · 정본 넘는 바꿈 · 지우기 · 저장 뒤 다시 열기.
|
|
* 짝 = `test_322_3_flow.py`.
|
|
*/
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const ROOT = path.join(__dirname, "..", "..");
|
|
const ts = require(path.join(ROOT, "config", "node_modules", "typescript"));
|
|
const DEF = JSON.parse(
|
|
fs.readFileSync(path.join(ROOT, "resources", "master_template", "구조물정의.json"), "utf8"),
|
|
);
|
|
const TYPES = DEF.types.map((t) => ({ managed_by: null, enabled: true, ...t }));
|
|
|
|
const state = { savedPipes: [], savedStructures: [], draftPipes: null, draftStructures: null };
|
|
const memory = new Map();
|
|
const store = {
|
|
getItem: (k) => (memory.has(k) ? memory.get(k) : null),
|
|
setItem: (k, v) => memory.set(k, String(v)),
|
|
removeItem: (k) => memory.delete(k),
|
|
key: (i) => [...memory.keys()][i] ?? null,
|
|
get length() {
|
|
return memory.size;
|
|
},
|
|
};
|
|
globalThis.window = { sessionStorage: store, localStorage: store, addEventListener: () => {} };
|
|
globalThis.sessionStorage = store;
|
|
globalThis.localStorage = store;
|
|
const file = (...parts) => path.resolve(ROOT, ...parts);
|
|
const STUBS = new Map([
|
|
[
|
|
file("B04_PreProcess", "B04_PreProcess_Api_Fetch.ts"),
|
|
{ fetchDetailPipePoints: async () => ({ pipe_points: state.savedPipes }) },
|
|
],
|
|
[
|
|
file("B05_Profile", "B05_Profile_Api_Structures.ts"),
|
|
{
|
|
fetchStructures: async () => ({ structures: state.savedStructures }),
|
|
fetchStructureTypes: async () => TYPES,
|
|
readPendingStructures: () => state.draftStructures,
|
|
},
|
|
],
|
|
[
|
|
file("B05_Profile", "B05_Profile_Api_Pipes_Draft.ts"),
|
|
{ readPendingPipes: () => state.draftPipes },
|
|
],
|
|
[
|
|
file("B06_Section", "B06_Section_Api_Fetch.ts"),
|
|
{
|
|
previewStructureSections: async (_p, chainages) =>
|
|
chainages.map((at) => ({ chainage_m: at, kind: "irregular", station_id: `p${at}` })),
|
|
},
|
|
],
|
|
]);
|
|
const cache = new Map();
|
|
function load(target) {
|
|
const full = path.resolve(target);
|
|
if (STUBS.has(full)) return STUBS.get(full);
|
|
if (cache.has(full)) return cache.get(full);
|
|
if (full.endsWith(".json")) return JSON.parse(fs.readFileSync(full, "utf8"));
|
|
const js = ts.transpileModule(fs.readFileSync(full, "utf8"), {
|
|
compilerOptions: {
|
|
module: ts.ModuleKind.CommonJS,
|
|
target: ts.ScriptTarget.ES2020,
|
|
esModuleInterop: true,
|
|
},
|
|
}).outputText;
|
|
const box = { exports: {} };
|
|
cache.set(full, box.exports);
|
|
const need = (spec) => {
|
|
if (spec.startsWith("@ui/")) return { showToast: () => {} };
|
|
const alias = spec.startsWith("@util/") ? path.join(ROOT, "common_util", spec.slice(6)) : null;
|
|
const base = alias ?? path.join(path.dirname(full), spec);
|
|
const hit = [`${base}.ts`, base.replace(/\.js$/, ".ts"), base].find(
|
|
(p) =>
|
|
(STUBS.has(path.resolve(p)) || fs.existsSync(p)) &&
|
|
!fs.statSync(p, { throwIfNoEntry: false })?.isDirectory(),
|
|
);
|
|
if (!hit) throw new Error(`모름: ${spec} (${full})`);
|
|
return load(hit);
|
|
};
|
|
new Function("exports", "module", "require", js)(box.exports, box, need);
|
|
return box.exports;
|
|
}
|
|
|
|
load(file("common_util", "common_util_structure_catalog.ts")).useCatalogDefinition(DEF);
|
|
const drafts = load(file("B06_Section", "B06_Section_Section_Drafts.ts"));
|
|
|
|
const grid = () =>
|
|
Array.from({ length: 11 }, (_v, i) => ({
|
|
chainage_m: i * 20,
|
|
kind: "regular",
|
|
station_id: `g${i * 20}`,
|
|
}));
|
|
const irregular = (at, structure, extra = {}) => ({
|
|
chainage_m: at,
|
|
kind: "irregular",
|
|
station_id: `i${at}`,
|
|
structure,
|
|
...extra,
|
|
});
|
|
const wall = (type_id, id = "w1") => ({
|
|
structure_id: id,
|
|
type_id,
|
|
placement: "interval",
|
|
start_m: 93,
|
|
chainage_m: 101,
|
|
end_m: 113,
|
|
options: { height_m: 3 },
|
|
});
|
|
const summary = (detail) =>
|
|
detail.cross_sections.map((s) => [
|
|
s.chainage_m,
|
|
s.structure ?? null,
|
|
!!s.preview,
|
|
["culvert", "ford", "box"].filter((k) => s[k]),
|
|
]);
|
|
|
|
let route = 0;
|
|
async function run(setup, detail) {
|
|
Object.assign(state, {
|
|
savedPipes: [],
|
|
savedStructures: [],
|
|
draftPipes: null,
|
|
draftStructures: null,
|
|
...setup,
|
|
});
|
|
drafts.forgetDraftStations("P");
|
|
route += 1;
|
|
await drafts.withDraftStations(detail, "P", route);
|
|
await drafts.withDraftShapes(detail, "P"); // 그림 자료 = 형상 얹기 한 벌(323-4)
|
|
return summary(detail);
|
|
}
|
|
|
|
(async () => {
|
|
const out = {};
|
|
out.place = await run({ draftStructures: [wall("retaining_wall")] }, { cross_sections: grid() });
|
|
out.gridPlace = await run(
|
|
{
|
|
draftStructures: [
|
|
{
|
|
structure_id: "u1",
|
|
type_id: "cross_underdrain",
|
|
placement: "point",
|
|
chainage_m: 100,
|
|
options: {},
|
|
},
|
|
],
|
|
draftPipes: [{ chainage_m: 120, options: { pipe_diameter_mm: "800" } }],
|
|
},
|
|
{ cross_sections: grid() },
|
|
);
|
|
const pipeSet = { culvert: { type: "pipe" } };
|
|
out.move = await run(
|
|
{
|
|
savedPipes: [{ chainage_m: 61, options: { pipe_diameter_mm: "1000" } }],
|
|
draftPipes: [{ chainage_m: 75, options: { pipe_diameter_mm: "1000" } }],
|
|
},
|
|
{ cross_sections: [...grid(), irregular(61, "파형강관 D1000", pipeSet)] },
|
|
);
|
|
const walls = () => [irregular(93, "옹벽"), irregular(101, "옹벽"), irregular(113, "옹벽")];
|
|
out.sameGroup = await run(
|
|
{ savedStructures: [wall("retaining_wall")], draftStructures: [wall("soil_guard")] },
|
|
{ cross_sections: [...grid(), ...walls()] },
|
|
);
|
|
out.crossOwner = await run(
|
|
{
|
|
savedPipes: [{ chainage_m: 101, options: { pipe_diameter_mm: "1000" } }],
|
|
draftPipes: [],
|
|
draftStructures: [
|
|
{
|
|
structure_id: "u2",
|
|
type_id: "cross_underdrain",
|
|
placement: "point",
|
|
chainage_m: 101,
|
|
options: {},
|
|
},
|
|
],
|
|
},
|
|
{ cross_sections: [...grid(), irregular(101, "파형강관 D1000", pipeSet)] },
|
|
);
|
|
const kept = { cross_sections: [...grid(), ...walls()] };
|
|
out.remove = await run({ savedStructures: [wall("retaining_wall")], draftStructures: [] }, kept);
|
|
// 저장 뒤 다시 열기 — 초안 없음 = 서버 목록 그대로(빼 둔 옛 자리 돌아옴)
|
|
out.reopen = await run({ savedStructures: [wall("retaining_wall")] }, kept);
|
|
// 관을 옮겨도 고름 유지(322-3) — 고른 관이면 새 자리 · id = 관 세션 id 그대로(323-5) · 다른 고름은 그대로
|
|
const sel = load(file("A00_Common", "design", "design_selection.ts"));
|
|
const pipeIds = load(file("A00_Common", "design", "design_pipe_ids.ts"));
|
|
sel.selectDesign("P", { chainageM: 61, structureId: pipeIds.pipeIdAt(61, "P") });
|
|
pipeIds.followMovedPipe("P", 61, 75);
|
|
const moved = sel.readSelection("P");
|
|
sel.selectDesign("P", { chainageM: 101, structureId: "w1" });
|
|
pipeIds.followMovedPipe("P", 101, 120);
|
|
out.follow = [moved, sel.readSelection("P")];
|
|
// 옛 소수 관(검증_sub1 견본) — B06 을 바로 열어도 B05 와 같은 정수 m 초안(`snapPlan` · 요구 167)
|
|
const snap = load(file("B05_Profile", "B05_Profile_UI_Structures_Snap.ts"));
|
|
const DEC = [
|
|
85.05, 173.09, 258.12, 352.14, 440.24, 533.98, 619.71, 720.44, 804.18, 899.91, 982.64,
|
|
];
|
|
const ford = new Set([173.09, 352.14]);
|
|
const decPipes = DEC.map((at) => ({
|
|
chainage_m: at,
|
|
facility: ford.has(at) ? "ford_bridge" : "pipe",
|
|
options: { pipe_diameter_mm: "800" },
|
|
}));
|
|
const plan = snap.snapPlan([], decPipes);
|
|
out.snapTo = [plan.pipes.map((move) => move.to), plan.skipped];
|
|
const snapped = decPipes.map((pipe) => ({
|
|
...pipe,
|
|
chainage_m: plan.pipes.find((move) => move.from === pipe.chainage_m)?.to ?? pipe.chainage_m,
|
|
}));
|
|
const gridTo = (end) =>
|
|
Array.from({ length: end / 20 + 1 }, (_v, i) => ({
|
|
chainage_m: i * 20,
|
|
kind: "regular",
|
|
station_id: `g${i * 20}`,
|
|
}));
|
|
const titleAt = (at) => (ford.has(at) ? "세월교" : "파형강관 D800");
|
|
const own = [85, 173, 258, 352, 534, 804, 983];
|
|
const onGrid = [440, 620, 720, 900];
|
|
// 서버가 정수 자리로 심은 저장분(격자 자리는 구조물 측점 하나 · grid) — 새 미리 뜬 카드 0
|
|
const savedInt = [
|
|
...gridTo(1000).filter((s) => !onGrid.includes(s.chainage_m)),
|
|
...onGrid.map((at) => irregular(at, "파형강관 D800", { grid: true })),
|
|
...own.map((at) => irregular(at, at === 173 || at === 352 ? "세월교" : "파형강관 D800")),
|
|
];
|
|
out.snapSavedInt = await run(
|
|
{ savedPipes: decPipes, draftPipes: snapped, draftStructures: [] },
|
|
{ cross_sections: savedInt },
|
|
);
|
|
// 소수 자리로 심긴 저장분(검증_sub1 지금) = B05 를 먼저 연 것과 같음 — 소수 카드는 빼 두고 정수 자리 미리 뜸
|
|
const savedDec = [...gridTo(1000), ...DEC.map((at) => irregular(at, titleAt(at)))];
|
|
out.snapSavedDec = await run(
|
|
{ savedPipes: decPipes, draftPipes: snapped },
|
|
{ cross_sections: savedDec },
|
|
);
|
|
process.stdout.write(JSON.stringify(out));
|
|
})().catch((error) => {
|
|
process.stderr.write(String(error?.stack ?? error));
|
|
process.exit(1);
|
|
});
|