- POST /design/save 에 route(노선 초안) · carry(옛 노선 세션 칸) 칸 · 서버 = 노선 저장 → Node facility.rebase(rebasePipeDraft · rebaseStructureDraft) → 6-A 차례(새 노선) → Node stations.carry(routeCarryPatches) → 횡단 저장 - 되돌림 범위 = 노선 행 · 자식 표 · 단계 · 배수 폴더 · 노선 파일 · 지표면 확정(설계 지형 바꿈) - 손 상단측은 안 넘김(요구 274) · 노선 바뀐 [확정] = [저장](새 노선 본 뒤 다시) - 브라우저 = 노선 초안 + 사용자 칸만 한 본문 · 옛 차례 길(saveRouteDraft 뒤 flush · carryAfterRouteSave · keepDraftsOnNewRoute) · 부름 0 된 flush 창구 여섯 · saveCachedCrossPatches 걷음 - 등록부 R9 stations.carry · F10 facility.rebase · 옛 차례 단언 시험 한 본문 꼴로 · test_472_6_route_save(옛 길 = 새 길 · 실패 되돌림) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
196 lines
8.0 KiB
JavaScript
196 lines
8.0 KiB
JavaScript
/* 472-6 6-B 헬퍼 — 노선 바뀐 [저장] 횡단 넘김: 브라우저 옛 길(세션 함수를 옛 `carryAfterRouteSave` →
|
|
* `saveCachedCrossPatches` 차례 그대로) 결과 · 새 길(서버 Node `stations.carry`) 입력을 JSON 한 벌로.
|
|
* node helper_472_6_route_carry.mjs <묶음.mjs>
|
|
* 시험(`test_472_6_route_save.py`)이 새 길 입력을 진짜 Node 입구로 돌려 두 결과를 견줌. */
|
|
import { pathToFileURL } from "node:url";
|
|
|
|
const store = () => {
|
|
const m = new Map();
|
|
return {
|
|
getItem: (k) => (m.has(k) ? m.get(k) : null),
|
|
setItem: (k, v) => m.set(k, String(v)),
|
|
removeItem: (k) => m.delete(k),
|
|
key: (i) => [...m.keys()][i] ?? null,
|
|
get length() {
|
|
return m.size;
|
|
},
|
|
clear: () => m.clear(),
|
|
};
|
|
};
|
|
globalThis.window = globalThis;
|
|
globalThis.sessionStorage = store();
|
|
globalThis.localStorage = store();
|
|
const any = new Proxy(function () {}, {
|
|
get: (_t, k) => (k === Symbol.toPrimitive ? () => 0 : any),
|
|
apply: () => any,
|
|
construct: () => any,
|
|
});
|
|
globalThis.document = any;
|
|
globalThis.requestAnimationFrame = (run) => setTimeout(run, 0);
|
|
|
|
const [bundlePath] = process.argv.slice(2);
|
|
const m = await import(pathToFileURL(bundlePath).href);
|
|
const P = "p4726";
|
|
const R = 7; // 옛 노선
|
|
const R2 = 9; // 새 노선(서버가 저장한 것)
|
|
const clone = (value) => JSON.parse(JSON.stringify(value));
|
|
|
|
const pipe = { structure: "파형강관 D800", culvert: {} };
|
|
const section = (chainage_m, kind, extra = {}) => ({
|
|
station_id: `s${chainage_m}`,
|
|
chainage_m,
|
|
kind,
|
|
label: String(chainage_m),
|
|
samples: [],
|
|
design: {},
|
|
...extra,
|
|
});
|
|
const detailOf = (sections) => ({
|
|
longitudinal: { stations: sections, profile_alignment: { policy: { station_interval_m: 20 } } },
|
|
cross_sections: sections,
|
|
});
|
|
/* 새 노선 서버 상세 — 50 m 뒤가 3 m 밀림(관 80 → 83) · MC 110 → 112 · 구조물 130 사라짐 · 끝 140 → 143 */
|
|
const fresh = () =>
|
|
detailOf([
|
|
section(0, "bp"),
|
|
section(20, "regular", { design: { ground_type: "rock" } }),
|
|
section(40, "regular"),
|
|
section(60, "regular", { design: { display_half_width_m: 5, ground_type: "soil" } }),
|
|
section(83, "irregular", pipe),
|
|
section(103, "regular"),
|
|
section(112, "curve"),
|
|
section(123, "regular"),
|
|
section(143, "ep"),
|
|
]);
|
|
/* 옛 노선 열쇠 목록(브라우저가 끌기 동안 세운 것) · 측점 주인(초안 자리) · 관 장부 */
|
|
m.useStationKeyList(P, R, [
|
|
{ key: "end:BP", chainageM: 0 },
|
|
{ key: "grid:1", chainageM: 20 },
|
|
{ key: "grid:2", chainageM: 40 },
|
|
{ key: "grid:3", chainageM: 60 },
|
|
{ key: "pipe:p80", chainageM: 80 },
|
|
{ key: "grid:5", chainageM: 100 },
|
|
{ key: "curve:110.00", chainageM: 110 },
|
|
{ key: "grid:6", chainageM: 120 },
|
|
{ key: "structure:s1", chainageM: 130 },
|
|
{ key: "end:EP", chainageM: 140 },
|
|
]);
|
|
m.useStationOwners(P, [{ chainage_m: 83, owners: ["p80"] }], [{ id: "p80" }]);
|
|
m.writeState("pipe-ids", { next: 2, ids: [{ id: "pipe-s1", at: 83 }] }, P);
|
|
const pipesBefore = [{ id: "p80", chainage_m: 80 }];
|
|
const pipesAfter = [
|
|
{ id: "p80", chainage_m: 83, options: { inlet_revet_height_m: 1.8, inlet_revet_form: "I" } },
|
|
];
|
|
|
|
/* 옛 노선 세션 고침 — 측점 열쇠 칸 다섯 · 누가거리 칸 · 프로젝트 칸(종류 바꿈 · 상단측) */
|
|
m.writeCrossDesignChoice(P, R, 20, {
|
|
ground_type: "soil",
|
|
section_mode: "left_cut",
|
|
ditch_type: "u_flume",
|
|
two_stage_slope: true,
|
|
});
|
|
m.writeCrossDesignChoice(P, R, 130, { ground_type: "soil", section_mode: "right_cut" }); // 밖
|
|
m.writeShoulderExtend(P, R, 40, "left", 0.5);
|
|
m.writeBermDepth(P, R, 130, "right", 1); // 밖
|
|
m.writeState("rockb", { "grid:5": 1.5, "@at": { "grid:5": 100 } }, P, R);
|
|
m.writeState("cutslope", { "curve:110.00": 0.8, "@at": { "curve:110.00": 110 } }, P, R);
|
|
const wall = { x: 0.3, d: null, h: 1.5, m: null };
|
|
m.writeState("revetx", { "80.00:outlet": wall, "40.00:inlet": wall }, P, R); // 40 = 일반 측점 → 버림
|
|
m.writeState("basinadjust", { "80.00": { depth_m: 1.2 } }, P, R);
|
|
m.writeState("crossw", { "60.00": 6 }, P, R);
|
|
m.writeState("structure-notes", { "130.00": "메모", "20.00": "앞 메모" }, P, R);
|
|
m.writeState("detail-table", { "80.00": { rows: 2 } }, P, R);
|
|
m.writeState("revetlink", { detached: { "80.00": true }, grade: {} }, P, R);
|
|
m.writeState("kindreset", { "60.00": "culvert" }, P); // 일반 측점 = 관 · 구조물 열쇠 아님 → 버림
|
|
m.writeState("uphill", { "60.00": "right" }, P);
|
|
|
|
const out = {};
|
|
/* 새 길 입력 — 브라우저가 한 본문 `carry` 칸에 싣는 것(넘기기 전 세션 그대로) */
|
|
out.carry = clone(m.routeCarryInputs(P, R));
|
|
out.detail = fresh();
|
|
out.pipesBefore = pipesBefore;
|
|
out.pipesAfter = pipesAfter;
|
|
|
|
/* 옛 길 — `loadSectionDetail`(관 옵션 얹기) → `carryAfterRouteSave` → `saveCachedCrossPatches` 차례 그대로 */
|
|
const detail = fresh();
|
|
m.layPipeOptions(detail, pipesAfter);
|
|
m.useStationKeys(P, R2, detail);
|
|
const newAt = (id) => pipesAfter.find((p) => id !== undefined && p.id === id)?.chainage_m ?? null;
|
|
const pipeAt = (key) => {
|
|
const id = key.startsWith("pipe:") ? key.slice(5) : null;
|
|
if (!id) return null;
|
|
return id.startsWith("pipe-") ? m.pipeChainageOf(id, P) : newAt(id);
|
|
};
|
|
const movedAt = (chainageM) =>
|
|
newAt(pipesBefore.find((p) => Math.abs(p.chainage_m - chainageM) < 0.01)?.id);
|
|
const outside =
|
|
m.carryStationDrafts(P, R, R2, { quiet: true }) +
|
|
m.carryChainageStates(P, R, R2, { keyAt: pipeAt, movedAt });
|
|
m.withDraftAdjusts(detail, P, R2);
|
|
m.withCarriedEdits(detail, P, R2);
|
|
const base = m.crossPatchesFromCache(
|
|
detail,
|
|
m.kindChangedChainages(P),
|
|
detail.cross_sections.map((s) => s.chainage_m),
|
|
);
|
|
const overlay = [];
|
|
m.crossDesignChoices(P, R2).forEach((choice, chainage) => {
|
|
const patch = {
|
|
chainage_m: chainage,
|
|
ground_type: choice.ground_type,
|
|
section_mode: choice.section_mode,
|
|
};
|
|
if (choice.ditch_side !== undefined) patch.ditch_side = choice.ditch_side;
|
|
if (choice.ditch_type !== undefined) patch.ditch_type = choice.ditch_type;
|
|
if (choice.paved !== undefined) patch.paved = choice.paved;
|
|
if (choice.two_stage_slope !== undefined) patch.two_stage_slope = choice.two_stage_slope;
|
|
if (typeof choice.ditch_choice === "boolean") patch.ditch_choice = choice.ditch_choice;
|
|
overlay.push(patch);
|
|
});
|
|
// 옛 `readRockBoundarySession` · `readCutSlopeSession` = 측점 열쇠 칸을 새 목록 누가거리로(`readKeyed`)
|
|
const keyed = (name) => [...m.readKeyed(name, P, R2)];
|
|
for (const [chainage, offset] of keyed("rockb"))
|
|
if (typeof offset === "number")
|
|
overlay.push({ chainage_m: Number(chainage), rock_boundary_offset_m: offset });
|
|
for (const [chainage, ratio] of keyed("cutslope"))
|
|
overlay.push({ chainage_m: Number(chainage), cut_slope_ratio_user: ratio });
|
|
out.oldPatches = JSON.parse(
|
|
JSON.stringify(
|
|
m.mergeCrossPatches(
|
|
base,
|
|
overlay.filter((p) => Number.isFinite(p.chainage_m)),
|
|
),
|
|
),
|
|
);
|
|
out.oldOutside = outside;
|
|
|
|
/* 관 · 구조물 초안 다시 얹기 — 브라우저 옛 길이 부르던 같은 함수(서버 Node `facility.rebase` 와 견줌) */
|
|
out.rebaseInput = {
|
|
pipes: {
|
|
draft: [
|
|
{ id: "p80", chainage_m: 80, x: 80, y: 0 }, // 따라가기만 함 → 서버 자리
|
|
{ id: "p20", chainage_m: 25, x: 25, y: 0 }, // 사용자가 옮김 → 그대로
|
|
],
|
|
before: [
|
|
{ id: "p80", chainage_m: 80, x: 80, y: 0 },
|
|
{ id: "p20", chainage_m: 20, x: 20, y: 0 },
|
|
],
|
|
after: [
|
|
{ id: "p80", chainage_m: 83, x: 80, y: 0 },
|
|
{ id: "p20", chainage_m: 20, x: 20, y: 0 },
|
|
{ id: "pnew", chainage_m: 120, x: 117, y: 2 }, // 서버가 새로 놓은 교차 관
|
|
],
|
|
},
|
|
structures: {
|
|
draft: [{ structure_id: "s1", chainage_m: 60, start_m: null, end_m: null, x: 60, y: 0 }],
|
|
before: [{ structure_id: "s1", chainage_m: 60, start_m: null, end_m: null, x: 60, y: 0 }],
|
|
after: [{ structure_id: "s1", chainage_m: 63, start_m: null, end_m: null, x: 60, y: 0 }],
|
|
},
|
|
};
|
|
const r = out.rebaseInput;
|
|
out.oldRebase = {
|
|
pipes: m.rebasePipeDraft(r.pipes.draft, r.pipes.before, r.pipes.after),
|
|
structures: m.rebaseStructureDraft(r.structures.draft, r.structures.before, r.structures.after),
|
|
};
|
|
process.stdout.write(JSON.stringify(out));
|