Files
Aislo/resources/tester/helper_322_3_flow.cjs
T
eomsangdonandClaude Opus 5.5 172ff8170b refactor(511-B B-6 정리): 운영 부름 0 파이썬 짝 걷음 · 남는 짝 머리에 남는 까닭 · 등록부 until 걷음 · 알려진 차 까닭 · 322_3 flow 빨강 고침
걷은 함수(운영 부름 0 · 전수 확인 = 화면 · 서버 · 체인 · 추천 엔진 · B07 · 시험)
- common_util/common_util_excavation.py 통째(126 줄 · pipe_trench_width_m · wall_trench_area_m2 · wall_trench_width_m · 서버 = Node excavation.trench)
- Engine_Grade_Carry.carry_edits(Node carry_seat 겉) · Route_Save_Parts.seat_route_moved(시험 헬퍼 helper_475_1_moved 로 옮김)
- Engine_Tilt.attach_underdrains · Engine_Structures_Wall.attach_wall_structures · load_wall_structures
- Engine_Grade_Alignment.tangent_elevation(부름 0) · station_key.dedupe_station_keys · loose_chainage
줄 수 전 → 후: excavation.py 126 → 0 · Grade_Carry 178 → 157 · Save_Parts 289 → 269 · Wall 221 → 197 · Tilt 115 → 112 · station_key 141 → 122 · Alignment 657 → 654

남는 짝(머리 「남는 까닭」 한 줄) — Grade · Grade_Profile · Alignment · CurveLaw(2~4 순위 폴백 · 추천) · NodeEdits · SampleExtend · Router_Design · Ranges · Engine_Design · Areas · cross_berm(DTM 아닌 지표면 · Node 못 부를 때 · B07 · 추천) · Tilt · Wall · Culvert(B04 B07 B08) · route_follow · structure_station · station_key · route_polyline(745 줄 · 까닭 = 등록부 R1 줄)

등록부 — until 칸 걷음(G1 = py 표본기 남김 · 까닭 note) · C6 pair = attach_underdrain_specs · F8 pair null
알려진 차 — golden KNOWN 셋 그대로 나타남(R7 표본 출처 · W0 저장 때 경고 · X2 입력 선 자릿수 · 까닭 고침) · underdrain 알려진 차 까닭 · 511_b5 KNOWN_ARC 그대로
계산 자리 창 — 45 줄 · 서버만 아닌 41(바꿀 수 있음 40 + F9) · 서버만 넷(G3 D1 D2 N1 줄마다 까닭) · 옛 까닭 「짝 모으는 중 · B-4」 글 걷음
test_322_3_flow 빨강 — 헬퍼가 @config/config_frontend 흉내(511-A design_calc_server import)
시험 기준 — 걷은 짝 거울 = 걷기 전 파이썬 값 고정(excavation · station_key) · Node carry_seat · attach_underdrain_specs · wall_specs_from

검증: 고친 대상 시험 97 파일 562 통과 · 21 건너뜀 · typecheck 0 · 700 줄 넘는 새 파일 없음 — 요구 320-4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:34:08 +09:00

262 lines
10 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: () => {} };
if (spec === "@config/config_frontend") return { API_BASE_URL: "/api" };
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);
swapIn(detail);
await drafts.withDraftStations(detail, "P", route); // 갈아끼움 뒤 다시 그림 = 다시 맞춤
await drafts.withDraftShapes(detail, "P"); // 그림 자료 = 형상 얹기 한 벌(323-4)
return summary(detail);
}
/** 415 M4 — 새 자리 = 계산 묶음 측점 갈아끼움 흉내(측점 계획 자리 중 상세에 없는 것 · 기본 설계 · 서버 안 물음). */
function swapIn(detail) {
const plan = load(file("common_util", "common_util_structure_station.ts"));
const { registryDefaults } = load(file("common_util", "common_util_culvert_sets.ts"));
const planned = plan.planStations(
state.draftPipes ?? state.savedPipes,
state.draftStructures ?? state.savedStructures,
TYPES,
registryDefaults(TYPES),
);
const near = (a, b) => Math.abs(a - b) < plan.SAME_STATION_M;
for (const { chainage_m: at } of planned)
if (detail.cross_sections.every((s) => !near(s.chainage_m, at)))
detail.cross_sections.push({ chainage_m: at, kind: "irregular", station_id: `p${at}` });
detail.cross_sections.sort((a, b) => a.chainage_m - b.chainage_m);
}
(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);
});