Files
Pier_v1/tests/abut-bearing-table.test.ts
ypshimandClaude Opus 5.5 3bd2340609 feat(bearing): 솔플레이트 표에 '방위각 검사 (″)' 열 (Q-SH4)
교각·교대 솔플레이트 상세 TABLE에 받침별로 엑셀 방위각과 짝 받침 방향의 차이(초)를 보인다.
10″ 이하 "≤ 10″", 넘으면 WS01, 짝이 없으면 "— (짝 없음)".
- BearingSole.azimuthCheckSec (짝의 from/to 쪽에 맞는 값), 표 행 azimuthSec
- 테스트: P1 전열 ≤ 0.5″ · 후열 없음, A1 ≤ 0.5″, P1 ③ 방위각 75°59′ 오타 → 약 87″ · WS01 그 받침만
- 브라우저 확인(운곡교 완도·광주 데이터): P1-광주 전열 짝 A1-광주 0.0–0.3″, 후열 짝 P2-광주 0.0–0.1″

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 15:39:42 +09:00

100 lines
5.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import * as XLSX from 'xlsx';
import { describe, expect, it } from 'vitest';
import { buildAbut } from '../src/core/abut/geometry';
import { parseAbutments, toAbutInput } from '../src/core/abut/parse';
import { buildMembers } from '../src/core/abut/members';
import {
ABUT_BEARING_RULE_DEFAULT,
abutBearingChecks,
abutElevationRows,
abutSolplateRows,
resolveBearingRule,
} from '../src/core/abut/bearing-table';
import { applyBearingRule } from '../src/core/abut/edit';
import { checkStoredAbutments } from '../src/core/project';
import { p1Pairs } from './helpers';
const RAG = fileURLToPath(new URL('../../../RAG/운곡교P1입력.xlsx', import.meta.url));
const rec = parseAbutments(XLSX.read(readFileSync(RAG), { type: 'buffer' })).abutments[0];
const input = toAbutInput(rec);
const model = buildAbut(input);
const members = buildMembers(model, { bottomEl: input.foundationBottomEl, pileLengthMm: input.pileLengthMm });
const rule = resolveBearingRule();
const H = rec.bearingHeightMm;
// Bridge context as in the app: A1 ①–⑤ pair with P1 전열 ①–⑤ (2026-11-06), 교대 종단경사 i from the sheet.
const ctx = { pairs: p1Pairs(), slopeI: rec.slopeI };
describe('교대 받침 카드 (솔플레이트 결정 2026-11-06, h3 = 받침높이 − T_ref)', () => {
it('defaults: A 700 × B 640 + 100, T_ref 30, T_min 22; H 275 from the 교대 sheet → h3 245', () => {
expect(ABUT_BEARING_RULE_DEFAULT).toMatchObject({ tRefMm: 30, tMinMm: 22, marginMm: 100, aMm: 700, bMm: 640 });
expect(H).toBe(275);
const [r] = abutSolplateRows(model, rule, H, ctx);
expect([r.L, r.W, r.T, r.T1, r.T2]).toEqual([800, 740, 30, 38, 22]);
expect(abutElevationRows(model, rule, H, ctx)[0].h3).toBe(245);
});
it('ELEVATION: block 125 = 75 + 50, E = 75 + 50 + 245 + 30 = 400 (the girder underside of the sheet)', () => {
const rows = abutElevationRows(model, rule, H, ctx);
expect(rows).toHaveLength(5);
for (const r of rows) {
expect([r.h1, r.h2, r.h3, r.h4, r.E]).toEqual([75, 50, 245, 30, 400]);
expect(r.solplateTopEl).toBeCloseTo(r.seatEl + 0.4, 6);
}
const first = rows.find((r) => r.no === 1)!;
expect(first.seg).toBe('①');
expect(first.seatEl).toBeCloseTo(44.367, 3);
expect(first.z).toBeCloseTo(44.492, 3);
});
it('솔플레이트 TABLE: S from the P1 pair (−1.908 %, 계산), T1 38 on the 오르막 (A1) side; W08 within 1 mm', () => {
const rows = abutSolplateRows(model, rule, H, ctx);
expect(rows.map((r) => r.S.toFixed(3))).toEqual(['-1.908', '-1.906', '-1.904', '-1.904', '-1.902']);
expect(rows.every((r) => r.source === '계산' && r.pair?.startsWith('P1-'))).toBe(true);
expect(rows[0].check).toBe('16 ≈ 15.3 일치');
expect(rows.every((r) => r.azimuthSec !== undefined && r.azimuthSec <= 0.5)).toBe(true);
expect(abutSolplateRows(model, rule, H, { slopeI: rec.slopeI })[0].azimuthSec).toBeUndefined();
});
it('without a pair the 교대 종단경사 i (−0.0191) is used: S −1.91 % (입력), T 30', () => {
const [r] = abutSolplateRows(model, rule, H, { slopeI: rec.slopeI });
expect(r.source).toBe('입력');
expect(r.S).toBeCloseTo(-1.91, 9);
expect(r.T).toBe(30);
});
it('S typed −3 % → T 34; the block concrete loses 4 mm and E stays 400 (거더 하면 고정)', () => {
const steep = resolveBearingRule({ slopePct: -3 });
expect(abutSolplateRows(model, steep, H, ctx)[0]).toMatchObject({ T: 34, T1: 46, T2: 22, source: '수정됨' });
const r = abutElevationRows(model, steep, H, ctx)[0];
expect([r.h1, r.h4, r.E]).toEqual([71, 34, 400]);
});
it('checks: W02, W04 (흉벽 ≈ 145 · 전면 255), W03 and I01 all clear for A1', () => {
const lines = abutBearingChecks(model, members, rule, H, ctx);
expect(lines).toHaveLength(4);
expect(lines[0]).toContain('W02 없음');
// 시안 145 is bearing ① (lateral 0.745); the smallest gap over the five bearings is 144 (lateral 0.744).
expect(lines[1]).toMatch(/흉벽 14[45]/);
expect(lines[1]).toContain('전면 255');
expect(lines[1]).toContain('W04 없음');
expect(lines[2]).toContain('W03 없음');
expect(lines[3]).toContain('I01 없음');
});
it('the edit keeps only changed values, clears optional ones, refuses non-positive sizes and h3 ≤ 0, and the project file accepts it', () => {
const edited = applyBearingRule(applyBearingRule(rec, 'aMm', 650), 'tMm', 32);
expect(edited.bearingRule).toEqual({ aMm: 650, tMm: 32 });
expect(applyBearingRule(edited, 'tMm', undefined).bearingRule).toEqual({ aMm: 650 });
expect(() => applyBearingRule(rec, 'aMm', 0)).toThrow('0보다');
expect(() => applyBearingRule(rec, 'tRefMm', 300)).toThrow('h3');
expect(() => checkStoredAbutments([edited])).not.toThrow();
expect(() => checkStoredAbutments([{ ...edited, bearingRule: { aMm: 'x' } }])).toThrow('받침 규칙');
});
it('values stored by the 2026-11-03 C안 (lengthMm, widthMm) are ignored', () => {
expect(resolveBearingRule({ lengthMm: 900, widthMm: 1000 } as Record<string, unknown>)).toEqual(ABUT_BEARING_RULE_DEFAULT);
});
});