Files
Aislo/E03_StabSection/E03_StabSection_Worker.py
T
eomsangdonandClaude Opus 5.5 ecd027f028 feat(532-3): E03 2D 측점 해석 길 · 화면 — 파생 저장본(측점 지문) · 진행률 · 임계면 · 등안전율 · 절편 자유물체도 · 평면 띠 · 3D 색칠
- 길: POST analyze2d(뒤에서 · 일꾼 프로세스 병렬 · 바뀐 측점만) · GET progress · result2d · section · corridor3d · PUT representative
- 입력 = E01 복사본(측점 · 횡단 · DTM 넓힘) + E02 지반 조건 · 엔진 = sub4 analyze_station · 벽 = 다음 일감(532-4)
- 화면: 측점 목록(조건 · 정렬 · N.G 만 · 대표 단면) · 횡단(지층 · 지하수선 · 임계면 · 후보 · 등안전율 지도 · 중심 격자 · 절편) · 결과 · 절편표 · 자유물체도 · 절편력 그래프 · 평면 띠 · 3D 보기
- 계산 = logic/E03_StabSection_Logic.ts · 시험 test_532_3_e03_section.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 20:35:36 +09:00

75 lines
2.9 KiB
Python

"""E03 측점 해석 일꾼 — 서버 밖 프로세스(PLAN 532-3).
`python -m E03_StabSection.E03_StabSection_Worker <job.json>` — 서버(`Router.run_all`)가 띄움.
측점마다 프로세스로 나눠 돌리고 끝날 때마다 파생 저장본을 쓰고 한 줄(JSON)을 찍음 → 서버 진행률.
서버 프로세스에서 바로 프로세스 풀을 열면 자식마다 `main.py` 를 다시 읽어 이 모듈을 입구로 둠.
job = {root, keep, kwargs, workers, items: [{fp, meta, layers, cross, design, ground}]}.
"""
from __future__ import annotations
import json
import logging
import sys
from concurrent.futures import ProcessPoolExecutor, as_completed
from pathlib import Path
from typing import Any, Callable
from common_util.common_util_derived_store import write_derived
from E03_StabSection.E03_StabSection_Engine_Station import analyze_station
KIND_STATION = "stab2d_station"
logger = logging.getLogger(__name__)
def _analyze(item: dict[str, Any], kwargs: dict[str, Any]) -> dict[str, Any]:
kw = {**kwargs, "layers": item["layers"], "ground": [tuple(p) for p in item["ground"]]}
return analyze_station(item["cross"], item["design"], **kw)
def _value(item: dict[str, Any], out: dict | None, error: str | None) -> dict[str, Any]:
return {
"meta": item["meta"],
"inputs": {"layers": item["layers"], "ground": item["ground"]},
"out": out,
"error": error,
}
def run_job(job: dict[str, Any], emit: Callable[[str], None]) -> None:
"""측점마다 해석 → 저장본 · emit(열쇠). 한 측점 실패는 그 줄에 까닭만 남기고 이어 감."""
root, keep, kwargs = Path(job["root"]), int(job["keep"]), job["kwargs"]
items = job["items"]
def save(item: dict[str, Any], out: dict | None, error: str | None) -> None:
write_derived(root, KIND_STATION, item["fp"], _value(item, out, error), keep=keep)
emit(item["meta"]["key"])
workers = max(1, min(int(job.get("workers") or 1), len(items)))
if workers == 1:
for item in items:
try:
save(item, _analyze(item, kwargs), None)
except Exception as error: # noqa: BLE001
logger.exception("E03 측점 해석 실패: %s", item["meta"]["key"])
save(item, None, str(error))
return
with ProcessPoolExecutor(max_workers=workers) as pool:
futures = {pool.submit(_analyze, item, kwargs): item for item in items}
for future in as_completed(futures):
item = futures[future]
try:
save(item, future.result(), None)
except Exception as error: # noqa: BLE001
logger.exception("E03 측점 해석 실패: %s", item["meta"]["key"])
save(item, None, str(error))
def main() -> None:
job = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
run_job(job, lambda key: print(json.dumps({"done": key}), flush=True))
if __name__ == "__main__":
main()