refactor(511-B B-6 정리): 운영 부름 0 파이썬 짝 걷음 · 남는 짝 머리에 남는 까닭 · 등록부 until 걷음 · 알려진 차 까닭 · 322_3 flow 빨강 고침
걷은 함수(운영 부름 0 · 전수 확인 = 화면 · 서버 · 체인 · 추천 엔진 · B07 · 시험) - common_util/common_util_excavation.py 통째(126 줄 · pipe_trench_width_m · wall_trench_area_m2 · wall_trench_width_m · 서버 = Node excavation.trench) - Engine_Grade_Carry.carry_edits(Node carry_seat 겉) · Route_Save_Parts.seat_route_moved(시험 헬퍼 helper_475_1_moved 로 옮김) - Engine_Tilt.attach_underdrains · Engine_Structures_Wall.attach_wall_structures · load_wall_structures - Engine_Grade_Alignment.tangent_elevation(부름 0) · station_key.dedupe_station_keys · loose_chainage 줄 수 전 → 후: excavation.py 126 → 0 · Grade_Carry 178 → 157 · Save_Parts 289 → 269 · Wall 221 → 197 · Tilt 115 → 112 · station_key 141 → 122 · Alignment 657 → 654 남는 짝(머리 「남는 까닭」 한 줄) — Grade · Grade_Profile · Alignment · CurveLaw(2~4 순위 폴백 · 추천) · NodeEdits · SampleExtend · Router_Design · Ranges · Engine_Design · Areas · cross_berm(DTM 아닌 지표면 · Node 못 부를 때 · B07 · 추천) · Tilt · Wall · Culvert(B04 B07 B08) · route_follow · structure_station · station_key · route_polyline(745 줄 · 까닭 = 등록부 R1 줄) 등록부 — until 칸 걷음(G1 = py 표본기 남김 · 까닭 note) · C6 pair = attach_underdrain_specs · F8 pair null 알려진 차 — golden KNOWN 셋 그대로 나타남(R7 표본 출처 · W0 저장 때 경고 · X2 입력 선 자릿수 · 까닭 고침) · underdrain 알려진 차 까닭 · 511_b5 KNOWN_ARC 그대로 계산 자리 창 — 45 줄 · 서버만 아닌 41(바꿀 수 있음 40 + F9) · 서버만 넷(G3 D1 D2 N1 줄마다 까닭) · 옛 까닭 「짝 모으는 중 · B-4」 글 걷음 test_322_3_flow 빨강 — 헬퍼가 @config/config_frontend 흉내(511-A design_calc_server import) 시험 기준 — 걷은 짝 거울 = 걷기 전 파이썬 값 고정(excavation · station_key) · Node carry_seat · attach_underdrain_specs · wall_specs_from 검증: 고친 대상 시험 97 파일 562 통과 · 21 건너뜀 · typecheck 0 · 700 줄 넘는 새 파일 없음 — 요구 320-4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,7 +18,6 @@
|
||||
* ts — TS 셈 자리 `<경로>#<함수>` · 없으면 null
|
||||
* runner — 실행기: `node:<id>`(서버 Node 입구) · `py:<경로>#<함수>`(지금 파이썬) ·
|
||||
* `browser:<경로>#<함수>`(browser_only 만)
|
||||
* until — "472-4" = 파이썬 실행기는 472-4 짝 모으기 동안만(그 뒤 node:)
|
||||
* pair — 파이썬 짝 `<경로>#<함수>` · 짝 아니면 null
|
||||
* mirror — 거울 · 단위 시험(`resources/tester` 파일 이름 · .py 뺌)
|
||||
*
|
||||
@@ -41,7 +40,6 @@ export interface CalcEntry {
|
||||
stage: StageName | null;
|
||||
ts: string | null;
|
||||
runner: string;
|
||||
until?: "472-4";
|
||||
pair: string | null;
|
||||
mirror: string[];
|
||||
note?: string;
|
||||
@@ -208,10 +206,9 @@ export const CALC_REGISTRY: readonly CalcEntry[] = [
|
||||
stage: "ground",
|
||||
ts: `${U}common_util_surface_sampler.ts#heightAt`,
|
||||
runner: `py:${U}common_util_surface_sampler.py#build_surface_sampler`,
|
||||
until: "472-4",
|
||||
pair: `${U}common_util_surface_sampler.py#build_surface_sampler`,
|
||||
mirror: ["test_344_1_surface_sampler_mirror"],
|
||||
note: "X2 — 브라우저 격자 .bin · 서버 npz(값 협의)",
|
||||
mirror: ["test_344_1_surface_sampler_mirror", "test_511_b2_stations_node"],
|
||||
note: "서버 측점 생성(DTM) = Node `stations.sections` 격자 표본 · 파이썬 표본기 남김 = DTM 아닌 지표면 · 조작 중 「서버」 표본 창구 · B04 · B07 · 추천 · X2 = 브라우저 격자 .bin · 서버 npz(값 협의)",
|
||||
},
|
||||
{
|
||||
id: "ground.extend",
|
||||
@@ -453,9 +450,9 @@ export const CALC_REGISTRY: readonly CalcEntry[] = [
|
||||
stage: "cross",
|
||||
ts: `${U}common_util_structure_underdrain.ts#attachUnderdrainSpecs`,
|
||||
runner: "node:facility.attach",
|
||||
pair: `${B06}B06_Section_Engine_Tilt.py#attach_underdrains`,
|
||||
pair: `${B06}B06_Section_Engine_Tilt.py#attach_underdrain_specs`,
|
||||
mirror: ["test_472_4_underdrain_mirror"],
|
||||
note: "얹기 = GET 상세 Node facility.attach 안(511-B B-4) · 알려진 차 = 옛 underdrain 걷음 TS 만 · 기울임은 cross.design 안",
|
||||
note: "얹기 = GET 상세 Node facility.attach 안(511-B B-4) · 파이썬 짝 = Node 못 부를 때(`_attach_python`) · 알려진 차 = 옛 underdrain 걷음 TS 만 · 기울임은 cross.design 안",
|
||||
},
|
||||
{
|
||||
id: "cross.fit",
|
||||
|
||||
@@ -141,9 +141,9 @@ export const FACILITY_ROWS: readonly CalcEntry[] = [
|
||||
stage: null,
|
||||
ts: `${U}common_util_excavation.ts#wallTrenchAreaM2`,
|
||||
runner: "node:excavation.trench",
|
||||
pair: `${U}common_util_excavation.py#wall_trench_area_m2`,
|
||||
pair: null,
|
||||
mirror: ["test_excavation_mirror"],
|
||||
note: "파이썬 짝 운영 부름 0 — 걷음 후보(472-7)",
|
||||
note: "서버 = Node 한 벌 · 파이썬 짝 운영 부름 0 → 걷음(511-B B-6)",
|
||||
},
|
||||
{
|
||||
id: "facility.ford",
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
절·성토 균형은 계획고와 지반고 차이의 적분(A안)으로 판정한다. 횡단 설계가
|
||||
나온 뒤 실제 단면적 기반 계획선을 추가할 수 있도록 결과는 배열의 한 원소로
|
||||
반환한다.
|
||||
남는 까닭(511-B B-6) — 기준표 · 설계 속도 = 서버 종단 입력(페이지 설정 · 재생성 옵션) · 추천 엔진 ·
|
||||
4순위 폴백(`design_grade_line`).
|
||||
"""
|
||||
|
||||
import math
|
||||
|
||||
@@ -18,6 +18,8 @@ scipy에 의존하지 않는 순수 기하 계산만 두어 프론트엔드 구
|
||||
(거울 시험 `test_426_profile_straight.py`).
|
||||
경고 글 짝: `build_alignment` warnings ↔ TS `buildAlignment` warnings — 같은 목록
|
||||
(거울 시험 `test_472_save_warnings.py`).
|
||||
남는 까닭(511-B B-6) — 계획선 짓기 정본 = Node `profile.alignment` · 이 짝 = 2~4 순위 폴백 · 추천
|
||||
엔진(`pass_through`).
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
@@ -187,11 +189,6 @@ class AlignmentPolicy:
|
||||
}
|
||||
|
||||
|
||||
def tangent_elevation(pvi_s: np.ndarray, pvi_z: np.ndarray, targets: np.ndarray) -> np.ndarray:
|
||||
"""종단곡선을 빼고 변화점 직선만으로 본 표고(탄젠트 표고)."""
|
||||
return np.interp(targets, pvi_s, pvi_z)
|
||||
|
||||
|
||||
#: 반올림만 다른 같은 측점(m) — 관 측점 0.001m(자동설계) ↔ 0.01m(화면 저장) 차이.
|
||||
#: 화면 `SAME_STATION_SNAP_M`(B05_Profile_UI_Profile_Alignment.ts) 와 짝.
|
||||
SAME_STATION_SNAP_M = 0.006
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
「노선 그대로?」 · 범위.
|
||||
- 같은 자리(`line_matcher`) = 새 선 점을 옛 선에 투영 · 옆 `SAME_LINE_TOL_M` 안 · 방향 30° 안
|
||||
(측점 스냅이 뒤 교점을 밀어도 이어받음).
|
||||
- 자동 꼭짓점(`carry_base`) · 편집(`carry_edits`)은 **새 측점 위에** 다시 놓음 — 같은 자리면
|
||||
- 자동 꼭짓점(`carry_base`) · 편집(Node `carry_seat`)은 **새 측점 위에** 다시 놓음 — 같은 자리면
|
||||
옛 선 그 자리 높이 · 아니면 새 지반 첫 계획선 · 편집은 옛 사용자 꼭짓점이 닿은 자리만 ·
|
||||
R 은 가까운 측점. 편집 셈 = TS `carryEdits`(서버 Node · 511-B B-3) — 파이썬 짝 없음.
|
||||
|
||||
@@ -142,27 +142,6 @@ def carry_base(
|
||||
return out
|
||||
|
||||
|
||||
def carry_edits(
|
||||
old_xy: list[tuple[float, float]],
|
||||
new_xy: list[tuple[float, float]],
|
||||
old_base: list[dict[str, float]],
|
||||
edits: dict[str, Any] | None,
|
||||
stations: list[float],
|
||||
half_m: float,
|
||||
) -> dict[str, dict[str, float]]:
|
||||
"""새 편집 — 새 측점마다 같은 자리 · 옛 사용자 꼭짓점이 닿은 자리면 옛 계획선 − 옛 자동선.
|
||||
|
||||
닿은 자리 = 꼭짓점 위(그 꼭짓점이 사용자) · 앞뒤 꼭짓점 하나가 사용자. 종단곡선 R = 옛 키에서
|
||||
`half_m` 안 가장 가까운 같은 자리 측점. 셈 = TS `carryEdits` 한 벌(서버 Node `profile.carry` ·
|
||||
511-B B-3) — 노선 [저장]은 꼭짓점 세움과 한 부름(`carry_seat`).
|
||||
"""
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Node import carry_seat
|
||||
|
||||
if not old_base:
|
||||
return {"station_offsets": {}, "curve_radii": {}}
|
||||
return carry_seat(old_xy, new_xy, old_base, edits, stations, half_m)[0]
|
||||
|
||||
|
||||
def carry_policy(
|
||||
old_xy: list[tuple[float, float]],
|
||||
new_xy: list[tuple[float, float]],
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
경고만 · 비포장 대수차 5 % 이하(생략 가능)는 뺌 · 45 % 규칙으로 줄어든 곡선도 같은 잣대.
|
||||
|
||||
짝: TS `logic/B05_Profile_Profile_CurveLaw.ts` — 거울 시험 `test_432_1_curve_law.py`.
|
||||
남는 까닭(511-B B-6) — 종단곡선 법칙 정본 = Node `profile.alignment` 안 · 이 짝 = 추천 엔진 첫
|
||||
계획선 · 2~4 순위 폴백.
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
`B05_Profile/logic/B05_Profile_Grade_Ground.ts` `groundFollowingBasePvi` · 노선 바뀜 때 계획고 옮김은
|
||||
`B05_Profile_Engine_Grade_Carry.py` ↔ `logic/B05_Profile_Grade_Carry.ts` · 거울 시험 =
|
||||
`resources/tester/test_344_2_route_pairs.py`.
|
||||
남는 까닭(511-B B-6) — 1순위 첫 계획선 · 종단 다시 셈 = Node(`Engine_Grade_Node`) · 이 짝 = 2~4 순위
|
||||
폴백 · 추천 엔진 · 정본 꼴 입력.
|
||||
"""
|
||||
|
||||
from collections import Counter
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
곡선표 `curves` 는 **그린** R(접선 자리에 눌린 값)만 있어, 그것으로 다시 열면 눌린 R 이
|
||||
넣은 값으로 굳고 R · 길이 고정이 [저장] 뒤 사라짐 — 넣은 값을 따로 둠. 자리 = 저장 노드
|
||||
(앞 노드에서 0.5 m 안인 점은 `build_planned_polyline` 과 같이 거름).
|
||||
남는 까닭(511-B B-6) — 노선 저장 편집표 = Node `route.polyline` · 이 짝 = 단순화 갈래 · Node 못 돌림
|
||||
때.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -267,23 +267,3 @@ def route_moved_stations(
|
||||
for station in stations
|
||||
if station.get("structure") and match(float(station["chainage_m"])) is None
|
||||
)
|
||||
|
||||
|
||||
def seat_route_moved(
|
||||
alignment: dict[str, Any], born: Sequence[float], held: Sequence[float] = ()
|
||||
) -> list[dict[str, Any]]:
|
||||
"""옮긴 시설 측점(`born`)에 427 규칙으로 꼭짓점을 다시 세움(459 · 요구 270) — 그 자리
|
||||
옛 꼭짓점(옮겨 붙은 지반 · 옛 높이)을 걷고 원지반 지남 · 이웃 1 mm 맞춤 · 균형 종단이면
|
||||
옮기기 전 계획선 높이(`_seat_born_nodes`). `alignment` = 새 지반 표본 · 정책 · 옮겨 붙인
|
||||
`base_pvi` · `held` = 높이 고친 측점. TS 짝 = `seatRouteMoved`."""
|
||||
from B05_Profile.B05_Profile_Router_Confirm import _SAME_STATION_M, _seat_born_nodes
|
||||
|
||||
base = alignment.get("base_pvi") or []
|
||||
if not born:
|
||||
return base
|
||||
kept = [
|
||||
node
|
||||
for node in base
|
||||
if all(abs(float(node["chainage_m"]) - value) >= _SAME_STATION_M for value in born)
|
||||
]
|
||||
return _seat_born_nodes(alignment, kept, list(born), list(held))
|
||||
|
||||
@@ -182,8 +182,8 @@ export function carryBase(
|
||||
|
||||
/**
|
||||
* 새 편집 — 새 측점마다 같은 자리이고 옛 사용자 꼭짓점이 닿은 자리(꼭짓점 위 · 앞뒤 꼭짓점 하나가 사용자)면
|
||||
* 옛 계획선 높이 − 옛 자동선 높이 · 종단곡선 R = 옛 키에서 `halfM` 안 가장 가까운 같은 자리 측점(파이썬
|
||||
* `carry_edits` 짝). 새 자동 꼭짓점이 같은 자리에서 옛 자동선 높이(`carryBase`)라 계획고가 그대로.
|
||||
* 옛 계획선 높이 − 옛 자동선 높이 · 종단곡선 R = 옛 키에서 `halfM` 안 가장 가까운 같은 자리 측점(서버 =
|
||||
* Node `profile.carry` · 파이썬 짝 없음). 새 자동 꼭짓점이 같은 자리에서 옛 자동선 높이(`carryBase`)라 계획고가 그대로.
|
||||
*/
|
||||
export function carryEdits(
|
||||
oldXy: ReadonlyArray<XY>,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* planline — 노선 초안이 없으면 저장 종단 기준 그대로 · 있으면 새 측점마다 같은 자리는 저장 꼭짓점
|
||||
* 높이 · 바뀐 자리만 새 지반 첫 계획선(`Grade_Ground` · `carryBase`) · 옮긴 시설 측점(바뀐 자리 ·
|
||||
* 사용자가 옮김)에 427 꼭짓점 다시 세움(459 · 짝 = 파이썬 `B05_Profile_Route_Save_Parts`
|
||||
* `route_moved_stations` · `seat_route_moved` · 거울 시험 test_459_route_seat_mirror.py).
|
||||
* `route_moved_stations` · 서버 세움 = Node `profile.carry` · 거울 시험 test_459_route_seat_mirror.py).
|
||||
* profile — `buildAlignment`(첫 계획선 + 계획고 편집) — 종단 보기가 쓰는 같은 함수.
|
||||
*
|
||||
* 화면 · fetch · 세션 없음(순수) — 저장 기준 · 저장 노선은 부르는 쪽이 넣음.
|
||||
@@ -288,7 +288,7 @@ export function routeMovedStations(
|
||||
}
|
||||
|
||||
/** 옮긴 시설 측점(`born`)에 427 꼭짓점을 다시 세움 — 그 자리 꼭짓점을 걷고 `seatBornNodes`(원지반 지남 ·
|
||||
* 이웃 1 mm 맞춤 · 균형 종단이면 옮기기 전 계획선 높이). 파이썬 `seat_route_moved` 짝. */
|
||||
* 이웃 1 mm 맞춤 · 균형 종단이면 옮기기 전 계획선 높이). 서버 = Node `profile.carry` 의 seat(거울 기준 = 시험 헬퍼 `seat_route_moved`). */
|
||||
export function seatRouteMoved(
|
||||
base: Pick<AlignmentBase, "chainage" | "ground" | "policy" | "basePvi">,
|
||||
born: ReadonlyArray<number>,
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
`_Engine_Design.py`가 700줄을 넘겨, 「설계선을 어떻게 세우나」(그쪽)와 「그 선과 지반 사이
|
||||
넓이를 어떻게 재나」(여기)로 갈랐다. 두 함수 모두 지반선과 설계선의 **차이 배열**만 받으므로
|
||||
설계 로직을 전혀 모른다 — 그래서 따로 떼어 검산하기도 쉽다.
|
||||
남는 까닭(511-B B-6) — `compute_cross_design` 안 몫 — 그 짝과 같은 까닭(B07 · 추천 · DTM 아닌 지표면
|
||||
· Node 못 부를 때).
|
||||
"""
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ B05 배수유역도가 찍은 관 지점(`pipe_points.json` = 배관 정본)을
|
||||
|
||||
⚠ 아래 세 상수는 **임도 확정값이 아니다**. 지식DB에 임도 기준이 없어 교차 참조로
|
||||
채택했고(2026-08-19 사용자 확정), 임도 기준이 확보되면 그 값으로 교체한다.
|
||||
남는 까닭(511-B B-6) — B04 · B07 · B08 파이썬 소비자(관 벽 높이 · 세트).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
좌표 규약(generate_sections cad_exchange 준수): offset_m 양수=좌, 음수=우.
|
||||
경사비는 수평:수직 = ratio:1 (예: 1:1.2 → ratio=1.2).
|
||||
남는 까닭(511-B B-6) — [저장] · [확정] · 기본 설계 = Node `cross.design` · 이 짝 = B07 · 추천 엔진
|
||||
되풀이 · DTM 아닌 지표면 · Node 못 부를 때.
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
⚠ 계산식은 안 바뀐다 — 샘플(입력)만 넓어진다. 넓힌 샘플은 횡단 파일에 남겨 화면(TS)·Node·B08 이
|
||||
같은 지반을 본다(한 벌).
|
||||
⚠ 파이썬·TS 짝 — `extend_unclosed` ↔ `B05_Profile_Sections_Ground.ts`(B05 logic) `extendUnclosed`.
|
||||
남는 까닭(511-B B-6) — [저장] 넓힘 = DTM 이면 Node `cross.enforce` 안 · 이 짝 = DTM 아닌 지표면 ·
|
||||
Node 못 부를 때.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
**짝**: `common_util/common_util_structure_walls.ts` — 브라우저는 아직 저장하지 않은
|
||||
목록으로 같은 제원을 만들어야 해서 한 벌을 더 둔다. 거울 테스트
|
||||
`tmp/tests/test_b06_structure_walls_mirror.py` 가 같은 값이 나오는지 대조한다.
|
||||
남는 까닭(511-B B-6) — B07 단 추가(`tier_extras`) · 형상 얹기 · 소단 구간 Node 못 부를 때.
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -67,18 +68,6 @@ def tier_extras(type_id: str, options: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
]
|
||||
|
||||
|
||||
def load_wall_structures(project_root: Path) -> list[dict[str, Any]]:
|
||||
"""구조물 정본에서 벽(표 형상 wall · 322-1) 목록을 읽는다. 실패하면 빈 목록.
|
||||
|
||||
소단은 형상 berm — 사면을 계단으로 끊는 시설이라 벽 제원 자리에 안 얹음(계획서 3-9).
|
||||
"""
|
||||
try:
|
||||
return wall_specs_from(load_structures(str(project_root))[1])
|
||||
except Exception: # noqa: BLE001 — 정본을 못 읽어도 횡단 조회는 이어 간다
|
||||
logger.exception("B06 구조물 벽 정본을 읽지 못했습니다 (없는 것으로 본다)")
|
||||
return []
|
||||
|
||||
|
||||
def wall_specs_from(structures: list[Any]) -> list[dict[str, Any]]:
|
||||
"""구조물 목록 → 벽 제원(TS `wallSpecsFrom` 짝 · 형상 얹기 한 벌 323-4)."""
|
||||
types = structure_type_map()
|
||||
@@ -185,19 +174,6 @@ def berm_span_at(
|
||||
return None
|
||||
|
||||
|
||||
def attach_wall_structures(project_root: Path, cross_sections: list[dict[str, Any]]) -> int:
|
||||
"""구간 안 측점의 횡단 dict에 `revetment` 키를 얹는다. 얹은 개수를 돌려준다.
|
||||
|
||||
이미 관 정본이 얹은 세트(`culvert`·`revetment`)가 있는 측점은 **건드리지 않는다** —
|
||||
관 유입·유출 벽과 구조물 벽이 한 자리에 겹치면 어느 쪽 그림인지 읽히지 않는다.
|
||||
한 측점에 여러 개가 겹치면 먼저 시작한 것을 쓴다(겹침 정리는 사용자 몫).
|
||||
"""
|
||||
attached = attach_wall_specs(cross_sections, load_wall_structures(project_root))
|
||||
if attached:
|
||||
logger.info("B06 구조물 벽 %d개 측점에 얹음", attached)
|
||||
return attached
|
||||
|
||||
|
||||
def attach_wall_specs(cross_sections: list[dict[str, Any]], walls: list[dict[str, Any]]) -> int:
|
||||
"""구간 안 측점에 벽 제원을 얹는다(TS `attachWallSpecs` 짝). 얹은 개수를 돌려준다."""
|
||||
if not walls:
|
||||
|
||||
@@ -9,6 +9,8 @@ tilt_pct=…)`). 경사 = 물넘이 경사각도(%) · 맹암거 = 표준 횡단
|
||||
판정 폭 = 포장 범위와 같은 0.02 m(`paved_at`).
|
||||
짝(349-0n): `tilt_ranges` · `tilt_args` ↔ `B06_Section/logic/B06_Section_Cross_Enforce.ts`
|
||||
(거울 시험 `test_349_0n_cross_enforce_mirror.py`).
|
||||
남는 까닭(511-B B-6) — 얹기 = GET 형상 Node `facility.attach` · 이 짝 = Node 못 부를
|
||||
때(`_attach_python`) · 파이썬 횡단 설계 폴백 기울기.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -92,11 +94,6 @@ def tilt_args(chainage_m: float, ranges: list[TiltRange]) -> dict[str, Any]:
|
||||
return {"tilted": hit is not None, "tilt_pct": float(pct) if pct is not None else None}
|
||||
|
||||
|
||||
def attach_underdrains(project_root: Path, cross_sections: list[dict[str, Any]]) -> int:
|
||||
"""맹암거 기준 측점의 횡단 dict 에 `underdrain` 을 얹는다. 얹은 개수를 돌려준다."""
|
||||
return attach_underdrain_specs(cross_sections, load_underdrains(project_root))
|
||||
|
||||
|
||||
def attach_underdrain_specs(
|
||||
cross_sections: list[dict[str, Any]], drains: list[dict[str, Any]]
|
||||
) -> int:
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""B06 라우터의 횡단 설계 파일 읽기와 프리뷰 계산.
|
||||
|
||||
포장 · 세월교 구간 읽기 · 판정(짝 349-0n) = `B06_Section_Router_Design_Ranges.py` · 다시 내보냄.
|
||||
남는 까닭(511-B B-6) — [저장] 횡단 보정 = DTM 이면 Node `cross.enforce` · 이 파이썬 길(`enforce_*`)
|
||||
= DTM 아닌 지표면 · Node 못 부를 때 · 확정 승계 칸 읽기는 본 길.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
짝(349-0n): `pavement_ranges` · `ford_surface_drops` · `paved_at` · `ford_drop_at` ↔
|
||||
`B06_Section/logic/B06_Section_Cross_Enforce.ts` · 거울 시험 `test_349_0n_cross_enforce_mirror.py`.
|
||||
부르는 쪽은 `B06_Section_Router_Design` 이름으로 씀(다시 내보냄 · 시험이 그 이름을 갈아 끼움).
|
||||
남는 까닭(511-B B-6) — 포장 제안 · 세월교 하강 = B05 확정 본 길 · 구간 = 기본 설계 Node 못 부를
|
||||
때(`_python_facility`).
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
그래서 지식DB 에도 적지 않는다(사용자 지시).
|
||||
· 성토 소단(178-2) = 노면 끝 아래 깊이 `depth_m`(기본 1.0 · 사용자 값)에 **한 단만** ·
|
||||
0 미만은 0(도로보다 높을 수 없음). 간격은 성토에 안 씀 · 절토는 소단을 안 넣음.
|
||||
남는 까닭(511-B B-6) — `compute_cross_design` 안 몫 — 그 짝과 같은 까닭(B07 · 추천 · DTM 아닌 지표면
|
||||
· Node 못 부를 때).
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
"""터파기 단면 — 관·벽 두 갈래. **근거의 급이 다르므로 갈라 둔다.**
|
||||
|
||||
⚠⚠ TS 짝 파일과 **한 벌**이다 — `common_util/common_util_excavation.ts`.
|
||||
거울 시험: `resources/tester/test_excavation_mirror.py` (고칠 때 같이 돌릴 것).
|
||||
화면(B06 횡단도)이 그리고 서버(B08 수량)가 세는 값이라 두 쪽에 같은 수가 있어야 한다.
|
||||
|
||||
① 관 — **법정 시방서에 표가 있다**
|
||||
`KCS 44 40 10 배수관(23.01) §3.2(4) 그림 3.2-1`.
|
||||
「설계도서에 따로 표기되지 않았을 때에는 그림 3.2-1 터파기 기준 폭에 따라야 한다」
|
||||
⇒ 기본값으로 쓰고 사용자가 덮어쓰는 구조가 원문과 맞다.
|
||||
⚠ **관경 + 2b 로 계산하지 않는다** — 표가 관 두께·작업여유를 이미 담고 있다
|
||||
(Ø800 은 800+2×300=1400 이 아니라 **1600**).
|
||||
여유폭 b 는 같은 그림의 값: 보통지반 0.30 m · 연약지반 0.50 m(한쪽).
|
||||
|
||||
② 벽(돌쌓기·기슭막이) — **법정 근거 없음. 실무 정본 식이 있다**
|
||||
법령·교본은 「설계도서 지시 폭·기울기·깊이」까지만 정한다
|
||||
(지식DB `01_임도/02_상세설계/구조물/구조물_기초_토공.md` §1 — 전수 확인).
|
||||
실무 정본 `original/구조물도/기슭막이/04.구조도(기슭막이).xls` 7탭:
|
||||
|
||||
터파기 = 기초 0.5 × (0.7 + 0.2) × 1.0 = 0.45 (버림만 있는 탭은 0.1 × 0.7 = 0.07)
|
||||
+ 비탈 H × (평균두께 + 0.2) × 1.0
|
||||
|
||||
⇒ **여유폭은 「+0.2」**(평균두께에 더함), **기초는 깊이 0.5 · 폭 0.9**.
|
||||
⇒ 정본 탭 제목이 **「기초유 / 기초버림」**이다 — 기초를 안 두는 것이 아니라
|
||||
**버림으로 얕게 두는 것**이라 뜻이 다르다. 그 축이 곧 **0.45 냐 0.07 이냐**를 가른다.
|
||||
⚠ **비탈을 주지 않는다 — 정본이 수직으로 센다.** 기울기 항이 식에 아예 없다.
|
||||
임의로 기울기를 주면 그림과 물량이 어긋난다.
|
||||
|
||||
⚠ 값을 지어내지 않는다 — 여기 있는 수는 전부 위 두 출처의 것이고, 사용자가 덮어쓰면
|
||||
그 값이 이긴다. 화면에는 **근거의 급**(법정 표 / 실무 식)을 노티스로 드러낸다.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
#: 관경(㎜) → 터파기 폭(㎜). `KCS 44 40 10` 그림 3.2-1 표 그대로.
|
||||
PIPE_TRENCH_WIDTH_MM: dict[int, int] = {
|
||||
300: 700,
|
||||
350: 800,
|
||||
400: 900,
|
||||
450: 1000,
|
||||
500: 1100,
|
||||
600: 1200,
|
||||
700: 1400,
|
||||
800: 1600,
|
||||
900: 1700,
|
||||
1000: 1800,
|
||||
1100: 2000,
|
||||
1200: 2100,
|
||||
1300: 2200,
|
||||
1400: 2400,
|
||||
1500: 2500,
|
||||
}
|
||||
|
||||
#: 한쪽 여유폭(m) — 같은 그림. 표에 없는 관경을 사용자가 넣었을 때의 안내용이다.
|
||||
PIPE_TRENCH_CLEARANCE_NORMAL_M = 0.30
|
||||
PIPE_TRENCH_CLEARANCE_SOFT_M = 0.50
|
||||
|
||||
#: 벽 터파기 — 실무 정본 식의 수(위 문서 참조).
|
||||
WALL_TRENCH_CLEARANCE_M = 0.2 # 평균두께에 더하는 여유
|
||||
WALL_FOUNDATION_DEPTH_M = 0.5 # 기초 깊이
|
||||
WALL_FOUNDATION_WIDTH_M = 0.9 # 기초 폭 (0.7 + 0.2)
|
||||
WALL_BLINDING_DEPTH_M = 0.1 # 기초 없이 버림만 있을 때의 깊이
|
||||
WALL_BLINDING_WIDTH_M = 0.7 # 그때의 폭 (여유폭 0.0 — 버림 탭에는 여유가 없다)
|
||||
|
||||
#: 근거의 급 — 화면 노티스가 이 값을 그대로 쓴다.
|
||||
BASIS_PIPE = "법정 표(KCS 44 40 10 그림 3.2-1) — 설계도서에 따로 없으면 이 값"
|
||||
#: ⭐ 2026-09-09 **사용자 확정 5차 1번** — 「비탈 터파기를 셀 것인가」에 **「지금 이대로」**로 답이
|
||||
#: 왔다(기초 + 비탈 두 줄). 그래서 문구에서 **「확인 후 쓸 것」을 걷는다** — 확인이 끝났다.
|
||||
#: ⚠ 「법정 근거 없음」은 그대로 둔다(사실이 안 바뀜) — 근거의 급은 여전히 실무 정본이다.
|
||||
#: ⚠ 소광리 07-구조도는 **비탈 몫을 안 적는다**(설계사무소 간 차이) — 그래도 우리는 정본
|
||||
#: 04.구조도(기슭막이)의 두 줄을 따른다는 것이 이 확정의 뜻이다.
|
||||
BASIS_WALL = (
|
||||
"법정 근거 없음 · 실무 정본 식(구조도 기슭막이 xls) — 기초 + 비탈 두 줄."
|
||||
" **2026-09-09 사용자 확정 5차 1번으로 「지금 이대로」 확정**"
|
||||
)
|
||||
|
||||
|
||||
def pipe_trench_width_m(diameter_mm: float | int | None) -> float | None:
|
||||
"""관경(㎜)에 대응하는 터파기 폭(m). 표에 없으면 `None`(지어내지 않는다)."""
|
||||
if diameter_mm is None:
|
||||
return None
|
||||
try:
|
||||
key = int(round(float(diameter_mm)))
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
width = PIPE_TRENCH_WIDTH_MM.get(key)
|
||||
return None if width is None else width / 1000.0
|
||||
|
||||
|
||||
def wall_trench_area_m2(
|
||||
height_m: float | None,
|
||||
average_thickness_m: float | None,
|
||||
*,
|
||||
has_foundation: bool,
|
||||
) -> float | None:
|
||||
"""벽 터파기 단면적(㎡/m). 높이나 평균두께가 없으면 `None`.
|
||||
|
||||
실무 정본 식 그대로: `기초분 + H × (평균두께 + 0.2)`.
|
||||
`has_foundation=False` 면 기초분이 **버림 몫(0.1 × 0.7)**으로 줄어든다.
|
||||
"""
|
||||
if height_m is None or average_thickness_m is None:
|
||||
return None
|
||||
try:
|
||||
height = float(height_m)
|
||||
thickness = float(average_thickness_m)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
if height <= 0 or thickness <= 0:
|
||||
return None
|
||||
base = (
|
||||
WALL_FOUNDATION_DEPTH_M * WALL_FOUNDATION_WIDTH_M
|
||||
if has_foundation
|
||||
else WALL_BLINDING_DEPTH_M * WALL_BLINDING_WIDTH_M
|
||||
)
|
||||
return base + height * (thickness + WALL_TRENCH_CLEARANCE_M)
|
||||
|
||||
|
||||
def wall_trench_width_m(average_thickness_m: float | None) -> float | None:
|
||||
"""벽 터파기 폭(m) — 평균두께 + 여유 0.2. 값이 없으면 `None`."""
|
||||
if average_thickness_m is None:
|
||||
return None
|
||||
try:
|
||||
thickness = float(average_thickness_m)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
return None if thickness <= 0 else thickness + WALL_TRENCH_CLEARANCE_M
|
||||
@@ -2,9 +2,8 @@
|
||||
* common_util_excavation.ts
|
||||
* 터파기 단면 — 관·벽 두 갈래. **근거의 급이 다르므로 갈라 둔다.**
|
||||
*
|
||||
* ⚠⚠ 파이썬 짝과 **한 벌**이다 — `common_util/common_util_excavation.py`.
|
||||
* 거울 시험: `resources/tester/test_excavation_mirror.py`. 어느 쪽을 고치든 같이 돌릴 것.
|
||||
* 화면(B06 횡단도)이 그리고 서버(B08 수량)가 세는 값이라 두 쪽 수가 같아야 한다.
|
||||
* 한 벌 — 화면(B06 횡단도)이 그리고 서버(B08 수량 · Node `excavation.trench`)가 셈.
|
||||
* 파이썬 짝 = 운영 부름 0 이라 걷음(511-B B-6) · 시험 `resources/tester/test_excavation_mirror.py`.
|
||||
*
|
||||
* ① 관 — 법정 시방서에 표가 있다: `KCS 44 40 10 배수관(23.01) §3.2(4) 그림 3.2-1`.
|
||||
* 「설계도서에 따로 표기되지 않았을 때에는 … 기준 폭에 따라야 한다」
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
(관 — 파이썬 본체 = `common_util_drainage_pipes.project_pipe_points`) · `placeOnLine`(좌표 낡음
|
||||
판정) · 거울 시험 = `resources/tester/test_344_2_route_pairs.py` · `test_410_follow_xy.py` ·
|
||||
끌기 여러 걸음 화면 자리 = [저장] 한 번 자리(관 · 구조물 · 추가 측점) = `test_493_3_follow_screen_save.py`.
|
||||
남는 까닭(511-B B-6) — 따라감 = Node `facility.follow` · 좌표 맞춤 파이썬 짝 = Node 못 돌림 때 ·
|
||||
`route_outside` 는 본 길.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -4,19 +4,17 @@
|
||||
시종점 = ``end:BP`` · ``end:EP`` · id 를 모르는 구조물 측점 = ``at:<누가거리 0.01 m>`` ·
|
||||
곡선 측점(MC · 252-2) = ``curve:<누가거리 0.01 m>``.
|
||||
구간형 구조물 시작 · 끝 측점 = ``structure:<id>:start`` · ``:end``(기준 측점은 ``structure:<id>``).
|
||||
한 목록에 같은 열쇠가 둘이면 둘째부터 ``at:<누가거리>``(``dedupe_station_keys``).
|
||||
한 목록에 같은 열쇠가 둘이면 둘째부터 ``at:<누가거리>``(TS ``dedupeStationKeys``).
|
||||
MC 가 움직여 곡선 열쇠가 바뀌면 넘길 때 가까운 새 곡선 측점으로 이음(``curve_moves`` · 432-3).
|
||||
|
||||
⚠ 파이썬·TS 짝 — ``common_util_station_key.ts`` ``stationKey`` ↔ ``station_key``.
|
||||
남는 까닭 = DTM 아닌 지표면 · Node 못 돌림 때 노선 저장 이월 열쇠(``station_keys``).
|
||||
한쪽을 고치면 다른 쪽도 고치고 거울 시험 ``resources/tester/test_344_3_station_key_mirror.py``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import re
|
||||
|
||||
_LOOSE = re.compile(r"^-?\d+(\.\d+)?$")
|
||||
|
||||
|
||||
def _half_up(value: float) -> int:
|
||||
@@ -65,23 +63,6 @@ def station_key(
|
||||
return loose
|
||||
|
||||
|
||||
def dedupe_station_keys(keys: list[str], chainages: list[float]) -> list[str]:
|
||||
"""한 목록에 같은 열쇠가 둘이면 둘째부터 ``at:<누가거리>`` — 열쇠마다 측점 하나."""
|
||||
seen: set[str] = set()
|
||||
out: list[str] = []
|
||||
for key, chainage in zip(keys, chainages, strict=True):
|
||||
unique = f"at:{chainage_key(chainage)}" if key in seen else key
|
||||
seen.add(unique)
|
||||
out.append(unique)
|
||||
return out
|
||||
|
||||
|
||||
def loose_chainage(key: str) -> float | None:
|
||||
"""옛 초안 열쇠(누가거리 글) · ``at:`` 열쇠의 누가거리 — 측점 열쇠면 None."""
|
||||
text = key[3:] if key.startswith("at:") else key
|
||||
return float(text) if _LOOSE.match(text) else None
|
||||
|
||||
|
||||
#: 옮긴 곡선 측점으로 보는 거리(m) — 옛 MC 자리에서 이 안의 새 MC.
|
||||
CURVE_NEAR_M = 10.0
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ TS 짝 = `common_util/common_util_structure_station.ts`
|
||||
측점 계획 `plan_stations` ↔ `planStations` · 322-3 · 추가 측점 `plus` 368-2).
|
||||
거울 시험 = `resources/tester/test_234_1_structure_station.py`.
|
||||
일반 측점 = 격자(간격) · 시종점 — 끝(노선 길이)을 넘지 않게 자르는 데만 씀.
|
||||
남는 까닭(511-B B-6) — B07 · 배수(snap · 일반 측점) 파이썬 소비자 · 측점 생성 Node 못 돌림 때.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -69,6 +69,7 @@ function load(target) {
|
||||
cache.set(full, box.exports);
|
||||
const need = (spec) => {
|
||||
if (spec.startsWith("@ui/")) return { showToast: () => {} };
|
||||
if (spec === "@config/config_frontend") return { API_BASE_URL: "/api" };
|
||||
const alias = spec.startsWith("@util/") ? path.join(ROOT, "common_util", spec.slice(6)) : null;
|
||||
const base = alias ?? path.join(path.dirname(full), spec);
|
||||
const hit = [`${base}.ts`, base.replace(/\.js$/, ".ts"), base].find(
|
||||
|
||||
@@ -10,22 +10,43 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from collections.abc import Sequence
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import helper_472_1_paths as P
|
||||
|
||||
|
||||
def seat_route_moved(
|
||||
alignment: dict[str, Any], born: Sequence[float], held: Sequence[float] = ()
|
||||
) -> list[dict[str, Any]]:
|
||||
"""옮긴 시설 측점(`born`)에 427 규칙으로 꼭짓점을 다시 세움(459) — 서버 [저장] 길(Node
|
||||
`carry_seat` 의 seat) 앞뒤 그대로: 그 자리 옛 꼭짓점을 걷고 `_seat_born_nodes`.
|
||||
파이썬 짝(`Route_Save_Parts.seat_route_moved`) = 운영 부름 0 이라 걷음(511-B B-6) — 거울 기준만
|
||||
여기(`test_459_route_seat_mirror` · `test_475_1_route_digits`)."""
|
||||
from B05_Profile.B05_Profile_Router_Confirm import _SAME_STATION_M, _seat_born_nodes
|
||||
|
||||
base = alignment.get("base_pvi") or []
|
||||
if not born:
|
||||
return base
|
||||
kept = [
|
||||
node
|
||||
for node in base
|
||||
if all(abs(float(node["chainage_m"]) - value) >= _SAME_STATION_M for value in born)
|
||||
]
|
||||
return _seat_born_nodes(alignment, kept, list(born), list(held))
|
||||
|
||||
|
||||
def server_moved(root: Path, move: dict[str, Any]) -> dict[str, Any]:
|
||||
from B05_Profile.B05_Profile_Engine import _route_geojson
|
||||
from B05_Profile.B05_Profile_Engine_AsPlanned import _vertex_z
|
||||
from B05_Profile.B05_Profile_Engine_Grade import GradeDesignOptions
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Carry import (
|
||||
carry_base,
|
||||
carry_edits,
|
||||
carry_policy,
|
||||
changed_span,
|
||||
)
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Node import carry_seat
|
||||
from B05_Profile.B05_Profile_Engine_Sections import run_section_generation
|
||||
from B05_Profile.B05_Profile_Engine_Sections_Core import SectionGenerationOptions
|
||||
from B05_Profile.B05_Profile_Plus_Stations import save_plus_stations
|
||||
@@ -33,7 +54,6 @@ def server_moved(root: Path, move: dict[str, Any]) -> dict[str, Any]:
|
||||
carry_cross_inputs,
|
||||
follow_pipes,
|
||||
route_moved_stations,
|
||||
seat_route_moved,
|
||||
station_keys,
|
||||
vertices_of,
|
||||
)
|
||||
@@ -125,9 +145,9 @@ def server_moved(root: Path, move: dict[str, Any]) -> dict[str, Any]:
|
||||
stations = [float(r["chainage_m"]) for r in longitudinal.get("stations") or []]
|
||||
half = float((alignment.get("policy") or {}).get("station_interval_m") or 20.0) / 2
|
||||
base = carry_base(old_line, points, old_alignment["base_pvi"], alignment["base_pvi"])
|
||||
edits = carry_edits(
|
||||
edits = carry_seat(
|
||||
old_line, points, old_alignment["base_pvi"], old_alignment.get("edits"), stations, half
|
||||
)
|
||||
)[0]
|
||||
alignment["policy"] = carry_policy(old_line, points, old_alignment, alignment)
|
||||
alignment["base_pvi"] = base
|
||||
if span is not None:
|
||||
|
||||
@@ -77,7 +77,7 @@ def test_명세_저장_왕복(out):
|
||||
|
||||
|
||||
def test_서버_저장_뒤_벽_제원(tmp_path):
|
||||
from B05_Profile.B05_Profile_Structures_Repository import save_structures
|
||||
from B05_Profile.B05_Profile_Structures_Repository import load_structures, save_structures
|
||||
from B06_Section import B06_Section_Engine_Structures_Wall as wall
|
||||
|
||||
items = [
|
||||
@@ -99,7 +99,8 @@ def test_서버_저장_뒤_벽_제원(tmp_path):
|
||||
),
|
||||
]
|
||||
save_structures(str(tmp_path), items, base_revision=0)
|
||||
got = {spec["type_id"]: spec for spec in wall.load_wall_structures(tmp_path)}
|
||||
stored = load_structures(str(tmp_path))[1]
|
||||
got = {spec["type_id"]: spec for spec in wall.wall_specs_from(stored)}
|
||||
assert got["soil_guard"]["tiers"] == 3
|
||||
assert got["soil_guard"]["extras"] == [
|
||||
{"form": "콘크리트", "height_m": 2},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
못박는 것
|
||||
- 288-1 맹암거 기준 측점 = 측구 없음 · 노면 = 표준 경사로 지대 낮은 쪽 한 경사
|
||||
(물넘이 286 · 287 길 — 서버 `tilt_ranges` · `tilt_args` · TS 짝 `tilted` · 암거 경사는
|
||||
노면에 안 감 371-3) · 제원 얹기 서버 `attach_underdrains` ↔ TS `attachUnderdrainSpecs`
|
||||
노면에 안 감 371-3) · 제원 얹기 서버 `attach_underdrain_specs` ↔ TS `attachUnderdrainSpecs`
|
||||
같은 값 · 형상 = 노면을 암거 높이만큼 아래로 옵셋 → 성토 쪽은 성토 사면과 만남 ·
|
||||
절토 쪽은 절토 사면을 늘여 만남 · 그 안 돌 무늬 · 높이 비면 안 그림.
|
||||
- 371-3 암거 경사 = 형상 하단 경계 — 높은 쪽 노면 끝 − 높이에서 암거 경사로 낮은 쪽으로 내림.
|
||||
@@ -97,7 +97,8 @@ def test_맹암거_자리_기울기_서버(project):
|
||||
|
||||
def test_제원_얹기_서버_TS_같은_값(project, ts):
|
||||
sections = [dict(s) for s in SECTIONS]
|
||||
assert tilt.attach_underdrains(project, sections) == 2 == ts["attached"]
|
||||
attached = tilt.attach_underdrain_specs(sections, tilt.load_underdrains(project))
|
||||
assert attached == 2 == ts["attached"]
|
||||
assert [s.get("underdrain") for s in sections] == ts["sections"]
|
||||
assert ts["sections"][1] == {
|
||||
"structure_id": "ud-1",
|
||||
|
||||
@@ -22,12 +22,12 @@ from pathlib import Path
|
||||
import pytest
|
||||
|
||||
from B05_Profile.B05_Profile_Engine_Sections import relabel_pipe_stations, resolve_extra_stations
|
||||
from B05_Profile.B05_Profile_Structures_Repository import structures_file_path
|
||||
from B05_Profile.B05_Profile_Structures_Repository import load_structures, structures_file_path
|
||||
from B05_Profile.B05_Profile_Structures_Schema import load_definition
|
||||
from B06_Section.B06_Section_Engine_Culvert import pipe_points_file
|
||||
from B06_Section.B06_Section_Engine_Culvert_Sets import attach_culvert_sets
|
||||
from B06_Section.B06_Section_Engine_Structures_Wall import attach_wall_structures
|
||||
from B06_Section.B06_Section_Engine_Tilt import attach_underdrains
|
||||
from B06_Section.B06_Section_Engine_Structures_Wall import attach_wall_specs, wall_specs_from
|
||||
from B06_Section.B06_Section_Engine_Tilt import attach_underdrain_specs, load_underdrains
|
||||
from common_util.common_util_drainage_pipes import parse_pipe_points
|
||||
from common_util.common_util_structure_pavement import attach_pavement_structures
|
||||
from M02_MasterTemplete.StructOptions.M02_SO_Options import with_visible
|
||||
@@ -139,9 +139,9 @@ def _python_row(sample: dict, root: Path, plain: dict) -> dict:
|
||||
sections.sort(key=lambda s: s["chainage_m"])
|
||||
attach_culvert_sets(root, sections)
|
||||
relabel_pipe_stations(root, sections)
|
||||
attach_wall_structures(root, sections)
|
||||
attach_wall_specs(sections, wall_specs_from(load_structures(str(root))[1]))
|
||||
attach_pavement_structures(root, sections)
|
||||
attach_underdrains(root, sections)
|
||||
attach_underdrain_specs(sections, load_underdrains(root))
|
||||
return {
|
||||
"station_py": [f"{label}@{at:.2f}" for at, label in extras],
|
||||
"cards_py": {
|
||||
|
||||
@@ -34,10 +34,10 @@ from B05_Profile.B05_Profile_Engine_Grade import resolve_grade_options # noqa:
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Alignment import resolve_pvi # noqa: E402
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Carry import ( # noqa: E402
|
||||
carry_base,
|
||||
carry_edits,
|
||||
changed_span,
|
||||
line_matcher,
|
||||
)
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Node import carry_seat # noqa: E402
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Profile import ( # noqa: E402
|
||||
design_ground_following_profile,
|
||||
)
|
||||
@@ -58,6 +58,11 @@ pytestmark = pytest.mark.skipif(
|
||||
# ── 입력 만들기 ───────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def carry_edits(*args):
|
||||
"""파이썬 짝 걷음(511-B B-6) — 서버 = Node `carry_seat` 의 편집 몫."""
|
||||
return carry_seat(*args)[0]
|
||||
|
||||
|
||||
def _longitudinal(total: float, *, wave: float, holes: tuple[int, ...] = ()) -> dict:
|
||||
samples = []
|
||||
for index in range(int(total) + 1):
|
||||
|
||||
@@ -14,12 +14,7 @@ PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from common_util.common_util_station_key import ( # noqa: E402
|
||||
chainage_key,
|
||||
dedupe_station_keys,
|
||||
loose_chainage,
|
||||
station_key,
|
||||
)
|
||||
from common_util.common_util_station_key import chainage_key, station_key # noqa: E402
|
||||
|
||||
TS = PROJECT_ROOT / "common_util" / "common_util_station_key.ts"
|
||||
TYPESCRIPT = PROJECT_ROOT / "config" / "node_modules" / "typescript"
|
||||
@@ -53,6 +48,9 @@ CASES = [
|
||||
]
|
||||
DEDUPE = (["grid:1", "structure:st-1", "grid:1", "structure:st-1"], [20.0, 25.0, 20.004, 25.006])
|
||||
LOOSE = ["85.05", "at:85.05", "grid:4", "pipe:pipe-s3", "-1.5", "end:BP"]
|
||||
# 파이썬 `loose_chainage` · `dedupe_station_keys` = 운영 부름 0 → 걷음(511-B B-6) · 걷기 전 값
|
||||
LOOSE_WANT = [85.05, 85.05, None, None, -1.5, None]
|
||||
DEDUPE_WANT = ["grid:1", "structure:st-1", "at:20.00", "at:25.01"]
|
||||
|
||||
SCRIPT = r"""
|
||||
const ts = require(process.argv[1]);
|
||||
@@ -92,8 +90,8 @@ def test_파이썬과_TS_열쇠_같음():
|
||||
out = json.loads(proc.stdout)
|
||||
assert out["keys"] == [station_key(*case) for case in CASES]
|
||||
assert out["chainage"] == [chainage_key(case[1]) for case in CASES]
|
||||
assert out["loose"] == [loose_chainage(key) for key in LOOSE]
|
||||
assert out["dedupe"] == dedupe_station_keys(*DEDUPE)
|
||||
assert out["loose"] == LOOSE_WANT
|
||||
assert out["dedupe"] == DEDUPE_WANT
|
||||
|
||||
|
||||
def test_열쇠_규칙():
|
||||
@@ -102,10 +100,8 @@ def test_열쇠_규칙():
|
||||
assert station_key("regular", 50.0, 20.0) == "grid:3"
|
||||
assert station_key("irregular", 85.054, 20.0, "pipe-s3", "pipe") == "pipe:pipe-s3"
|
||||
assert station_key("irregular", 85.054, 20.0) == "at:85.05"
|
||||
assert loose_chainage("85.05") == 85.05 and loose_chainage("grid:4") is None
|
||||
# 5파 — 구간형 시작 · 끝 · 기준은 그대로 · 원래 격자 번호 · 같은 열쇠 둘째는 at:
|
||||
assert station_key("structure", 30.0, 20.0, "st-9", role="start") == "structure:st-9:start"
|
||||
assert station_key("structure", 33.0, 20.0, "st-9") == "structure:st-9"
|
||||
assert station_key("grid", 41.3, 20.0, "2") == "grid:2"
|
||||
assert station_key("end", 99.0, 20.0) == "end:EP"
|
||||
assert dedupe_station_keys(*DEDUPE) == ["grid:1", "structure:st-1", "at:20.00", "at:25.01"]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
화면 planline 단계: 옛 = 저장 계획 폴리라인(`read_route_plan` planned) · 새 = `buildEditedPolyline`(노드
|
||||
초안) → `changedSpan` → `carryBase` · `carryEdits`. 서버 노선 저장: 옛 = 같은 계획 폴리라인
|
||||
CSV · 새 = `_write_planned_polyline` 이 쓴 CSV → `changed_span` → `carry_base` · `carry_edits`(400).
|
||||
CSV · 새 = `_write_planned_polyline` 이 쓴 CSV → `changed_span` → `carry_base` · Node `carry_seat`(400).
|
||||
두 길이 같은 구간 · 같은 계획고를 내야 함(풀린 노선 geojson 으로 가르면 점 간격이 달라
|
||||
앞머리가 일찍 갈렸음).
|
||||
"""
|
||||
@@ -26,9 +26,9 @@ if str(ROOT) not in sys.path:
|
||||
from B05_Profile import B05_Profile_Router_Replan as replan # noqa: E402
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Carry import ( # noqa: E402
|
||||
carry_base,
|
||||
carry_edits,
|
||||
changed_span,
|
||||
)
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Node import carry_seat # noqa: E402
|
||||
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not (ROOT / "config" / "node_modules" / "typescript").is_dir(),
|
||||
@@ -44,6 +44,11 @@ CURVE_ON = [True] * len(OLD_NODES)
|
||||
RADII: list[float | None] = [None] * len(OLD_NODES)
|
||||
|
||||
|
||||
def carry_edits(*args):
|
||||
"""파이썬 짝 걷음(511-B B-6) — 서버 = Node `carry_seat` 의 편집 몫."""
|
||||
return carry_seat(*args)[0]
|
||||
|
||||
|
||||
def _csv_line(path: Path, nodes) -> list[list[float]]:
|
||||
replan._write_planned_polyline(
|
||||
path, list(nodes), RADIUS_M, simplify=False, curve_flags=CURVE_ON, radii=RADII
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""400 평면 고친 뒤 종단 편집 이어받기 — 같은 자리(옆 1 m · 방향 30° 안)는 옛 계획고를 새 측점 위에.
|
||||
|
||||
거울(파이썬 = 서버 [저장] 정본 · TS = 브라우저 즉시 · 같은 값):
|
||||
`B05_Profile_Engine_Grade_Carry` line_matcher · carry_base · carry_edits ↔
|
||||
`B05_Profile_Engine_Grade_Carry` line_matcher · carry_base · Node carry_seat(편집 · 파이썬 carry_edits 걷음) ↔
|
||||
`logic/B05_Profile_Grade_Carry.ts` lineMatcher · carryBase · carryEdits.
|
||||
묶음 follow(`logic/B05_Profile_Pipeline_Grade.ts`) — 걸음 수와 무관(기준 한 벌에서 한 번) ·
|
||||
되돌린 노선 = 옛 편집.
|
||||
@@ -28,9 +28,9 @@ from B05_Profile.B05_Profile_Engine_Grade_Alignment import resolve_pvi # noqa:
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Carry import ( # noqa: E402
|
||||
SAME_LINE_TOL_M,
|
||||
carry_base,
|
||||
carry_edits,
|
||||
line_matcher,
|
||||
)
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Node import carry_seat # noqa: E402
|
||||
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not (ROOT / "config" / "node_modules" / "typescript").is_dir(),
|
||||
@@ -44,6 +44,11 @@ HALF = INTERVAL / 2
|
||||
# ── 입력 만들기 ───────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def carry_edits(*args):
|
||||
"""파이썬 짝 걷음(511-B B-6) — 서버 = Node `carry_seat` 의 편집 몫."""
|
||||
return carry_seat(*args)[0]
|
||||
|
||||
|
||||
def _dense(nodes: list[tuple[float, float]], step: float = 2.0) -> list[list[float]]:
|
||||
"""꺾은선을 `step` m 안팎 점으로(그린 노선처럼 정점이 많게)."""
|
||||
out = [list(nodes[0])]
|
||||
|
||||
@@ -27,6 +27,8 @@ ROOT = HERE.parents[1]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from helper_475_1_moved import seat_route_moved # noqa: E402
|
||||
|
||||
from B05_Profile.B05_Profile_Engine_Grade import GradeDesignOptions # noqa: E402
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Alignment import ( # noqa: E402
|
||||
AlignmentPolicy,
|
||||
@@ -42,10 +44,7 @@ from B05_Profile.B05_Profile_Plus_Stations import ( # noqa: E402
|
||||
plus_chainages,
|
||||
save_plus_stations,
|
||||
)
|
||||
from B05_Profile.B05_Profile_Route_Save_Parts import ( # noqa: E402
|
||||
route_moved_stations,
|
||||
seat_route_moved,
|
||||
)
|
||||
from B05_Profile.B05_Profile_Route_Save_Parts import route_moved_stations # noqa: E402
|
||||
from B05_Profile.B05_Profile_Router_Confirm import _seat_born_nodes # noqa: E402
|
||||
from common_util.common_util_drainage_pipes import PipePoint # noqa: E402
|
||||
from common_util.common_util_route_follow import route_outside # noqa: E402
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
면적(절 · 성 · 토사 · 암 · 측구) · 층따기 밑수 · 단면 모양 · 누가토량(측점별 · 끝값) ·
|
||||
관 길이 · 종단 경고 · 기울기 위반 수.
|
||||
- 두 길 사이 = 「알려진 차」(`KNOWN`)만 · 그 밖 차는 실패 · 안 나타난 알려진 차도 실패
|
||||
(목록에서 지움 — 472-4 가 짝을 하나 옮길 때마다 한 줄씩 지워 마지막에 빈 목록).
|
||||
(목록에서 지움). 짝 모으기(511-B) 끝 — 남은 줄은 계산 짝이 아닌 까닭(표본 출처 · 입력 선
|
||||
자릿수 · 저장 때 코드) · 줄마다 `why`.
|
||||
- 화면 참값 — 정본 누가토량 −17,570.70 ㎥ · 측점 57(465 기준값).
|
||||
이 고정 자료가 안 거치는 짝인데 값이 갈리는 자리 — X1 단면 모양 기본값(설계 없는 새 측점 없음) ·
|
||||
X3 면적 두 갈래(구조물 = 관 351 하나 · 두 길 다 Node TS 로 셈) · X4 · X6(노선 안 바뀜).
|
||||
@@ -58,7 +59,8 @@ KNOWN: dict[tuple[str, str], list[Known]] = {
|
||||
Known(
|
||||
"R7",
|
||||
{"fill_m2": 0.00011, "cumulative_m3": 0.011},
|
||||
"다시 뜬 표본 마지막 자리 — grid:37 성토 1e-4 ㎡ · 누가토량 0.01 ㎥ 넷(끝값 같음)",
|
||||
"지반 표본 출처(G1) — 정본 = 10-17 파이썬 표본 · 노선 저장 = Node 격자 표본 · "
|
||||
"grid:37 성토 1e-4 ㎡ · 누가토량 0.01 ㎥ 넷(끝값 같음 · 기준값 다시 뜸 안 함 B-2)",
|
||||
),
|
||||
Known(
|
||||
"W0",
|
||||
|
||||
@@ -12,7 +12,7 @@ Node 입구 = `A00_Common/design/logic/design_calc_node.ts`(`build:design-calc`
|
||||
- Node 입구 도달 집합 = logic · common_util · 자료만 + 「알려진 끌림」(`KNOWN_PULLS`) — 그 밖은
|
||||
실패 · 안 나타난 알려진 끌림도 실패(목록에서 지움 — 472-2 2-B · 472-4 가 하나씩 걷어 빈 목록).
|
||||
- Node 입구 값(고정 자료 `fixtures/golden_472` · 면적 · 다단 벽 · 누가토량 끝값 · 배분 두 벌 ·
|
||||
corridor 3D = corridor 묶음) · 터파기 = 파이썬 짝.
|
||||
corridor 3D = corridor 묶음) · 터파기 = 걷기 전 파이썬 값.
|
||||
|
||||
./venv/Scripts/python.exe -m pytest resources/tester/test_472_3_calc_registry.py -q
|
||||
"""
|
||||
@@ -328,9 +328,8 @@ def test_corridor_묶음과_같음() -> None:
|
||||
assert co and out["results"]["corridor.mesh"] == co
|
||||
|
||||
|
||||
def test_터파기_파이썬_짝과_같음() -> None:
|
||||
from common_util import common_util_excavation as py
|
||||
|
||||
def test_터파기_값() -> None:
|
||||
# 파이썬 짝 걷음(511-B B-6 · 운영 부름 0) — 기대값 = 걷기 전 파이썬 값
|
||||
pipes = [{"diameter_mm": d} for d in (300, 800, 1500, 250, None)]
|
||||
walls = [
|
||||
{"height_m": 2.0, "average_thickness_m": 0.5, "has_foundation": True},
|
||||
@@ -341,15 +340,11 @@ def test_터파기_파이썬_짝과_같음() -> None:
|
||||
{"run": ["excavation.trench"], "inputs": {"excavation": {"pipes": pipes, "walls": walls}}}
|
||||
)
|
||||
got = out["results"]["excavation.trench"]
|
||||
assert [p["width_m"] for p in got["pipes"]] == [
|
||||
py.pipe_trench_width_m(p["diameter_mm"]) for p in pipes
|
||||
]
|
||||
for row, wall in zip(got["walls"], walls, strict=True):
|
||||
assert row["width_m"] == py.wall_trench_width_m(wall["average_thickness_m"])
|
||||
want = py.wall_trench_area_m2(
|
||||
wall["height_m"], wall["average_thickness_m"], has_foundation=wall["has_foundation"]
|
||||
)
|
||||
assert row["area_m2"] == pytest.approx(want) if want is not None else row["area_m2"] is None
|
||||
assert [p["width_m"] for p in got["pipes"]] == [0.7, 1.6, 2.5, None, None]
|
||||
assert [w["width_m"] for w in got["walls"]] == pytest.approx([0.7, 0.55, 0.6], abs=1e-12)
|
||||
assert got["walls"][0]["area_m2"] == pytest.approx(1.85)
|
||||
assert got["walls"][1]["area_m2"] == pytest.approx(0.73)
|
||||
assert got["walls"][2]["area_m2"] is None
|
||||
|
||||
|
||||
def test_모르는_id_는_실패() -> None:
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
- 구조물 목록 → 제원(높이 · 경사 · 빈 값 · 0 · 음수 · 글 · 맹암거 아닌 종류) 두 쪽 같음.
|
||||
- 기준 측점(±0.02 m) 얹기 · 얹은 수 같음.
|
||||
- 알려진 차 하나 — 옛 `underdrain` 이 남은 측점: TS 는 걷음 · 파이썬은 둠
|
||||
(서버는 파일에서 새로 읽어 옛 값 없음 · Node 로 옮길 때 한 벌).
|
||||
(서버 GET = Node `facility.attach` = TS · 파이썬 짝은 Node 못 부를 때만 · 그 길은 정본 파일을
|
||||
새로 읽어 옛 값 없음).
|
||||
|
||||
./venv/Scripts/python.exe -m pytest resources/tester/test_472_4_underdrain_mirror.py -q
|
||||
"""
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
못박는 것
|
||||
- 단계 id 다섯(`stage.route` · `follow` · `stations` · `profile` · `cross`) = TS 표 =
|
||||
파이썬 `STAGE_IDS` · `available`(등록부 `node:`) 밖 · 창구(`run_calc_served`)가 받음.
|
||||
- 판 = 단계 줄(잠금 · 기본 both · 까닭 「짝 모으는 중 · B-n」 · stations = B-2 · route = B-5 가
|
||||
엶) · F9 = 폼 기본값 ·
|
||||
- 판 = 단계 줄(B-2 ~ B-5 가 엶 · 기본 both · 옛 까닭 「짝 모으는 중 · B-n」 = B-6 걷음) ·
|
||||
F9 = 폼 기본값 ·
|
||||
커밋한 판 그대로.
|
||||
- 고정 자료(`fixtures/golden_472`) — Node 단계가 저장 노선 한 바퀴를 돎
|
||||
(측점 = 저장 상세 측점 0.01 m).
|
||||
@@ -108,8 +108,7 @@ def test_판_다섯_줄_잠금_기본값_그대로():
|
||||
).stdout
|
||||
assert calc_sites.PATH.read_bytes().replace(b"\r\n", b"\n") == committed.replace(b"\r\n", b"\n")
|
||||
locale = (ROOT / "ui_template" / "ui_template_locale_m1.ts").read_text(encoding="utf-8")
|
||||
for n in (4,):
|
||||
assert f'B01_CalcSites_Why_pair_B{n}: ["짝 모으는 중 · B-{n}"' in locale
|
||||
assert "짝 모으는 중" not in locale # B-6 — 단계 줄 모두 엶 · 옛 까닭 글 걷음
|
||||
|
||||
|
||||
def test_페이지_빼기만_같은_맞춤_함수():
|
||||
|
||||
@@ -34,7 +34,6 @@ from B05_Profile.B05_Profile_Engine_Grade_Alignment import ( # noqa: E402
|
||||
AlignmentPolicy,
|
||||
build_alignment,
|
||||
)
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Carry import carry_edits # noqa: E402
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Node import carry_seat # noqa: E402
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Profile import ( # noqa: E402
|
||||
design_ground_following_profile,
|
||||
@@ -180,7 +179,7 @@ def test_저장_선형_없으면_ValueError() -> None:
|
||||
|
||||
def test_계획고_옮김_꼭짓점_세움_한_부름() -> None:
|
||||
"""노선 [저장] 몫 — 편집 옮김(`carryEdits`)과 꼭짓점 세움(`seatRouteMoved`)이 Node 한 부름 ·
|
||||
편집만 부르는 겉(`carry_edits`)과 같은 편집."""
|
||||
편집만 부른 값(seat 없음)과 같은 편집."""
|
||||
old_xy = [[0.0, 0.0], [200.0, 0.0], [400.0, 0.0]]
|
||||
new_xy = [[0.0, 0.0], [200.0, 0.0], [300.0, 60.0], [420.0, 60.0]]
|
||||
longitudinal = _longitudinal(0.4)
|
||||
@@ -196,12 +195,8 @@ def test_계획고_옮김_꼭짓점_세움_한_부름() -> None:
|
||||
"born": [251.2],
|
||||
}
|
||||
moved, seated = carry_seat(old_xy, new_xy, base, edits, stations, 10.0, seat)
|
||||
assert moved == carry_edits(old_xy, new_xy, base, edits, stations, 10.0)
|
||||
assert moved == carry_seat(old_xy, new_xy, base, edits, stations, 10.0)[0]
|
||||
assert set(moved["station_offsets"]) == {"60.000", "120.000"}
|
||||
assert moved["curve_radii"] == {"120.000": 400.0}
|
||||
assert all(isinstance(v, float) for v in moved["station_offsets"].values())
|
||||
assert any(abs(node["chainage_m"] - 251.2) < 1e-6 for node in seated)
|
||||
assert carry_edits(old_xy, new_xy, [], edits, stations, 10.0) == {
|
||||
"station_offsets": {},
|
||||
"curve_radii": {},
|
||||
}
|
||||
|
||||
@@ -82,7 +82,6 @@ writeFileSync(process.argv[3], JSON.stringify({ specs, sections }));
|
||||
def _python_result() -> dict:
|
||||
from B06_Section.B06_Section_Engine_Structures_Wall import (
|
||||
_wall_form,
|
||||
attach_wall_structures,
|
||||
tier_extras,
|
||||
)
|
||||
|
||||
@@ -126,7 +125,6 @@ def _python_result() -> dict:
|
||||
if spec["start_m"] - 0.02 <= section["chainage_m"] <= spec["end_m"] + 0.02:
|
||||
section["revetment"] = spec
|
||||
break
|
||||
assert callable(attach_wall_structures)
|
||||
return {"specs": specs, "sections": sections}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"""터파기 단면 — 파이썬·TS 짝이 같은 값을 내는지 (2026-09-09).
|
||||
"""터파기 단면 — TS 한 벌(`common_util_excavation.ts`)이 법정 표 · 실무 정본 값을 냄 (2026-09-09).
|
||||
|
||||
화면(B06 횡단도)이 그리고 서버(B08 수량)가 세는 값이라 두 쪽이 갈리면
|
||||
「그림은 이런데 수량은 저렇다」가 된다.
|
||||
화면(B06 횡단도)이 그리고 서버(B08 수량 · Node `excavation.trench`)가 세는 값.
|
||||
파이썬 짝(`common_util_excavation.py`)은 운영 부름 0 이라 걷음(511-B B-6) — 기대값 = 걷기 전
|
||||
파이썬 값 그대로.
|
||||
|
||||
⚠ 근거의 급이 다른 두 갈래를 함께 잠근다 —
|
||||
관은 **법정 표**(KCS 44 40 10 그림 3.2-1), 벽은 **실무 정본 식**(구조도 기슭막이 xls).
|
||||
@@ -12,24 +13,19 @@ from __future__ import annotations
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from common_util.common_util_excavation import ( # noqa: E402
|
||||
PIPE_TRENCH_WIDTH_MM,
|
||||
WALL_FOUNDATION_DEPTH_M,
|
||||
WALL_FOUNDATION_WIDTH_M,
|
||||
WALL_TRENCH_CLEARANCE_M,
|
||||
pipe_trench_width_m,
|
||||
wall_trench_area_m2,
|
||||
wall_trench_width_m,
|
||||
)
|
||||
# 걷기 전 파이썬 짝 값(511-B B-6)
|
||||
PIPE_TABLE = {
|
||||
300: 700, 350: 800, 400: 900, 450: 1000, 500: 1100, 600: 1200, 700: 1400, 800: 1600,
|
||||
900: 1700, 1000: 1800, 1100: 2000, 1200: 2100, 1300: 2200, 1400: 2400, 1500: 2500,
|
||||
} # fmt: skip
|
||||
WALL_FOUNDATION_DEPTH_M = 0.5
|
||||
WALL_FOUNDATION_WIDTH_M = 0.9
|
||||
WALL_TRENCH_CLEARANCE_M = 0.2
|
||||
|
||||
TSC = PROJECT_ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
|
||||
|
||||
@@ -37,6 +33,9 @@ _RUNNER = """
|
||||
import { writeFileSync } from "node:fs";
|
||||
import {
|
||||
PIPE_TRENCH_WIDTH_MM,
|
||||
WALL_FOUNDATION_DEPTH_M,
|
||||
WALL_FOUNDATION_WIDTH_M,
|
||||
WALL_TRENCH_CLEARANCE_M,
|
||||
pipeTrenchWidthM,
|
||||
wallTrenchAreaM2,
|
||||
wallTrenchWidthM,
|
||||
@@ -49,6 +48,8 @@ const out = {
|
||||
wallBlindingOnly: [1.0, 2.0, 3.0].map((h) => wallTrenchAreaM2(h, 0.7, false)),
|
||||
wallWidth: [0.7, 1.05, 0].map((t) => wallTrenchWidthM(t)),
|
||||
nulls: [pipeTrenchWidthM(null), wallTrenchAreaM2(null, 0.7, true), wallTrenchAreaM2(1, null, true)],
|
||||
practice: [1.0, 2.0, 3.0].map((h) => wallTrenchAreaM2(h, 0.83, true)),
|
||||
consts: [WALL_FOUNDATION_DEPTH_M, WALL_FOUNDATION_WIDTH_M, WALL_TRENCH_CLEARANCE_M],
|
||||
};
|
||||
writeFileSync(process.argv[2], JSON.stringify(out));
|
||||
"""
|
||||
@@ -92,50 +93,51 @@ def _ts_values(tmp_path: Path) -> dict:
|
||||
return json.loads(result.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
@pytest.mark.skipif(not TSC.is_file(), reason="프론트엔드 의존성 미설치")
|
||||
def test_두_쪽이_같은_값을_낸다(tmp_path: Path) -> None:
|
||||
ts = _ts_values(tmp_path)
|
||||
@pytest.fixture(scope="module")
|
||||
def ts(tmp_path_factory) -> dict:
|
||||
if not TSC.is_file():
|
||||
pytest.skip("프론트엔드 의존성 미설치")
|
||||
return _ts_values(tmp_path_factory.mktemp("excavation"))
|
||||
|
||||
assert {int(k): v for k, v in ts["table"].items()} == PIPE_TRENCH_WIDTH_MM
|
||||
assert ts["pipe"] == [pipe_trench_width_m(d) for d in (300, 800, 1000, 1200, 1500, 250, 1600)]
|
||||
assert ts["wallWithFoundation"] == [
|
||||
wall_trench_area_m2(h, 0.7, has_foundation=True) for h in (1.0, 2.0, 3.0)
|
||||
]
|
||||
assert ts["wallBlindingOnly"] == [
|
||||
wall_trench_area_m2(h, 0.7, has_foundation=False) for h in (1.0, 2.0, 3.0)
|
||||
]
|
||||
assert ts["wallWidth"] == [wall_trench_width_m(t) for t in (0.7, 1.05, 0)]
|
||||
|
||||
def test_걷기_전_파이썬_값과_같다(ts: dict) -> None:
|
||||
assert {int(k): v for k, v in ts["table"].items()} == PIPE_TABLE
|
||||
assert ts["pipe"] == [0.7, 1.6, 1.8, 2.1, 2.5, None, None]
|
||||
assert ts["wallWithFoundation"] == pytest.approx([1.35, 2.25, 3.15], abs=1e-12)
|
||||
assert ts["wallBlindingOnly"] == pytest.approx([0.97, 1.87, 2.77], abs=1e-12)
|
||||
assert ts["wallWidth"][:2] == pytest.approx([0.9, 1.25], abs=1e-12)
|
||||
assert ts["wallWidth"][2] is None
|
||||
assert ts["nulls"] == [None, None, None]
|
||||
assert ts["consts"] == [
|
||||
WALL_FOUNDATION_DEPTH_M,
|
||||
WALL_FOUNDATION_WIDTH_M,
|
||||
WALL_TRENCH_CLEARANCE_M,
|
||||
]
|
||||
|
||||
|
||||
def test_관은_표를_그대로_쓴다() -> None:
|
||||
"""⚠ 관경 + 2b 로 계산하지 않는다 — 표가 관 두께·작업여유를 담고 있다."""
|
||||
assert pipe_trench_width_m(800) == 1.6
|
||||
assert pipe_trench_width_m(800) != 0.8 + 2 * 0.3
|
||||
for diameter in (800, 1000, 1200, 1500):
|
||||
assert pipe_trench_width_m(diameter) is not None, diameter
|
||||
def test_관은_표를_그대로_쓴다(ts: dict) -> None:
|
||||
"""⚠ 관경 + 2b 로 계산하지 않는다 — 표가 관 두께·작업여유를 담고 있다(800 → 1.6)."""
|
||||
width_800 = ts["pipe"][1]
|
||||
assert width_800 == 1.6 and width_800 != 0.8 + 2 * 0.3
|
||||
assert all(width is not None for width in ts["pipe"][1:5]) # 800 · 1000 · 1200 · 1500
|
||||
|
||||
|
||||
def test_표에_없는_관경은_지어내지_않는다() -> None:
|
||||
assert pipe_trench_width_m(250) is None
|
||||
assert pipe_trench_width_m(1600) is None
|
||||
def test_표에_없는_관경은_지어내지_않는다(ts: dict) -> None:
|
||||
assert ts["pipe"][5:] == [None, None] # 250 · 1600
|
||||
|
||||
|
||||
def test_실무_정본_수치와_맞는다() -> None:
|
||||
def test_실무_정본_수치와_맞는다(ts: dict) -> None:
|
||||
"""xls 7탭: H=1.0 → 1.03+0.45 = 1.48 · H=2.0 → 2.51 · H=3.0 → 3.54 (평균두께 0.83)."""
|
||||
thickness = 0.83
|
||||
for height, expected in ((1.0, 1.48), (2.0, 2.51), (3.0, 3.54)):
|
||||
area = wall_trench_area_m2(height, thickness, has_foundation=True)
|
||||
assert area == pytest.approx(expected, abs=0.01), height
|
||||
for area, expected in zip(ts["practice"], (1.48, 2.51, 3.54), strict=True):
|
||||
assert area == pytest.approx(expected, abs=0.01)
|
||||
|
||||
|
||||
def test_기초_유무가_기초분을_가른다() -> None:
|
||||
with_base = wall_trench_area_m2(2.0, 0.7, has_foundation=True)
|
||||
without = wall_trench_area_m2(2.0, 0.7, has_foundation=False)
|
||||
def test_기초_유무가_기초분을_가른다(ts: dict) -> None:
|
||||
with_base, without = ts["wallWithFoundation"][1], ts["wallBlindingOnly"][1] # H 2.0 · 0.7
|
||||
assert with_base - without == pytest.approx(
|
||||
WALL_FOUNDATION_DEPTH_M * WALL_FOUNDATION_WIDTH_M - 0.1 * 0.7
|
||||
)
|
||||
|
||||
|
||||
def test_여유폭은_평균두께에_더한다() -> None:
|
||||
assert wall_trench_width_m(0.7) == pytest.approx(0.7 + WALL_TRENCH_CLEARANCE_M)
|
||||
def test_여유폭은_평균두께에_더한다(ts: dict) -> None:
|
||||
assert ts["wallWidth"][0] == pytest.approx(0.7 + WALL_TRENCH_CLEARANCE_M)
|
||||
|
||||
@@ -132,7 +132,6 @@ export const ui_locales_m1 = {
|
||||
"끄면 첫 계획선 · 계획선 짓기를 서버 Node 가 셈(조작 중 왕복)",
|
||||
"Off = server Node builds the first grade line · grade line (round trip while editing)",
|
||||
],
|
||||
B01_CalcSites_Why_pair_B4: ["짝 모으는 중 · B-4", "Merging pairs · B-4"],
|
||||
B01_CalcSites_Why_cross: [
|
||||
"끄면 횡단을 서버 Node 가 셈(잠잠 뒤 · 왕복) · DTM 아닌 지표면 = 브라우저",
|
||||
"Off = server Node calcs cross sections (after a pause · round trip) · non-DTM surface = browser",
|
||||
|
||||
Reference in New Issue
Block a user