refactor(511-B B-4 기본 설계 셋 Node · preview 걷음): run_calc = 늘 떠 있는 Node(못 띄우면 새 프로세스) · 기본 설계 셋(체인 · 노선 저장 이월 · [측점 만들기] · GET 빈 측점 · B05 확정 측구 방향 역반영) = 측점 묶음 Node cross.design 한 번 · 포장 · 기울임 · 세월교 하강 = TS facilityRanges(cross.design facilities · Node 못 부르면 파이썬 구간 짝) · /cross-design/preview 걷음(창구 · 스키마 · recompute_designs_for_alignment · 화면 refreshFromServer) · crossOptions = logic/B06_Section_Cross_Save.ts(다음 단계 [저장] 횡단 한 바퀴 짝 · 아직 안 부름) · 시험 266 · 475_3 · masshaul_single_source 기대 맞춤 — 요구 320-4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 14:18:09 +09:00
co-authored by Claude Opus 5.5
parent f6abe881a2
commit f40ff98819
14 changed files with 496 additions and 543 deletions
+20 -52
View File
@@ -12,7 +12,8 @@
* excavation?: { pipes: [{ diameter_mm }] · walls: [{ height_m · average_thickness_m ·
* has_foundation }] } ·
* cross_design?: { standard: 표준 횡단(config 위에 저장분 덮은 한 벌) ·
* stations: [{ samples · design_elevation_m · options: 파이썬 `compute_cross_design` 키 }] } ·
* stations: [{ samples · design_elevation_m · options: 파이썬 `compute_cross_design` 키 ·
* chainage_m? }] · facilities?: { pipes · structures }(시설 구간 · 511-B B-4) } ·
* berm?: { structures: 구조물 정본 목록 · chainages: [측점 m] } ·
* rebase? · carry?(노선 바뀐 [저장] · `design_calc_node_carry.ts`) ·
* stages?(묶음 단계 · `design_calc_node_stages.ts` · 511-B) } }
@@ -25,9 +26,9 @@
import { readFileSync, writeFileSync } from "node:fs";
import { bermSpansFromStructures, bermSpecAt } from "@util/common_util_berm_spans";
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 type { CulvertPipePoint } from "@util/common_util_culvert_sets";
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";
@@ -36,6 +37,12 @@ import type { RoutePoint } from "../../../B05_Profile/B05_Profile_Api_Fetch";
import { buildCorridor } from "../../../B05_Profile/B05_Profile_UI_Corridor_Build";
import { corridorHash, serialize } from "../../../B05_Profile/B05_Profile_UI_Corridor_Envelope";
import type { SectionDetailResponse } from "../../../B06_Section/B06_Section_Api_Fetch";
import {
facilityAt,
facilityRanges,
type FacilityStructure,
} from "../../../B06_Section/logic/B06_Section_Cross_Enforce";
import { crossOptions } from "../../../B06_Section/logic/B06_Section_Cross_Save";
import {
applyStructureAreaRows,
extraWallRows,
@@ -83,59 +90,14 @@ interface CalcInputs extends FollowInputs, CarryInputs, TrialInputs, StagesInput
samples: CrossGroundSample[];
design_elevation_m: number | null;
options: Record<string, unknown>;
/** 시설 구간을 얹을 누가거리(`facilities` 와 함께 · 511-B B-4) */
chainage_m?: number;
}>;
/** 관 지점 · 구조물 정본 → 측점마다 포장 · 기울임 · 세월교 하강(`facilityRanges` · C3) */
facilities?: { pipes: CulvertPipePoint[]; structures: FacilityStructure[] };
};
}
/** 파이썬 `compute_cross_design` 키 → TS 짝 키(거울 시험 test_b06_cross_design_mirror 표와 같음). */
const CROSS_OPTION_KEYS: Record<string, keyof CrossDesignOptions> = {
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<string, number | null>;
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<string, unknown>,
standard: CrossDesignOptions["standard"],
): CrossDesignOptions {
const options: Record<string, unknown> = { 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` 로 · 단계 id 는 약속(기다림). */
async function runDesignCalc(run: readonly string[], inputs: CalcInputs) {
const memo = new Map<string, unknown>();
@@ -205,9 +167,15 @@ async function runDesignCalc(run: readonly string[], inputs: CalcInputs) {
// 측점마다 TS 짝(`computeCrossDesign`) — 못 세우면 그 자리 { error }(파이썬은 ValueError 로 되던짐).
"cross.design": () => {
const batch = inputs.cross_design;
const ranges = batch?.facilities
? facilityRanges(batch.facilities.pipes, batch.facilities.structures)
: null;
return (batch?.stations ?? []).map((station) => {
try {
const options = crossOptions(station.options, batch!.standard);
const options = {
...crossOptions(station.options, batch!.standard),
...(ranges && station.chainage_m != null ? facilityAt(ranges, station.chainage_m) : {}),
};
return computeCrossDesign(station.samples, station.design_elevation_m, options);
} catch (error) {
return { error: error instanceof Error ? error.message : String(error) };