feat(511-A ④ 이월 열기): carry = server 면 R9 측점 열쇠 넘김을 잠잠 뒤 서버(같은 carryKeyedStates)가 셈 → 브라우저 값과 대조 · 다르면 그 노선 칸을 서버 값으로 · F10 = 브라우저 부름 없음(판만) · 거울 시험 셋 — 요구 320-4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 12:48:15 +09:00
co-authored by Claude Opus 5.5
parent e71efdfeb5
commit 57c36a9d15
4 changed files with 69 additions and 6 deletions
+17 -6
View File
@@ -22,6 +22,7 @@ import { renameChainageKeys } from "./logic/B06_Section_Station_Swap";
import { afterAnySwap } from "./B06_Section_Swap_Bus";
import { pipeIdAt, pipeIdNear } from "../A00_Common/design/design_pipe_ids";
import { showToast } from "@ui/ui_template_elements";
import { calcChecked } from "../A00_Common/design/design_calc_server";
import { chainageKey, looseChainage, type StationKeyRole } from "@util/common_util_station_key";
import { inferStationInterval } from "@util/common_util_svg";
import type { CrossSection, SectionDetailResponse } from "./B06_Section_Api_Fetch";
@@ -253,12 +254,22 @@ export function carryStationDrafts(
projectId: string,
fromRouteId: number,
toRouteId: number,
{ quiet = false }: { quiet?: boolean } = {},
{ quiet = false, applyTo = toRouteId }: { quiet?: boolean; applyTo?: number } = {},
): number {
const { states, outside } = carryKeyedStates(
(name) => readRaw(name, projectId, fromRouteId),
lists.get(scopeOf(projectId, fromRouteId)),
lists.get(scopeOf(projectId, toRouteId)),
// 계산 자리 carry = server 면 잠잠 뒤 같은 입력 서버 셈 → 다르면 그 칸을 서버 값으로(applyTo · 511-A)
const read = Object.fromEntries(
KEYED_STATES.map((name) => [name, readRaw(name, projectId, fromRouteId)]),
) as Record<KeyedState, Raw>;
const { states, outside } = calcChecked(
"carry",
"stations.carry_keyed",
(from: typeof read, before?: Keyed[], after?: Keyed[]) =>
carryKeyedStates((name) => from[name], before, after),
[read, lists.get(scopeOf(projectId, fromRouteId)), lists.get(scopeOf(projectId, toRouteId))],
{
apply: (server) =>
KEYED_STATES.forEach((name) => writeState(name, server.states[name], projectId, applyTo)),
},
);
for (const name of KEYED_STATES) writeState(name, states[name], projectId, toRouteId);
if (outside.size && !quiet)
@@ -275,7 +286,7 @@ const NEXT_ROUTE = -1;
*/
export function carryStationKeys(projectId: string, routeId: number, next: Keyed[]): number {
lists.set(scopeOf(projectId, NEXT_ROUTE), next);
const outside = carryStationDrafts(projectId, routeId, NEXT_ROUTE);
const outside = carryStationDrafts(projectId, routeId, NEXT_ROUTE, { applyTo: routeId });
for (const name of KEYED_STATES) {
writeState(name, readRaw(name, projectId, NEXT_ROUTE), projectId, routeId);
writeState(name, null, projectId, NEXT_ROUTE);
+10
View File
@@ -0,0 +1,10 @@
/* PLAN 511-A 거울 — R9 브라우저 자리(`carryStationDrafts`)가 부르는 꼴 그대로(읽기 = 칸 이름 → 값 표). */
import {
carryKeyedStates,
type Keyed,
type KeyedState,
type Raw,
} from "../../B06_Section/logic/B06_Section_Route_Carry";
export const carryWith = (from: Record<KeyedState, Raw>, before?: Keyed[], after?: Keyed[]) =>
carryKeyedStates((name) => from[name], before, after);
@@ -71,3 +71,9 @@ def test_자리마다_판_열쇠_저장_길_안_부름():
assert "design_calc_server" not in _text(rel), rel
page = _text("A00_Common/design/design_pipeline_page.ts")
assert len(page.splitlines()) <= 699 and "design_calc_server" not in page
def test_이월_R9_자리_판_열쇠():
keys = _text("B06_Section/B06_Section_Station_Keys.ts")
assert '"carry",\n "stations.carry_keyed",' in keys
assert "carryStationDrafts(projectId, routeId, NEXT_ROUTE, { applyTo: routeId })" in keys
@@ -92,3 +92,39 @@ def test_quantity_F7_C4_E1_서버_브라우저_같음(tmp_path):
for index, case in enumerate(STRUCTURE_CASES):
got = _served(["cross.berm_spans"], {"berm": {"structures": case, "chainages": []}})
assert got["cross.berm_spans"]["spans"] == browser[2 + index], index
# ── ④ 이월 · 다시 얹기(R9 · F10 = 브라우저 부름 없음) ──────────────────────────────────
def test_carry_R9_서버_브라우저_같음(tmp_path):
seat = {"x": 10.0, "y": 5.0, "left": [0.0, 1.0]}
states = {
"crossdesign": {"grid:1": {"ditch_type": "U"}, "20.00": {"paved": True}},
"shoulderext": {"grid:2": 0.5, "@at": {"grid:2": 40.0}, "@seat": {"grid:2": seat}},
"bermdepth": {"at:55.00": 1.0},
"rockb": {"30.00": -2.5},
"cutslope": {},
}
before = [
{"key": "grid:1", "chainageM": 20.0, "seat": seat},
{"key": "grid:2", "chainageM": 40.0},
{"key": "end:EP", "chainageM": 60.0},
]
after = [
{"key": "grid:1", "chainageM": 22.0},
{"key": "grid:2", "chainageM": 41.0, "seat": seat},
{"key": "end:EP", "chainageM": 58.0},
]
empty = dict.fromkeys(states, {}) # 브라우저 읽기 = 빈 칸도 {}(readRaw)
cases = [[states, before, after], [states, before, None], [empty, None, after]]
browser = _browser(
tmp_path,
[
{"mod": "resources/tester/helper_511_a_carry.ts", "fn": "carryWith", "args": case}
for case in cases
],
)
for index, case in enumerate(cases):
assert _trial("stations.carry_keyed", case) == browser[index], index
assert browser[0]["states"]["crossdesign"] # 넘김이 실제로 일어남