feat(b06,b08): 관 기준벽도 벽 몸 ∩ 성토 폐회로 겹침을 줄 사유로 — 다단에만 뜨면 「다단만 문제」로 읽힘(㉡)
서버 Node 가 기준벽 역할별 넓이를 design.wall_fill_overlap 에 남기고 B08 이 다단 목록 통(base 표시 칸)으로 읽어 기준벽 줄 비고에 붙임 · 호출부(Router_Material) 안 늘림 · 936be972 빈 저장: 관 9 곳 유출 벽 258.12 1.417 · 720 1.403 · 804.18 1.166 · 982.64 0.807 · 620 0.136 · 440 0.046 · 533.98 0.004 · 85.05/900 0㎡ · 이 프로젝트 기준벽은 모두 미확정(금액 밖)이라 비고 안 뜸 · 벽 칸을 적으면 「1.42㎡ × 10m ≈ 14.2㎥」 · 금액 변화 0 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
This commit is contained in:
@@ -246,8 +246,15 @@ async def _recompute(project_id: UUID | str, route_id: int) -> int:
|
|||||||
rows = output.get("areas")
|
rows = output.get("areas")
|
||||||
mass_haul = output.get("mass_haul")
|
mass_haul = output.get("mass_haul")
|
||||||
# 선 다단 벽 목록(④) — 목록째 얹음(수가 아니라 `_AREA_KEYS` 로는 못 거름). 주인 측점엔 빈 목록도.
|
# 선 다단 벽 목록(④) — 목록째 얹음(수가 아니라 `_AREA_KEYS` 로는 못 거름). 주인 측점엔 빈 목록도.
|
||||||
|
# 관 기준벽 벽 몸 겹침(㉡) — 역할별 ㎡ 한 벌. 빈 dict 도 실어 옛 값을 지움.
|
||||||
extra_walls = [
|
extra_walls = [
|
||||||
(float(row["chainage_m"]), {"extra_walls": row["extra_walls"]})
|
(
|
||||||
|
float(row["chainage_m"]),
|
||||||
|
{
|
||||||
|
"extra_walls": row["extra_walls"],
|
||||||
|
"wall_fill_overlap": row.get("wall_fill_overlap") or {},
|
||||||
|
},
|
||||||
|
)
|
||||||
for row in output.get("extra_walls") or []
|
for row in output.get("extra_walls") or []
|
||||||
if isinstance(row, dict) and isinstance(row.get("extra_walls"), list)
|
if isinstance(row, dict) and isinstance(row.get("extra_walls"), list)
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -251,16 +251,21 @@ export interface BuiltExtraWall {
|
|||||||
fill_overlap_m2: number | null;
|
fill_overlap_m2: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ExtraWallRow {
|
||||||
|
chainage_m: number;
|
||||||
|
extra_walls: BuiltExtraWall[];
|
||||||
|
wall_fill_overlap: Record<string, number>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 관(·독립 기슭막이) 주인 측점마다 **실제로 선** 다단 벽 목록(④, 2026-09-14).
|
* 관(·독립 기슭막이) 주인 측점마다 **실제로 선** 다단 벽 목록(④, 2026-09-14).
|
||||||
*
|
*
|
||||||
* 단 수·높이는 지형이 정해 요청보다 적게 설 수 있음 — 그래서 요청 칸(`extra_wall_counts`)이 아니라
|
* 단 수·높이는 지형이 정해 요청보다 적게 설 수 있음 — 그래서 요청 칸(`extra_wall_counts`)이 아니라
|
||||||
* 기하가 세운 결과를 남김(관 연장 `pipe_length_m` 과 같은 길). 주인 측점에는 빈 목록도 실어 옛 값을 지움.
|
* 기하가 세운 결과를 남김(관 연장 `pipe_length_m` 과 같은 길). 주인 측점에는 빈 목록도 실어 옛 값을 지움.
|
||||||
|
* `wall_fill_overlap` — 관 기준벽 역할별 벽 몸 ∩ 성토 폐회로(㎡). B08 기준벽 줄 비고.
|
||||||
*/
|
*/
|
||||||
export function extraWallRows(
|
export function extraWallRows(sections: readonly CrossSection[]): ExtraWallRow[] {
|
||||||
sections: readonly CrossSection[],
|
const rows: ExtraWallRow[] = [];
|
||||||
): Array<{ chainage_m: number; extra_walls: BuiltExtraWall[] }> {
|
|
||||||
const rows: Array<{ chainage_m: number; extra_walls: BuiltExtraWall[] }> = [];
|
|
||||||
for (const section of sections) {
|
for (const section of sections) {
|
||||||
if (!section.culvert) continue;
|
if (!section.culvert) continue;
|
||||||
const owner = pipeOwnerChainage(section, sections);
|
const owner = pipeOwnerChainage(section, sections);
|
||||||
@@ -293,7 +298,14 @@ export function extraWallRows(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
rows.push({ chainage_m: section.chainage_m, extra_walls: walls });
|
// 관 기준벽(유입·유출)도 같은 겹침 — 다단에만 사유가 뜨면 「다단만 문제」로 읽힘(㉡ 2026-09-14).
|
||||||
|
const wall_fill_overlap: Record<string, number> = {};
|
||||||
|
for (const wall of culvert.walls) {
|
||||||
|
if (areaInput && (wall.role === "inlet" || wall.role === "outlet")) {
|
||||||
|
wall_fill_overlap[wall.role] = Number(wallBodyInFillM2(wall.points, areaInput).toFixed(3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rows.push({ chainage_m: section.chainage_m, extra_walls: walls, wall_fill_overlap });
|
||||||
}
|
}
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -337,14 +337,27 @@ NOTE_FILL_OVERLAP = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#: 관 기준벽(유입·유출) 벽 몸 겹침 ㎡ — `design.wall_fill_overlap` {inlet, outlet}(서버 Node 가 잼, ㉡ 2026-09-14).
|
||||||
|
#: 호출부(`Router_Material`)를 안 늘리려고 다단 목록 통에 `base` 표시 칸으로 실어 옴 — 다단 줄은 거름.
|
||||||
|
WALL_OVERLAP_KEY = "wall_fill_overlap"
|
||||||
|
|
||||||
|
|
||||||
def extra_walls_from_designs(designs: list[dict[str, Any]] | None) -> dict[float, list[dict]]:
|
def extra_walls_from_designs(designs: list[dict[str, Any]] | None) -> dict[float, list[dict]]:
|
||||||
"""측점별 「선 다단 벽 목록」 — 목록이 있는 측점만(빈 목록도 담음 — 「다단 없음」)."""
|
"""측점별 「선 다단 벽 목록」 — 목록이 있는 측점만(빈 목록도 담음 — 「다단 없음」).
|
||||||
|
기준벽 겹침 값이 있으면 `{"base": 역할, "fill_overlap_m2": ㎡}` 칸을 뒤에 붙임."""
|
||||||
found: dict[float, list[dict]] = {}
|
found: dict[float, list[dict]] = {}
|
||||||
for row in designs or []:
|
for row in designs or []:
|
||||||
design = row.get("design") if isinstance(row, dict) else None
|
design = row.get("design") if isinstance(row, dict) else None
|
||||||
walls = design.get(EXTRA_WALLS_KEY) if isinstance(design, dict) else None
|
if not isinstance(design, dict):
|
||||||
if isinstance(walls, list):
|
continue
|
||||||
found[round(float(row.get("chainage_m") or 0.0), 3)] = walls
|
walls = design.get(EXTRA_WALLS_KEY)
|
||||||
|
overlaps = design.get(WALL_OVERLAP_KEY)
|
||||||
|
if not isinstance(walls, list) and not isinstance(overlaps, dict):
|
||||||
|
continue
|
||||||
|
entries = list(walls) if isinstance(walls, list) else []
|
||||||
|
if isinstance(overlaps, dict):
|
||||||
|
entries += [{"base": role, "fill_overlap_m2": area} for role, area in overlaps.items()]
|
||||||
|
found[round(float(row.get("chainage_m") or 0.0), 3)] = entries
|
||||||
return found
|
return found
|
||||||
|
|
||||||
|
|
||||||
@@ -438,6 +451,12 @@ def facility_structures(
|
|||||||
walls[0][3].append(NOTE_SIDE_UNKNOWN.format(side=side))
|
walls[0][3].append(NOTE_SIDE_UNKNOWN.format(side=side))
|
||||||
foundation = options.get("foundation" if legacy else "revet_foundation")
|
foundation = options.get("foundation" if legacy else "revet_foundation")
|
||||||
kept = {k: v for k, v in options.items() if not k.startswith(("inlet_", "outlet_"))}
|
kept = {k: v for k, v in options.items() if not k.startswith(("inlet_", "outlet_"))}
|
||||||
|
station_walls = _extra_walls_at(extra_walls, chainage)
|
||||||
|
base_overlap = {
|
||||||
|
str(entry["base"]): _num_or_zero(entry.get("fill_overlap_m2"))
|
||||||
|
for entry in station_walls
|
||||||
|
if entry.get("base")
|
||||||
|
}
|
||||||
for role, label, values, notes, withheld, filled in walls:
|
for role, label, values, notes, withheld, filled in walls:
|
||||||
before, after = _num_or_zero(values["before_m"]), _num_or_zero(values["after_m"])
|
before, after = _num_or_zero(values["before_m"]), _num_or_zero(values["after_m"])
|
||||||
row = child_row(base, "revetment", label, f"{role}_revet")
|
row = child_row(base, "revetment", label, f"{role}_revet")
|
||||||
@@ -445,6 +464,18 @@ def facility_structures(
|
|||||||
if legacy and _blank(values["height_m"]):
|
if legacy and _blank(values["height_m"]):
|
||||||
# 높이는 설계자 입력(계획홍수위 + 여유고) — 없으면 줄만 서고 금액 밖.
|
# 높이는 설계자 입력(계획홍수위 + 여유고) — 없으면 줄만 서고 금액 밖.
|
||||||
reasons.append(own_height_unconfirmed())
|
reasons.append(own_height_unconfirmed())
|
||||||
|
# 좌·우 한 벽으로 합친 독립 기슭막이는 그 측점에 선 벽 몸 전부.
|
||||||
|
overlap = (
|
||||||
|
sum(base_overlap.values())
|
||||||
|
if legacy and len(walls) == 1
|
||||||
|
else base_overlap.get(role, 0.0)
|
||||||
|
)
|
||||||
|
if overlap > 0 and not reasons: # 미확정 벽은 금액에 안 듦 — 두 번 셀 금액이 없음
|
||||||
|
notes.append(
|
||||||
|
NOTE_FILL_OVERLAP.format(
|
||||||
|
area=overlap, length=before + after, volume=overlap * (before + after)
|
||||||
|
)
|
||||||
|
)
|
||||||
row.update(
|
row.update(
|
||||||
start_m=chainage - before,
|
start_m=chainage - before,
|
||||||
end_m=chainage + after,
|
end_m=chainage + after,
|
||||||
@@ -458,7 +489,9 @@ def facility_structures(
|
|||||||
parents = {role: (values, filled) for role, _label, values, _notes, _w, filled in walls}
|
parents = {role: (values, filled) for role, _label, values, _notes, _w, filled in walls}
|
||||||
counts = {"outlet": 0, "basin": 0}
|
counts = {"outlet": 0, "basin": 0}
|
||||||
first_unset: dict[str, int] = {}
|
first_unset: dict[str, int] = {}
|
||||||
for tier in _extra_walls_at(extra_walls, chainage):
|
for tier in station_walls:
|
||||||
|
if tier.get("base"):
|
||||||
|
continue
|
||||||
side = "basin" if str(tier.get("side")) == "basin" else "outlet"
|
side = "basin" if str(tier.get("side")) == "basin" else "outlet"
|
||||||
counts[side] += 1
|
counts[side] += 1
|
||||||
if not tier.get("height_set"):
|
if not tier.get("height_set"):
|
||||||
|
|||||||
@@ -139,6 +139,26 @@ def test_서버가_벽_몸_겹침을_잰다() -> None:
|
|||||||
assert "wallBodyInFillM2(" in LAYOUTS and "fill_overlap_m2" in LAYOUTS
|
assert "wallBodyInFillM2(" in LAYOUTS and "fill_overlap_m2" in LAYOUTS
|
||||||
|
|
||||||
|
|
||||||
|
def test_관_기준벽도_겹침_사유가_뜬다() -> None:
|
||||||
|
"""2026-09-14 브레인 ㉡ — 같은 겹침이 관 기준벽에도 있음. 다단에만 뜨면 「다단만 문제」로 읽힘.
|
||||||
|
258.12 실측 유출 벽 몸 1.417㎡ 가 성토 폐회로 안."""
|
||||||
|
designs = [
|
||||||
|
{"chainage_m": 85.0, "design": {"extra_walls": [], "wall_fill_overlap": {"outlet": 1.417}}}
|
||||||
|
]
|
||||||
|
rows = facility_structures([POINT], extra_walls_from_designs(designs))
|
||||||
|
by_label = {
|
||||||
|
row["attachment_label"]: " ".join(row["notes"])
|
||||||
|
for row in rows
|
||||||
|
if row.get("notes") is not None
|
||||||
|
}
|
||||||
|
outlet = next(text for label, text in by_label.items() if "유출" in label)
|
||||||
|
inlet = next(text for label, text in by_label.items() if "유입" in label)
|
||||||
|
assert "성토 면적에도" in outlet and "1.42㎡" in outlet and "14.2㎥" in outlet
|
||||||
|
assert "성토 면적에도" not in inlet # 잰 값이 없는 벽엔 지어내지 않음
|
||||||
|
assert not _tiers(rows) # 기준벽 값이 다단 줄로 새지 않음
|
||||||
|
assert "wall_fill_overlap" in LAYOUTS and '"wall_fill_overlap"' in PREBUILD
|
||||||
|
|
||||||
|
|
||||||
def test_목록이_없으면_종전과_같다() -> None:
|
def test_목록이_없으면_종전과_같다() -> None:
|
||||||
assert not _tiers(facility_structures([POINT]))
|
assert not _tiers(facility_structures([POINT]))
|
||||||
assert not _tiers(facility_structures([POINT], {}))
|
assert not _tiers(facility_structures([POINT], {}))
|
||||||
|
|||||||
Reference in New Issue
Block a user