- ground-model.ts: read and check schema 1 of the Python ground model. - ② 지반·지층 card: import the json, show the DTM value beside the excel value, and apply it only when the user presses the button (tracked as an override, revertible). - groundEl becomes an editable, tracked input; the project file keeps the imported model and checks it on open. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
89 lines
3.8 KiB
TypeScript
89 lines
3.8 KiB
TypeScript
import type { FckByMember, Material } from './rebar/common';
|
|
import { checkGroundModel, type GroundModelFile } from './ground-model';
|
|
import type { PierInput } from './types';
|
|
|
|
/** Project file schema (spec §8). Bump only with a migration. */
|
|
export const SCHEMA_VERSION = 1;
|
|
|
|
export interface ProjectInfo {
|
|
name: string;
|
|
bridge: string;
|
|
client: string;
|
|
author: string;
|
|
crs: string;
|
|
memo: string;
|
|
}
|
|
|
|
export interface ProjectPier {
|
|
id: string;
|
|
/** Generated input: the values the 3D model was built from. The model is not stored; it is regenerated on open (spec §8). */
|
|
input: PierInput;
|
|
/** Values as imported from the sheet (기본값). Missing in files saved before change tracking: input is used. */
|
|
defaults?: PierInput;
|
|
/** Values currently in the cards (현재 입력). Missing in older files: input is used. */
|
|
draft?: PierInput;
|
|
}
|
|
|
|
export interface ProjectFile {
|
|
schemaVersion: number;
|
|
project: ProjectInfo;
|
|
source: { fileName: string; sheets: string[] };
|
|
piers: ProjectPier[];
|
|
history: { version: string; note: string; at: string }[];
|
|
/** ③ 공통 설정 (fck · fy). Absent in older files: the defaults are used. */
|
|
common?: Material;
|
|
/** 받침 기준 (project-wide): 'excel' (default) or 'drawing'. Absent = 'excel'. */
|
|
bearingBasis?: 'excel' | 'drawing';
|
|
/** 도면 받침 값 per pier name (EL.X in m, h1 and h2 in mm). */
|
|
bearingDrawing?: Record<string, { no: number; elx: number; h1: number; h2: number }[]>;
|
|
/** ② 부재별 fck (MPa). Absent in older files: every member uses the 공통 fck. */
|
|
fckByMember?: FckByMember;
|
|
/** 지반 모델 (ground_model.json)의 교각별 지반고. Absent = 불러오지 않음. Applied to a pier only by the user. */
|
|
groundModel?: GroundModelFile;
|
|
}
|
|
|
|
/** Serialises a project to the pretty-printed JSON stored in <교량명>_교각설계.pzr.json. */
|
|
export function serializeProject(p: ProjectFile): string {
|
|
return `${JSON.stringify(p, null, 2)}\n`;
|
|
}
|
|
|
|
/**
|
|
* Parses and checks a project file. Throws with a message that names the first problem, so a damaged file is
|
|
* refused instead of being half-loaded.
|
|
*/
|
|
export function parseProject(text: string): ProjectFile {
|
|
let raw: unknown;
|
|
try {
|
|
raw = JSON.parse(text);
|
|
} catch {
|
|
throw new Error('프로젝트 파일이 JSON으로 읽히지 않습니다.');
|
|
}
|
|
const obj = raw as Partial<ProjectFile>;
|
|
if (!obj || typeof obj !== 'object') throw new Error('프로젝트 파일 형식이 아닙니다.');
|
|
if (obj.schemaVersion !== SCHEMA_VERSION) {
|
|
throw new Error(`지원하지 않는 파일 버전입니다 (schemaVersion ${String(obj.schemaVersion)}, 지원: ${SCHEMA_VERSION}).`);
|
|
}
|
|
if (!Array.isArray(obj.piers)) throw new Error('piers 목록이 없습니다.');
|
|
if (obj.fckByMember !== undefined) {
|
|
for (const [member, value] of Object.entries(obj.fckByMember)) {
|
|
if (!['column', 'coping', 'footing'].includes(member) || typeof value !== 'number' || !(value > 0)) {
|
|
throw new Error(`부재별 fck 값이 맞지 않습니다 (${member}: ${String(value)}).`);
|
|
}
|
|
}
|
|
}
|
|
if (obj.groundModel !== undefined) checkGroundModel(obj.groundModel);
|
|
obj.piers.forEach((p, i) => {
|
|
if (!p || typeof p.id !== 'string' || !p.input) throw new Error(`교각 ${i + 1}: id 또는 input이 없습니다.`);
|
|
const input = p.input;
|
|
if (typeof input.name !== 'string' || !Array.isArray(input.ips) || !Array.isArray(input.bearings)) {
|
|
throw new Error(`교각 ${i + 1}(${String(input.name)}): 입력 형식이 맞지 않습니다.`);
|
|
}
|
|
for (const extra of [p.defaults, p.draft]) {
|
|
if (extra !== undefined && (typeof extra !== 'object' || extra === null || typeof extra.name !== 'string')) {
|
|
throw new Error(`교각 ${i + 1}(${input.name}): defaults 또는 draft 형식이 맞지 않습니다.`);
|
|
}
|
|
}
|
|
});
|
|
return obj as ProjectFile;
|
|
}
|