146 lines
6.4 KiB
TypeScript
146 lines
6.4 KiB
TypeScript
/* =============================================================================
|
|
* B05_Profile_UI_Drainage_Facility_Ford.ts
|
|
* 물넘이포장·세월교 칸 묶음 — 월류 폭·높이·바닥 경사·포장 두께·길이·련 수 + 개략 단면 줄.
|
|
*
|
|
* 서브폼 본체(`_Drainage_Facility.ts`)가 700줄에 닿아 떼어 냄(2026-09-14).
|
|
* 월류 폭 기본값(세월교 10m · 물넘이 5m · 등록부)은 시설이 생길 때 칸에 실제 값으로 들어옴
|
|
* (PLAN 46-7 · 제안값 단추 없음). 빈 칸 안내 = 엔진이 비운 칸을 읽는 값(폭 기본 · 필요 수심).
|
|
* 적은 높이가 필요 수심보다 낮으면 되돌림(계산값 미만은 안 받음).
|
|
* ========================================================================== */
|
|
|
|
import {
|
|
FORD_BRIDGE_DEFAULT_WIDTH_M,
|
|
FORD_PAVEMENT_DEFAULT_WIDTH_M,
|
|
} from "@config/config_frontend";
|
|
import {
|
|
fordSection,
|
|
grid,
|
|
labeled,
|
|
numberInput,
|
|
putNumber,
|
|
stepper,
|
|
} from "./B05_Profile_UI_Drainage_Facility_Fields";
|
|
|
|
type FordKind = "ford_pavement" | "ford_bridge";
|
|
|
|
export interface FordFields {
|
|
widthRow: HTMLElement;
|
|
slopeRow: HTMLElement;
|
|
countRow: HTMLElement;
|
|
summary: HTMLElement;
|
|
/** 바뀌면 저장 흐름을 울릴 칸(높이는 자기 검사 뒤 스스로 울림). */
|
|
inputs: HTMLInputElement[];
|
|
setDesignFlow: (designFlow: number | null) => void;
|
|
sync: () => void;
|
|
write: (kind: FordKind, options: Record<string, string | number>) => void;
|
|
read: (kind: FordKind, target: Record<string, string | number>) => void;
|
|
}
|
|
|
|
export function createFordFields(emit: () => void): FordFields {
|
|
// 세월교 — 구체 내 배관 수량(련). 련은 정수라 소수 자릿수를 두지 않는다.
|
|
const count = numberInput("1", "1", "련");
|
|
const countRow = grid(labeled("수량 (련)", stepper(count, 1, 0), "pipe_count"));
|
|
const width = numberInput("0.1");
|
|
const height = numberInput("0.01");
|
|
const widthRow = grid(
|
|
labeled("월류 폭 (m)", stepper(width, 0.1), "ford_width_m"),
|
|
labeled("월류 높이 (m)", stepper(height, 0.1), "ford_height_m"),
|
|
);
|
|
// 물넘이 바닥은 유입(상류)이 높고 유출이 낮게 기운다(2026-08-28 사용자 확정).
|
|
// 비우면 그 측점의 **노면 횡단경사**를 그대로 쓴다 — 횡단도가 판단한다.
|
|
const slope = numberInput("0.1");
|
|
slope.placeholder = "노면 기울기";
|
|
// 포장 두께·노폭 방향 길이 — 수량(㎡ = 월류 폭 × 길이 · 두께로 원단위 고름)이 읽는 칸(A3).
|
|
const thickness = numberInput("1");
|
|
const length = numberInput("0.1");
|
|
const slopeRow = grid(
|
|
labeled("바닥 경사 유입→유출 (%)", stepper(slope, 0.1), "ford_slope_pct"),
|
|
labeled("포장 두께 (㎝)", stepper(thickness, 1, 0), "thickness_cm"),
|
|
labeled("포장 길이 노폭 방향 (m)", stepper(length, 0.1), "length_m"),
|
|
);
|
|
const summary = document.createElement("p");
|
|
summary.className = "b05-drainage__facility-note";
|
|
let designFlowM3s: number | null = null;
|
|
/** 현재 조건(설계유량·월류 폭)의 필요 최소 수심(m). 계산 불가면 null. */
|
|
let minDepthM: number | null = null;
|
|
|
|
/** 폭을 비우면 엔진이 쓰는 폭(`B06_Section_Engine_Culvert.FORD_*_DEFAULT_WIDTH_M` 짝). */
|
|
const emptyWidth = (kind: FordKind): number =>
|
|
kind === "ford_bridge" ? FORD_BRIDGE_DEFAULT_WIDTH_M : FORD_PAVEMENT_DEFAULT_WIDTH_M;
|
|
|
|
function sync(): void {
|
|
const widthM = Number.parseFloat(width.value || width.dataset.empty || "");
|
|
const section = designFlowM3s !== null ? fordSection(designFlowM3s, widthM) : null;
|
|
minDepthM = section ? Number(section.depthM.toFixed(2)) : null;
|
|
height.placeholder = minDepthM !== null ? `필요 수심 ${minDepthM.toFixed(2)} 이상` : "";
|
|
if (designFlowM3s === null) {
|
|
summary.textContent =
|
|
"담당 유역의 설계유량이 아직 없습니다 — [유역 분석] 후 개략 단면이 나옵니다.";
|
|
return;
|
|
}
|
|
const head = `설계유량 ${designFlowM3s.toFixed(3)} ㎥/s`;
|
|
if (!section) {
|
|
summary.textContent = `${head} · 월류 폭을 넣으면 필요 수심·단면을 계산합니다.`;
|
|
return;
|
|
}
|
|
// 필요 최소 수심이다 — 월류 높이는 이 값 아래로 내릴 수 없다.
|
|
summary.textContent =
|
|
`${head} · 필요 수심 ${section.depthM.toFixed(2)} m · ` +
|
|
`필요 단면 ${section.areaM2.toFixed(2)} ㎡ · 유속 ${section.velocityMs.toFixed(2)} m/s — ` +
|
|
`월류 높이는 필요 수심 이상만 입력됩니다.`;
|
|
}
|
|
|
|
// 적은 값이 계산값 미만이면 받지 않는다 — 계산값으로 되돌리고 잠깐 붉힌다(빈 칸은 그대로 빈 칸).
|
|
height.addEventListener("change", () => {
|
|
const value = Number.parseFloat(height.value);
|
|
if (
|
|
minDepthM !== null &&
|
|
height.value !== "" &&
|
|
(!Number.isFinite(value) || value < minDepthM)
|
|
) {
|
|
height.value = minDepthM.toFixed(2);
|
|
height.classList.add("is-invalid");
|
|
window.setTimeout(() => height.classList.remove("is-invalid"), 900);
|
|
}
|
|
emit();
|
|
});
|
|
|
|
return {
|
|
widthRow,
|
|
slopeRow,
|
|
countRow,
|
|
summary,
|
|
inputs: [count, width],
|
|
setDesignFlow(designFlow) {
|
|
designFlowM3s = designFlow ?? null;
|
|
},
|
|
sync,
|
|
write(kind, options) {
|
|
const text = (key: string): string =>
|
|
options[key] !== undefined ? String(options[key]) : "";
|
|
count.value = kind === "ford_bridge" ? text("pipe_count") : "";
|
|
width.value = text("ford_width_m");
|
|
width.dataset.empty = String(emptyWidth(kind));
|
|
width.placeholder = `비우면 ${emptyWidth(kind)}`;
|
|
height.value = text("ford_height_m");
|
|
slope.value = text("ford_slope_pct");
|
|
thickness.value = text("thickness_cm");
|
|
length.value = text("length_m");
|
|
},
|
|
read(kind, target) {
|
|
putNumber(target, "ford_width_m", width.value);
|
|
// 월류 높이는 cm 단위 수심이라 0.01m 정밀도로 싣는다(putNumber는 0.1m 반올림).
|
|
const value = Number.parseFloat(height.value);
|
|
if (Number.isFinite(value) && value > 0) target.ford_height_m = Number(value.toFixed(2));
|
|
if (kind === "ford_pavement") {
|
|
putNumber(target, "ford_slope_pct", slope.value);
|
|
putNumber(target, "thickness_cm", thickness.value);
|
|
putNumber(target, "length_m", length.value);
|
|
return;
|
|
}
|
|
const pipes = Number.parseInt(count.value, 10);
|
|
if (Number.isFinite(pipes) && pipes > 0) target.pipe_count = pipes;
|
|
},
|
|
};
|
|
}
|