import type { PierInput } from '../types'; import { DEFAULT_COLUMN_REBAR } from './column-settings'; import { NOMINAL_DIAMETER, DEFAULT_MATERIAL, type Material } from './common'; /** Dedicated settings: round-column defaults were authorised by the user on 2026-11-05. */ export interface CastRebarPart { mainDiameterMm?: number; mainCount?: number; mainGradeMpa?: number; coverMm?: number; concreteStrengthMpa?: number; tieDiameterMm?: number; tieGradeMpa?: number; tieSpacingMm?: number; endZoneLengthMm?: number; endZoneSpacingMm?: number; cageLengthMm?: number; endCoverMm?: number; topEmbedmentMm?: number; tieKind?: 'hoop' | 'spiral'; coverReference?: 'clear' | 'main-center'; innerMainDiameterMm?: number; innerMainCount?: number; } export interface CastInPlaceRebar { column?: CastRebarPart; pile?: CastRebarPart; connection?: { mode?: 'continuous' | 'lap' | 'coupler'; lapLengthMm?: number; couplerLengthMm?: number; }; } export const CAST_PART_FIELDS: { key: Exclude; label: string; only?: 'column' | 'pile' }[] = [ { key: 'mainDiameterMm', label: '외측 주철근 공칭지름 (mm)' }, { key: 'mainCount', label: '외측 주철근 본수 (본)' }, { key: 'mainGradeMpa', label: '주철근 강도 fy (MPa)' }, { key: 'coverMm', label: '측면 피복 (아래 기준 선택, mm)' }, { key: 'concreteStrengthMpa', label: '콘크리트 강도 fck (MPa)' }, { key: 'tieDiameterMm', label: '띠·나선철근 직경 (mm)' }, { key: 'tieGradeMpa', label: '띠·나선철근 강도 fy (MPa)' }, { key: 'tieSpacingMm', label: '일반구간 띠 간격·나선 피치 (mm)' }, { key: 'endZoneLengthMm', label: '단부 보강구간 길이 (mm)' }, { key: 'endZoneSpacingMm', label: '단부 보강구간 간격 (mm)' }, { key: 'cageLengthMm', label: '말뚝 철근망 길이 (mm)', only: 'pile' }, { key: 'endCoverMm', label: '말뚝 선단 순피복 (mm)', only: 'pile' }, { key: 'topEmbedmentMm', label: '코핑 내부 주철근 연장 길이 (mm)', only: 'column' }, { key: 'innerMainDiameterMm', label: '내측 주철근 공칭지름 (mm)' }, { key: 'innerMainCount', label: '내측 주철근 본수 (본)' }, ]; export function castRebarDefaults(input: Pick, material: Material = DEFAULT_MATERIAL): CastInPlaceRebar { const c = { ...DEFAULT_COLUMN_REBAR, ...input.columnRebar }; const saved = input.castInPlaceRebar ?? {}; const part = (key: 'column' | 'pile'): CastRebarPart => ({ mainDiameterMm: NOMINAL_DIAMETER.S32, mainCount: c.nDiv, mainGradeMpa: material.grade.S, innerMainDiameterMm: NOMINAL_DIAMETER.S32, innerMainCount: c.nDiv, coverMm: c.c, coverReference: saved[key]?.coverMm !== undefined && saved[key]?.coverReference === undefined ? 'clear' : 'main-center', concreteStrengthMpa: material.fck, tieDiameterMm: NOMINAL_DIAMETER.H19, tieGradeMpa: material.grade.H, tieSpacingMm: c.ctcP3, endZoneSpacingMm: c.ctcP5, tieKind: 'hoop', ...(key === 'pile' ? { endCoverMm: 250 } : {}), ...saved[key], }); return { column: part('column'), pile: part('pile'), connection: { mode: 'coupler', couplerLengthMm: 150, ...saved.connection } }; } export const CAST_PENDING = ['현장타설 기둥 배근 상세 미확정', '현장타설말뚝 배근 상세 미확정', '기둥·말뚝 접합부 배근 상세 미확정']; /** Storage and input validation; domain-specific spacing/anchorage rules remain unconfirmed. */ export function castSettingsIssues(value: unknown, pileLengthMm?: number, diameterMm?: number): string[] { if (value === undefined) return []; const issues: string[] = []; if (!value || typeof value !== 'object' || Array.isArray(value)) return ['현장타설 배근 입력 형식이 맞지 않습니다.']; const record = value as Record; if (Object.keys(record).some(k => !['column', 'pile', 'connection'].includes(k))) issues.push('알 수 없는 현장타설 배근 항목입니다.'); for (const part of ['column', 'pile', 'connection'] as const) { const values = record[part]; if (values === undefined) continue; if (!values || typeof values !== 'object' || Array.isArray(values)) { issues.push(`${part}: 입력 형식이 맞지 않습니다.`); continue; } for (const [key, v] of Object.entries(values)) { if (v === undefined) continue; if (part === 'connection' && key === 'mode') { if (typeof v !== 'string' || !['continuous', 'lap', 'coupler'].includes(v)) issues.push('접합 방식이 맞지 않습니다.'); } else if (part !== 'connection' && key === 'tieKind') { if (typeof v !== 'string' || !['hoop', 'spiral'].includes(v)) issues.push('횡철근 형식이 맞지 않습니다.'); } else if (part !== 'connection' && key === 'coverReference') { if (typeof v !== 'string' || !['clear', 'main-center'].includes(v)) issues.push('피복 기준이 맞지 않습니다.'); } else { const allowed = part === 'connection' ? ['lapLengthMm', 'couplerLengthMm'] : CAST_PART_FIELDS.filter(f => !f.only || f.only === part).map(f => f.key); if (!allowed.includes(key) || typeof v !== 'number' || !Number.isFinite(v) || v <= 0 || (key.endsWith('Count') && !Number.isInteger(v))) issues.push(`${part}.${key}: 양수${key.endsWith('Count') ? ' 정수' : ''}로 입력하세요.`); } } if (part === 'pile') { const p = values as CastRebarPart; if (pileLengthMm !== undefined && p.cageLengthMm !== undefined && p.cageLengthMm > pileLengthMm) issues.push('말뚝 철근망 길이가 말뚝 길이보다 큽니다.'); } if (part !== 'connection') { const p = values as CastRebarPart; if (diameterMm !== undefined && p.coverMm !== undefined && p.coverMm * 2 >= diameterMm) issues.push(`${part}: 피복이 단면 반지름 이상입니다.`); } } return issues; }