From 4752cd223fcf3ea9e86c4f8295ddcd78641af7ad Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 19 Oct 2026 09:08:37 +0900 Subject: [PATCH] =?UTF-8?q?feat(472-4=20=E2=91=A1=20Node=20=EC=9E=85?= =?UTF-8?q?=EA=B5=AC):=20cross.design=20=EC=8B=A4=ED=96=89=EA=B8=B0=20?= =?UTF-8?q?=E2=80=94=20TS=20computeCrossDesign=20=EC=9D=84=20=EC=B8=A1?= =?UTF-8?q?=EC=A0=90=20=EB=AC=B6=EC=9D=8C=EC=9C=BC=EB=A1=9C(=ED=8C=8C?= =?UTF-8?q?=EC=9D=B4=EC=8D=AC=20=ED=82=A4=20=EB=B0=9B=EC=9D=8C=20=C2=B7=20?= =?UTF-8?q?=EB=AA=BB=20=EC=84=B8=EC=9A=B4=20=EC=9E=90=EB=A6=AC=20error)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 아직 부르는 곳 없음 — 다음 커밋에서 [저장] · [확정] 재계산이 씀 - 고정 자료 견줌: [저장] 59 회 · 노선 체인 179 회 — 면적 · 계획고 · 노면 끝 0 차 · 설계선 점 13 · 34 곳 1e-4 m(반올림 동점) Co-Authored-By: Claude Opus 5.5 --- A00_Common/design/logic/design_calc_node.ts | 76 ++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/A00_Common/design/logic/design_calc_node.ts b/A00_Common/design/logic/design_calc_node.ts index 1708bcc3f..130e0546d 100644 --- a/A00_Common/design/logic/design_calc_node.ts +++ b/A00_Common/design/logic/design_calc_node.ts @@ -9,7 +9,9 @@ * context?: { earthwork_conversion · natural_spoil_min_ground_slope · * haul_equipment_limits · 채집석 · 구조물 잔토(B08) } · * excavation?: { pipes: [{ diameter_mm }] · walls: [{ height_m · average_thickness_m · - * has_foundation }] } } } + * has_foundation }] } · + * cross_design?: { standard: 표준 횡단(config 위에 저장분 덮은 한 벌) · + * stations: [{ samples · design_elevation_m · options: 파이썬 `compute_cross_design` 키 }] } } } * 출력 { results: { [id]: 값 }, available: [id…] } * — 차례는 요청 차례가 아니라 기댐 차례(누가토량 앞에 구조물 면적을 상세에 얹음 · * server-calc 묶음과 같은 차례). @@ -17,6 +19,9 @@ * ========================================================================== */ import { readFileSync, writeFileSync } from "node:fs"; +import type { BermSpec } from "@util/common_util_cross_berm"; +import { computeCrossDesign } from "@util/common_util_cross_design"; +import type { CrossDesignOptions, CrossGroundSample } from "@util/common_util_cross_design"; import { pipeTrenchWidthM, wallTrenchAreaM2, wallTrenchWidthM } from "@util/common_util_excavation"; import { computeMassHaul, massHaulPayload } from "@util/common_util_mass_haul"; import type { MassHaulResult } from "@util/common_util_mass_haul"; @@ -58,6 +63,63 @@ interface CalcInputs { has_foundation?: boolean; }>; }; + cross_design?: { + standard: CrossDesignOptions["standard"]; + stations?: Array<{ + samples: CrossGroundSample[]; + design_elevation_m: number | null; + options: Record; + }>; + }; +} + +/** 파이썬 `compute_cross_design` 키 → TS 짝 키(거울 시험 test_b06_cross_design_mirror 표와 같음). */ +const CROSS_OPTION_KEYS: Record = { + ground_type: "groundType", + section_mode: "sectionMode", + ditch_side: "ditchSide", + ditch_type: "ditchType", + paved: "paved", + rock_boundary_offset_m: "rockBoundaryOffsetM", + two_stage_slope: "twoStageSlope", + cut_slope_ratio: "cutSlopeRatio", + ditch_enabled: "ditchEnabled", + ditch_choice: "ditchChoice", + surface_drop_m: "surfaceDropM", + plan_radius_m: "planRadiusM", + curve_outer_side: "curveOuterSide", + curve_widening_m: "curveWideningM", + shoulder_extend_left_m: "shoulderExtendLeftM", + shoulder_extend_right_m: "shoulderExtendRightM", + tilted: "tilted", + tilt_pct: "tiltPct", +}; + +/** 파이썬 BermSpec(NamedTuple → dict) → TS BermSpec. */ +function bermOf(value: unknown): BermSpec | null { + if (!value || typeof value !== "object") return null; + const berm = value as Record; + return { + widthM: berm.width_m as number, + intervalM: berm.interval_m as number, + slopeDeg: berm.slope_deg as number, + depthM: berm.depth_m as number, + depthLeftM: berm.depth_left_m ?? null, + depthRightM: berm.depth_right_m ?? null, + }; +} + +function crossOptions( + raw: Record, + standard: CrossDesignOptions["standard"], +): CrossDesignOptions { + const options: Record = { standard }; + for (const [key, value] of Object.entries(raw)) { + if (key === "berm") options.berm = bermOf(value); + else if (key in CROSS_OPTION_KEYS) options[CROSS_OPTION_KEYS[key]] = value; + else throw new Error(`모르는 횡단 설계 키: ${key}`); + } + return options as unknown as CrossDesignOptions; } /** 한 번 돌림 — 같은 id 는 한 번만 셈(memo) · 앞 id 는 `need` 로. */ @@ -117,6 +179,18 @@ function runDesignCalc(run: readonly string[], inputs: CalcInputs) { ), })), }), + // 측점마다 TS 짝(`computeCrossDesign`) — 못 세우면 그 자리 { error }(파이썬은 ValueError 로 되던짐). + "cross.design": () => { + const batch = inputs.cross_design; + return (batch?.stations ?? []).map((station) => { + try { + const options = crossOptions(station.options, batch!.standard); + return computeCrossDesign(station.samples, station.design_elevation_m, options); + } catch (error) { + return { error: error instanceof Error ? error.message : String(error) }; + } + }); + }, // 편집 전 정본 — 계획선 샘플은 저장분 그대로(corridor 묶음과 같은 값) · 측점 둘 미만이면 null. "corridor.mesh": () => { const detail = inputs.detail;