배분·운반거리 산식(_balance 523 + _settle 235 = 758줄)을 브라우저 번들에서 빼기 위한
배선. 계산은 여전히 한 벌 — 서버가 같은 TS 를 Node 로 돈다(CLAUDE.md 5장).
- POST /projects/{id}/sections/{route_id}/haul-plan 신설
(B06_Section_Router_HaulPlan.py). 브라우저가 낸 누가토량 결과를 받아 배분만 돌려줌.
화면이 쓰는 꼴 그대로 내보내 그리기 코드를 안 건드림. 표시 전용 — 정본은 저장 때 따로.
- B05_Profile_Api_HaulPlan.ts 신설 — 편집이 멈추면(400ms) 조용히 받아 두는 선반입기.
늦게 온 응답은 버림(최신 요청만 채택). 못 받아도 곡선 자체는 그대로 보임.
- common_util_mass_haul.massHaulPayload 가 배분을 값으로 물던 것을 끊음(extra 인자).
이 커플링 때문에 화면에서 안 불러도 번들에 남았음.
- 저장 경로는 곡선도 배분도 안 만듦 — 서버가 정본을 내므로 balloon 위치만 보냄.
- 죽은 파일 B06_Section_UI_Section_View_MassHaul.ts(236줄) 삭제.
자체검증(공용 브라우저, 용화 route 149) — 엔드포인트 200 · 196ms.
서버 배분과 브라우저 computeHaulPlan 결과가 JSON 문자열까지 동일(blocks 1 · steps 2 ·
spoil/borrow/hauled/transferred/fill_total 전부 일치). 시험 400 통과·17 건너뜀.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
82 lines
4.3 KiB
TypeScript
82 lines
4.3 KiB
TypeScript
/* =============================================================================
|
|
* B06_Section_Server_Calc_Node.ts
|
|
* 브라우저에서만 돌던 횡단 계산을 **서버가 한 번 돌리는** 진입점 — 구조물 폐회로
|
|
* 절·성토 면적 + 그것을 쌓아 만든 유토곡선.
|
|
*
|
|
* 왜 있나(2026-09-06, CLAUDE.md 5장 「계산 자리」) — 이 두 값은 지금까지 브라우저에서만
|
|
* 나왔다. 사용자가 B06 을 한 번도 안 열면 값이 없고, 저장·확정 뒤 서버가 다시 계산하면
|
|
* 구조물을 모르는 표준값으로 되돌아갔다. 코리도(`B05_Profile_Corridor_Node.ts`)와 같은
|
|
* 방식으로 **브라우저가 쓰는 코드를 서버가 그대로 실행**한다 — 계산을 두 벌로 짜지 않는다.
|
|
*
|
|
* 순서가 중요하다: 면적 보정을 **먼저** 얹고 그 위에서 유토곡선을 쌓는다. 화면도 같은
|
|
* 순서다(카드를 그리며 면적을 고친 뒤 유토곡선을 낸다).
|
|
*
|
|
* 실행: node <번들> <입력.json> <출력.json>
|
|
* 입력 { detail: 종횡단 상세(API와 같은 꼴), context: { earthwork_conversion,
|
|
* natural_spoil_min_ground_slope, haul_equipment_limits } }
|
|
* 출력 { areas: [{ chainage_m, cut_area_m2, … }], mass_haul: {…} | null }
|
|
* — areas 는 **구조물 트림이 있는 측점만**. 나머지는 표준 계산값이 이미 맞다.
|
|
* 끝 코드: 0 성공 / 2 인자 오류
|
|
* ========================================================================== */
|
|
|
|
import { readFileSync, writeFileSync } from "node:fs";
|
|
import { computeMassHaul, massHaulPayload } from "@util/common_util_mass_haul";
|
|
import { computeHaulPlan, haulPlanPayload } from "@util/common_util_mass_haul_balance";
|
|
import type { CrossSection, SectionDetailResponse } from "./B06_Section_Api_Fetch";
|
|
import { applyStructureAreaRows, structureAreaRows } from "./B06_Section_Structure_Layouts";
|
|
|
|
interface ServerCalcInput {
|
|
detail?: SectionDetailResponse;
|
|
/**
|
|
* 유토 **배분만** 낼 때 쓰는 입력 — 브라우저가 누가토량(`computeMassHaul`)까지 내고
|
|
* 그 결과를 보내면 여기서 배분·운반거리만 얹어 돌려준다(2026-09-06).
|
|
* 배분 코드를 브라우저 번들에서 빼기 위한 길이라, 이 갈래는 `detail` 을 안 받는다.
|
|
*/
|
|
haul_plan_for?: Parameters<typeof computeHaulPlan>[0];
|
|
context?: {
|
|
earthwork_conversion?: Parameters<typeof computeMassHaul>[1];
|
|
natural_spoil_min_ground_slope?: number | null;
|
|
haul_equipment_limits?: Parameters<typeof computeHaulPlan>[1];
|
|
};
|
|
}
|
|
|
|
const [inputPath, outputPath] = process.argv.slice(2);
|
|
if (!inputPath || !outputPath) {
|
|
console.error("사용법: node <번들> <입력.json> <출력.json>");
|
|
process.exit(2);
|
|
}
|
|
|
|
const input = JSON.parse(readFileSync(inputPath, "utf8")) as ServerCalcInput;
|
|
|
|
// 배분만 내는 갈래 — 화면이 편집을 멈추면 조용히 물어보는 자리(유토곡선 배경 선반입).
|
|
if (input.haul_plan_for) {
|
|
// **화면이 쓰는 꼴 그대로** 내보낸다(직렬화 형태 `haulPlanPayload` 가 아니다) — 그래야
|
|
// 그리기 코드가 손대지 않고 그대로 받는다. 전부 숫자·문자열이라 JSON 으로 오간다.
|
|
const plan = computeHaulPlan(input.haul_plan_for, input.context?.haul_equipment_limits);
|
|
writeFileSync(outputPath, JSON.stringify({ haul_plan: plan ?? null }));
|
|
process.exit(0);
|
|
}
|
|
|
|
const sections: CrossSection[] = input.detail?.cross_sections ?? [];
|
|
|
|
const areas = structureAreaRows(sections);
|
|
// 보정값을 **자리에서** 얹는다 — 유토곡선이 고쳐진 면적 위에서 쌓이게 한다.
|
|
applyStructureAreaRows(sections, areas);
|
|
|
|
// 유토곡선 — balloon 위치는 사용자 화면값이라 서버가 만들지 않는다(파이썬이 보존).
|
|
const conversion = input.context?.earthwork_conversion;
|
|
const result = conversion
|
|
? computeMassHaul(
|
|
sections,
|
|
conversion,
|
|
input.context?.natural_spoil_min_ground_slope ?? undefined,
|
|
)
|
|
: null;
|
|
// 배분은 **서버만** 만든다 — 그래야 그 코드가 브라우저 번들에서 빠진다(2026-09-06).
|
|
const plan = result ? computeHaulPlan(result, input.context?.haul_equipment_limits) : null;
|
|
const massHaul = result
|
|
? massHaulPayload(result, plan ? { haul_plan: haulPlanPayload(plan) } : null)
|
|
: null;
|
|
|
|
writeFileSync(outputPath, JSON.stringify({ areas, mass_haul: massHaul }));
|