Files
Aislo/resources/tester/helper_472_1_browser.mjs
T
eomsangdonandClaude Opus 5.5 c0af1c0180 test(472-1 기준값): B05 · B06 고정 자료 하나로 길 넷 기준값 시험 — 구조 다시 세움 단계마다의 문
- 고정 자료 fixtures/golden_472(검증_랩탑메인 노선 370 · GET · storage 사본 · 2.98 MB)
- 길 넷: 정본(GET) · [저장] 서버 재계산 · 노선 저장 체인(DB 없이) · 브라우저 진짜 단계 묶음(vite ssr)
- 값 = 측점 · 열쇠 · x · y · 계획고 · 면적 · 층따기 · 누가토량 · 관 길이 · 종단 경고 · 길마다 0 차
- 두 길 사이 = 알려진 차(R7 · W0 · X2 · W1)만 · 안 나타난 알려진 차도 실패
- 값 다시 뜨기 = helper_472_1_dump.py(손으로만)

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

155 lines
5.6 KiB
JavaScript

/* 472-1 헬퍼 — 브라우저 길 기준값(진짜 단계 함수 · vite ssr 묶음 · helper_472_1_browser_entry.ts).
* node helper_472_1_browser.mjs <묶음.mjs> <고정 자료 폴더> <격자 .bin>
* 페이지 묶음(`design_pipeline_page`)과 같은 단계 붙임 — 저장 노선 · 초안 없음 · 세션 = 메모리 가짜 · fetch 없음 ·
* 격자 = 서버 `dtm_grid_bytes` 가 고정 npz 로 지은 이진. 묶음 출력 → 새 노선 상세(`draftSectionDetail`) → 구조물
* 면적 얹기 → 누가토량. 표준출력 JSON 한 벌(값 꼴 = test_472_1_golden.py `values_of`). */
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { pathToFileURL } from "node:url";
const storage = () => {
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 = storage();
globalThis.localStorage = storage();
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);
globalThis.fetch = () => Promise.reject(new Error("서버 없음"));
const [bundle, dir, gridPath] = process.argv.slice(2);
const m = await import(pathToFileURL(bundle).href);
const read = (name) => JSON.parse(readFileSync(join(dir, name), "utf8"));
const detail = read("detail.json");
const plan = read("plan.json");
const context = read("context.json");
const pipes = read("pipes.json").pipe_points.map(({ lonlat: _l, ...pipe }) => pipe);
const structures = read("structures.json").structures;
const types = read("structure_types.json").types;
const plus = read("plus_stations.json").chainages;
const P = context.project_id;
const R = context.route_id;
const bin = readFileSync(gridPath);
const grid = m.parseDtmGrid(bin.buffer.slice(bin.byteOffset, bin.byteOffset + bin.byteLength));
// 페이지가 열 때 세션에 두는 것 — 표준 · 암 경계 기본 · 저장 상세(design_view_profile · 상세 가게)
localStorage.setItem("frd_current_project_id", P);
m.writeState("std-cross-default", context.standard_cross_section, P);
m.writeStateRaw("rock-boundary-default", String(context.rock_boundary_default_offset_m), P);
m.writeState("section-detail", detail, P, R);
const interval = m.SECTION_STATION_INTERVAL_M;
const enabled = types.filter((type) => type.enabled !== false);
const savedLine = () => plan.planned.map(([x, y]) => [x, y]);
const savedCurves = () =>
(plan.curves ?? []).map(({ start, apex, end, radius_m }) => ({ start, apex, end, radius_m }));
const savedStations = m.buildStations(savedLine(), savedCurves(), pipes, structures, {
intervalM: interval,
types: enabled,
stationSnap: plan.station_snap ?? false,
stationSnapStep: plan.station_snap_step,
});
const crossSource = m.pipelineCrossSource(P, detail, savedStations, R);
const edits = m.readAlignment(detail.longitudinal).edits;
const status = m.shapeStatus(pipes, structures, types);
const bermAt = (list) => {
const spans = m.bermSpansFromStructures(Array.isArray(list) ? list : []);
return (chainageM) => m.bermSpecAt(spans, chainageM);
};
const crossStage = m.createCrossStage({ grid: () => ({ grid }) });
const pipe = m.createDesignPipeline({
stages: {
route: m.createRouteStage({
minRadiusM: () => plan.min_radius_m ?? 0,
savedLine: () => ({ line: savedLine(), curves: savedCurves() }),
}),
follow: m.createFollowStage({
savedLine,
saved: () => ({ pipes, structures, alignment: edits }),
}),
stations: m.createStationsStage({
intervalM: interval,
types: () => enabled,
saved: () => plan,
}),
ground: m.createGroundStage({
grid: () => ({ grid, design: "las" }),
remote: () => Promise.reject(new Error("서버 없음")),
}),
planline: m.createPlanlineStage({
savedBase: (at) => m.plantedBase({ base: null, detail }, at, types),
savedLine,
}),
profile: m.profileStage,
cross: (ctx) =>
crossStage.stage({
...ctx,
inputs: {
...ctx.inputs,
cross: m.crossWithContext(
ctx.inputs.cross,
ctx.out.ground?.stations ?? [],
m.shapeStatus(ctx.inputs.pipes, ctx.inputs.structures, types),
bermAt(ctx.inputs.structures),
),
},
}),
footprint: m.footprintStage,
},
});
const record = await pipe.start({
route: null,
alignment: edits,
cross: crossSource?.input ?? null,
pipes,
structures,
plusStations: plus,
});
await pipe.settled();
if (record.outcome !== "done") throw new Error(`묶음 ${record.outcome}`);
const ground = pipe.output("ground");
const profile = pipe.output("profile");
const shown = m.draftSectionDetail(
detail,
ground,
profile,
pipe.output("cross"),
crossSource?.specs ?? new Map(),
interval,
status,
);
m.applyStructureAreaRows(shown.cross_sections, m.structureAreaRows(shown.cross_sections));
const haul = m.computeMassHaul(
shown.cross_sections,
context.earthwork_conversion,
context.natural_spoil_min_ground_slope ?? undefined,
);
process.stdout.write(
JSON.stringify({
keys: ground.stations.map((station) => station.key),
sections: shown.cross_sections,
mass_haul: haul,
warnings: profile.warnings ?? [],
violations: profile.violations ?? [],
footprint_records: pipe.output("footprint")?.records?.length ?? 0,
skipped: record.skipped,
}),
);
process.exit(0);