Merge remote-tracking branch 'origin/sub_laptop_6' into main_laptop_1
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/* =============================================================================
|
||||
* A00_Common/design/design_pipeline_facilities.ts
|
||||
* 계산 묶음 관 · 구조물 먹이기(PLAN 415) — 가게 관 · 구조물 초안이 바뀌면 묶음에 넣음(비면 저장분).
|
||||
* 계산 묶음 관 · 구조물 · 추가 측점 먹이기(PLAN 415 · 429 ⑨) — 가게 초안이 바뀌면 묶음에 넣음(비면 저장분).
|
||||
* · [저장] 도중 = 안 넣음(H2) — 초안이 비는 사이 열 때 읽은 옛 저장분으로 돌지 않게 · 끝나면 `resync`
|
||||
* (저장 목록 다시 읽기 · 입력 맞춤) 한 번.
|
||||
* · 되돌리기 · 다시하기 = 세션만 되쓰고 가게 알림이 없음 → 묶음 입력과 견줘 다르면 넣음(415-2 · 노선 초안은
|
||||
@@ -12,10 +12,11 @@ import { onPageHistory } from "../../B05_Profile/B05_Profile_UI_Profile_History"
|
||||
import type { useDesignStore } from "./design_store";
|
||||
import type { ChangeSource } from "./logic/design_pipeline_types";
|
||||
|
||||
type Key = "pipes" | "structures";
|
||||
type Key = "pipes" | "structures" | "plusStations";
|
||||
const FACILITIES: ReadonlyArray<{ source: ChangeSource; key: Key }> = [
|
||||
{ source: "pipe", key: "pipes" },
|
||||
{ source: "structure", key: "structures" },
|
||||
{ source: "plus", key: "plusStations" },
|
||||
];
|
||||
|
||||
export interface FacilityFeed {
|
||||
|
||||
@@ -58,6 +58,7 @@ import {
|
||||
readAlignmentDraft,
|
||||
} from "../../B05_Profile/B05_Profile_UI_Profile_Edit";
|
||||
import { readAlignment } from "../../B05_Profile/logic/B05_Profile_UI_Profile_Data";
|
||||
import { plusStationsOf } from "../../B05_Profile/B05_Profile_Api_PlusStations";
|
||||
import { pageReplaying } from "../../B05_Profile/B05_Profile_UI_Profile_History";
|
||||
import {
|
||||
analysisTerrain,
|
||||
@@ -533,6 +534,7 @@ function createPagePipeline(
|
||||
cross: crossNow()?.input ?? null,
|
||||
pipes: store.get("pipes") ?? savedPipes,
|
||||
structures: store.get("structures") ?? savedStructures,
|
||||
plusStations: plusStationsOf(projectId),
|
||||
};
|
||||
};
|
||||
const start = (): void => {
|
||||
@@ -625,7 +627,12 @@ function createPagePipeline(
|
||||
store,
|
||||
ready: () => started && !echo,
|
||||
gone: () => disposed,
|
||||
saved: (key) => (key === "pipes" ? savedPipes : savedStructures),
|
||||
saved: (key) =>
|
||||
key === "pipes"
|
||||
? savedPipes
|
||||
: key === "structures"
|
||||
? savedStructures
|
||||
: plusStationsOf(projectId),
|
||||
input: (key) => pipe.input(key),
|
||||
routed: () => pipe.input("route") !== null || store.get("routeDraft") !== null,
|
||||
feed: (source, value) => void feed(source, value),
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
* 새 노선 번호를 준다. 그 뒤 차례는 **새 노선**으로: 관 · 구조물 초안(좌표 정본) · 계획고 초안(옛 노선 칸 →
|
||||
* 새 노선) · 횡단 초안은 측점 열쇠로 넘겨(`carryStationDrafts`) 저장 · 결과 한 줄 · 페이지 다시 세움.
|
||||
* 노선 저장이 실패하면 서버가 되살리고 여기서 멈춤(다른 초안도 화면 · 세션에 그대로 · 오류 한 줄).
|
||||
* 어느 차례든 실패하면 그 차례에서 멈추고 차례 이름째 알림(429 ⑨ · 뒤 차례는 안 보냄).
|
||||
* 노선을 저장했으면 횡단은 고침이 없어도 한 번 나감 — 뒤에 나간 계획고 초안을 서버가 횡단에 다시 셈(429 ④).
|
||||
*
|
||||
* 차례는 여기 한 곳에만 둔다. 보기마다 다른 것은 **화면이 쥔 값으로 한 번 더 맞추는 일**
|
||||
* (B05 배수유역도 목록 · 구조물 목록 새로 받기 · 종단 패널 다시 그리기)과 횡단 저장 본문뿐이라,
|
||||
@@ -86,12 +88,22 @@ export function savingDesign(projectId: string | null): boolean {
|
||||
return projectId !== null && saving.has(projectId);
|
||||
}
|
||||
|
||||
/** 실패해도 저장을 멈추지 않는 창구 — 알리고 넘어간다(초안은 세션에 남아 다시 나갈 수 있다). */
|
||||
function reportOnly(label: string): (error: unknown) => void {
|
||||
return (error) => {
|
||||
const detail = error instanceof Error ? ` ${error.message}` : "";
|
||||
showToast(`${label} 저장에 실패했습니다.${detail}`, "error");
|
||||
};
|
||||
/** 차례 이름 — 실패하면 그 차례에서 멈추고 이름째 알림(429 ⑨ · 뒤 차례가 옛 정본 위에 얹히지 않게 ·
|
||||
* 초안은 세션에 남아 다음 [저장]에서 다시 나감). */
|
||||
const STEP_LABEL: Record<DesignSaveStep, string> = {
|
||||
route: "노선",
|
||||
pipes: "배수관",
|
||||
structures: "구조물",
|
||||
plusStations: "추가 측점",
|
||||
alignment: "계획선",
|
||||
culvertOptions: "암거 옵션",
|
||||
uphill: "상단측",
|
||||
cross: "횡단",
|
||||
};
|
||||
|
||||
function stepFailed(step: DesignSaveStep, error: unknown): Error {
|
||||
const detail = error instanceof Error ? ` ${error.message}` : "";
|
||||
return new Error(`${STEP_LABEL[step]} 차례에서 멈췄습니다(뒤 차례 안 보냄).${detail}`);
|
||||
}
|
||||
|
||||
/** 서버가 버린 계획고 편집 자리(측점 · 관 · 구조물이 없는 자리 · 213-2)를 알린다. */
|
||||
@@ -128,15 +140,7 @@ async function flushAlignmentDraft(
|
||||
if (moved) markAlignmentSaved(DRAFT_ROUTE_ID);
|
||||
}
|
||||
|
||||
/** 실패해도 다음 차례로 넘어가는 창구와 그 이름(보기가 준 일에도 같이 씌운다). */
|
||||
const TOLERANT: Partial<Record<DesignSaveStep, string>> = {
|
||||
pipes: "배수관",
|
||||
structures: "구조물",
|
||||
plusStations: "추가 측점",
|
||||
alignment: "계획선",
|
||||
};
|
||||
|
||||
/** 차례마다 기본 창구. 암거 옵션 · 상단측은 조용히 넘어간다(다음 [저장]에서 다시 나감). */
|
||||
/** 차례마다 기본 창구. */
|
||||
function defaultStep(
|
||||
step: Exclude<DesignSaveStep, "cross" | "route">,
|
||||
projectId: string,
|
||||
@@ -153,12 +157,9 @@ function defaultStep(
|
||||
case "alignment":
|
||||
return () => flushAlignmentDraft(projectId, routeId, fromRouteId);
|
||||
case "culvertOptions":
|
||||
return () =>
|
||||
flushCulvertOptions(projectId, stateKey("culvertopt", projectId, fromRouteId)).catch(
|
||||
() => undefined,
|
||||
);
|
||||
return () => flushCulvertOptions(projectId, stateKey("culvertopt", projectId, fromRouteId));
|
||||
case "uphill":
|
||||
return () => flushUphillOverrides(projectId).catch(() => undefined);
|
||||
return () => flushUphillOverrides(projectId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,35 +202,38 @@ export async function saveDesign(
|
||||
try {
|
||||
await ctx.prepare?.();
|
||||
for (const step of DESIGN_SAVE_ORDER) {
|
||||
if (step === "route") {
|
||||
const body = await pendingRouteSave(ctx.projectId); // 안 바뀌었으면 null — 안 보냄
|
||||
if (!body) continue;
|
||||
const before = await savedPipesIfDraft(ctx.projectId);
|
||||
routeSaved = await saveRouteDraft(ctx.projectId, body); // 실패 = throw(서버가 되살림)
|
||||
if (routeSaved.route_id != null) routeId = routeSaved.route_id;
|
||||
if (before) await rebasePendingPipes(ctx.projectId, before);
|
||||
continue;
|
||||
}
|
||||
if (step === "cross") {
|
||||
if (!routeSaved) await ctx.cross(confirm);
|
||||
else {
|
||||
const outside = await carryAfterRouteSave(ctx.projectId, fromRouteId, routeId);
|
||||
showToast(routeSaveSummary(routeSaved, outside), "info");
|
||||
try {
|
||||
if (step === "route") {
|
||||
const body = await pendingRouteSave(ctx.projectId); // 안 바뀌었으면 null — 안 보냄
|
||||
if (!body) continue;
|
||||
const before = await savedPipesIfDraft(ctx.projectId);
|
||||
routeSaved = await saveRouteDraft(ctx.projectId, body); // 실패 = throw(서버가 되살림)
|
||||
if (routeSaved.route_id != null) routeId = routeSaved.route_id;
|
||||
if (before) await rebasePendingPipes(ctx.projectId, before);
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
if (step === "cross") {
|
||||
if (!routeSaved) await ctx.cross(confirm);
|
||||
else {
|
||||
const outside = await carryAfterRouteSave(ctx.projectId, fromRouteId, routeId);
|
||||
showToast(routeSaveSummary(routeSaved, outside), "info");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 노선이 바뀌면 화면 몫 일(옛 노선 판 목록 · 계획고 판)은 건너뛰고 기본 창구만 새 노선으로.
|
||||
const own = routeSaved ? undefined : ctx.steps?.[step];
|
||||
const base = defaultStep(step, ctx.projectId, routeId, fromRouteId);
|
||||
const run =
|
||||
step === "pipes"
|
||||
? async () => {
|
||||
await base();
|
||||
await own?.();
|
||||
}
|
||||
: (own ?? base);
|
||||
await run();
|
||||
} catch (error) {
|
||||
throw stepFailed(step, error); // 그 차례에서 멈춤 · 이름째 알림(429 ⑨)
|
||||
}
|
||||
// 노선이 바뀌면 화면 몫 일(옛 노선 판 목록 · 계획고 판)은 건너뛰고 기본 창구만 새 노선으로.
|
||||
const own = routeSaved ? undefined : ctx.steps?.[step];
|
||||
const base = defaultStep(step, ctx.projectId, routeId, fromRouteId);
|
||||
const run =
|
||||
step === "pipes"
|
||||
? async () => {
|
||||
await base();
|
||||
await own?.();
|
||||
}
|
||||
: (own ?? base);
|
||||
const label = TOLERANT[step];
|
||||
await (label ? run().catch(reportOnly(label)) : run());
|
||||
}
|
||||
clearState("structure-type-cache", ctx.projectId); // 종류별 캐시는 저장 뒤 비움(54-32)
|
||||
saving.delete(ctx.projectId); // 다 나감 — 뒤처리는 새 상세로 그림
|
||||
|
||||
@@ -50,7 +50,7 @@ type Dep = StageName | `input.${InputKey}`;
|
||||
export const STAGE_DEPS: Record<StageName, Dep[]> = {
|
||||
route: ["input.route"],
|
||||
follow: ["route"],
|
||||
stations: ["route", "input.pipes", "input.structures"],
|
||||
stations: ["route", "input.pipes", "input.structures", "input.plusStations"],
|
||||
ground: ["stations"],
|
||||
planline: ["ground"],
|
||||
profile: ["planline", "input.alignment"],
|
||||
@@ -152,6 +152,7 @@ export function createDesignPipeline(options: { stages: PipelineStages }): Desig
|
||||
cross: null,
|
||||
pipes: null,
|
||||
structures: null,
|
||||
plusStations: null,
|
||||
};
|
||||
const inputVer: Record<InputKey, number> = {
|
||||
route: 0,
|
||||
@@ -159,6 +160,7 @@ export function createDesignPipeline(options: { stages: PipelineStages }): Desig
|
||||
cross: 0,
|
||||
pipes: 0,
|
||||
structures: 0,
|
||||
plusStations: 0,
|
||||
};
|
||||
const out = emptyOuts();
|
||||
const outVer = {} as Record<StageName, number>;
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
* 계산 묶음 본체 = `design_pipeline.ts`.
|
||||
*
|
||||
* 흐름 — 한 방향:
|
||||
* 입력 route(노선 초안) · alignment(계획고) · cross(횡단 입력) · pipes(관) · structures(구조물)
|
||||
* 입력 route(노선 초안) · alignment(계획고) · cross(횡단 입력) · pipes(관) · structures(구조물) ·
|
||||
* plusStations(추가 측점 · 429 ⑨)
|
||||
* ↓ update(출처, 값, 범위) — 조작마다 차례 번호(generation) +1 · 옛 계산 · 옛 요청 버림
|
||||
* 브라우저 즉시(차례대로 · 단계 사이마다 번호 확인 · 단계마다 결과 바로 내보냄)
|
||||
* route → follow → stations → ground → planline → profile → cross → footprint → masshaul
|
||||
@@ -24,7 +25,7 @@
|
||||
* | 단계 | 주인 | deps | 출력 |
|
||||
* | route | sub8 | input.route | RouteLine |
|
||||
* | follow | sub1 | route | FollowPatch(입력 고침) |
|
||||
* | stations | sub7 | route · input.pipes · input.structures | PipelineStation[](지반 null) |
|
||||
* | stations | sub7 | route · input.pipes · input.structures · input.plusStations | PipelineStation[](지반 null) |
|
||||
* | ground | sub8 | stations | GroundOutput |
|
||||
* | planline | sub1 | ground | AlignmentBase(첫 계획선) |
|
||||
* | profile | sub1 | planline · input.alignment | ProfileAlignment |
|
||||
@@ -89,12 +90,14 @@ export interface PipelineInputs {
|
||||
pipes: unknown;
|
||||
/** 구조물 목록(`StructureInstance[]`) — 위와 같음. */
|
||||
structures: unknown;
|
||||
/** 추가 측점(정수 m · 초안 ?? 저장분) — 바뀌면 stations 부터(429 ⑨) · 셈은 `plusStations()` 가 같은 값을 읽음. */
|
||||
plusStations?: ReadonlyArray<number> | null;
|
||||
}
|
||||
|
||||
export type InputKey = keyof PipelineInputs;
|
||||
|
||||
/** 조작 출처 — 노선 · 계획고 · 횡단 · 관 · 구조물. */
|
||||
export type ChangeSource = "route" | "grade" | "cross" | "pipe" | "structure";
|
||||
/** 조작 출처 — 노선 · 계획고 · 횡단 · 관 · 구조물 · 추가 측점. */
|
||||
export type ChangeSource = "route" | "grade" | "cross" | "pipe" | "structure" | "plus";
|
||||
|
||||
/** 출처 → 입력 칸. */
|
||||
export const SOURCE_INPUT: Record<ChangeSource, InputKey> = {
|
||||
@@ -103,6 +106,7 @@ export const SOURCE_INPUT: Record<ChangeSource, InputKey> = {
|
||||
cross: "cross",
|
||||
pipe: "pipes",
|
||||
structure: "structures",
|
||||
plus: "plusStations",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user