feat(core): parse P1 workbook and build T-type pier shape
- parse Excel input by header strings (pier, IP, bearing tables) - DMS azimuth parser (E03), input validation (E01, E02) - pure geometry: axis, bearings, stepped coping, column, footing, piles, blinding - warnings W01, W02, W05, W07, I01 - regression tests against spec.md section 12 (P1) Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
d963d8ed3c
15 files changed
+2527
No files matched your search
@@ -0,0 +1,69 @@
|
||||
import { DIM, type PierModel } from './geometry';
|
||||
import type { Issue, PierInput, PierType } from './types';
|
||||
|
||||
const EXPECTED_COLUMNS: Record<PierType, number> = { 'T형': 1, '2주형': 2, '3주형': 3 };
|
||||
|
||||
/** Rules from spec §10 that are not implemented in this stage. Shown to the user, never silently skipped. */
|
||||
export const NOT_IMPLEMENTED_RULES = [
|
||||
'E04 fck 미입력 (배근 단계, 3단계)',
|
||||
'W03 받침 중심 ~ 계단 경계 < 블럭 폭/2',
|
||||
'W04 블럭이 코핑 평면 밖',
|
||||
'W06 속성 복사 건너뜀 (2단계)',
|
||||
'W08 솔플레이트 T1−T2 검사 (2단계)',
|
||||
'다주형 코핑 헌치·Level−300 (Q3), 일체기초 판정 (2·3주형)',
|
||||
] as const;
|
||||
|
||||
/** Input-level checks (spec §10 E01, E02). Returns false when the pier must not be built. */
|
||||
export function validateInput(input: PierInput, issues: Issue[]): boolean {
|
||||
let ok = true;
|
||||
if (input.columnCount !== EXPECTED_COLUMNS[input.type]) {
|
||||
issues.push({
|
||||
code: 'E01',
|
||||
level: 'E',
|
||||
pier: input.name,
|
||||
message: `교각형식 ${input.type}과 기둥갯수 ${input.columnCount}개가 맞지 않습니다. 기둥 ${EXPECTED_COLUMNS[input.type]}개로 맞추세요.`,
|
||||
});
|
||||
ok = false;
|
||||
}
|
||||
const front = input.bearings.filter((b) => b.row === '전열').length;
|
||||
const back = input.bearings.filter((b) => b.row === '후열').length;
|
||||
if (front !== back) {
|
||||
issues.push({ code: 'E02', level: 'E', pier: input.name, message: `전열 받침 ${front}개, 후열 받침 ${back}개로 수가 다릅니다.` });
|
||||
ok = false;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/** Geometry-level checks (spec §10 W01, W02, W05, W07, I01) on a built model. */
|
||||
export function validatePier(input: PierInput, model: PierModel, issues: Issue[]): void {
|
||||
const ip2Gap = Math.hypot(model.ip2Input.e - model.ip2Calc.e, model.ip2Input.n - model.ip2Calc.n);
|
||||
if (ip2Gap > 0.001) {
|
||||
issues.push({
|
||||
code: 'W01',
|
||||
level: 'W',
|
||||
pier: input.name,
|
||||
message: `IP2 입력과 재계산값이 ${(ip2Gap * 1000).toFixed(1)} mm 다릅니다. 재계산값을 사용합니다.`,
|
||||
});
|
||||
}
|
||||
const minBlock = Math.min(model.derived.blockHeightFront, model.derived.blockHeightBack);
|
||||
if (minBlock <= 0) {
|
||||
issues.push({ code: 'W02', level: 'W', pier: input.name, message: '받침블럭 콘크리트 높이가 0 이하입니다. 블럭을 생략합니다.' });
|
||||
}
|
||||
if (input.foundationType !== '현장타설말뚝' && model.footings.some((f) => f.top > input.groundEl)) {
|
||||
issues.push({ code: 'W05', level: 'W', pier: input.name, message: '확대기초 상면이 지반고보다 높습니다.' });
|
||||
}
|
||||
if (model.derived.frontBackDiffSpread > DIM.lateralTolerance) {
|
||||
issues.push({ code: 'W07', level: 'I', pier: input.name, message: '전열·후열 EL 차이가 받침마다 다릅니다 (정보).' });
|
||||
}
|
||||
// I01: block concrete below cover + bar diameter (cover 100 mm, S1·S2 H19 from spec §6.3).
|
||||
const cover = 0.1;
|
||||
const barDia = 0.019;
|
||||
if (minBlock < cover + barDia) {
|
||||
issues.push({
|
||||
code: 'I01',
|
||||
level: 'I',
|
||||
pier: input.name,
|
||||
message: `받침블럭 높이 ${(minBlock * 1000).toFixed(0)} mm가 피복 + 철근 지름(${((cover + barDia) * 1000).toFixed(0)} mm)보다 작습니다. 블럭 철근은 코핑 안에 묻힙니다.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user