"""B08 운반 사이클시간(PLAN 253-2) — 건설품셈 8-2-8 덤프트럭 · 거리이정표 거리 → 운반 로직 입력. ⚠ 짝 = `B08_Quantity_Haul_Cycle.ts`(같은 식) — 한쪽을 고치면 다른 쪽도 · 거울 시험 `resources/tester/test_253_2_haul_cycle.py`. 조작 중은 TS 짝(「운반 단가」 모달)이 바로 셈 · B09 셈 때 서버가 이 파일로 다시 채움(정본). 식(거창 25 설계서 단가산출 1308~1347행 꼴 · 끝수 = 소수 둘째 자리 반올림) q = T / rt × f · n = q / (qs × k) · t1 = Cms × n / (60 × Es) t2 = (L1/V1 + L1/V2 + L2/V3 + L2/V4) × 60 · Cm = t1 + t2 + t3 + t4 + t5 + t6 L1 = 거리이정표 그 행 포장 계(2차 + 1차 + 포장) · L2 = 비포장 · 속도 0 = 그 항 0. 로직 입력 채움(이름으로) — `Cm` · `사이클시간` = Cm · `t2` = t2 · `운반거리` = L1 + L2 · `own` = 사용자가 덮은 칸(L1 · L2 · t1 · 로직 입력 이름) — 그 값이 이김. 기본값 = 거창 설계서(혼합골재 덤프 15톤) · 속도 · t 표 = master_data CC000054~058(화면이 고름). """ from __future__ import annotations import math from typing import Any DEFAULTS: dict[str, float] = { "L1": 0.0, "L2": 0.0, "V1": 30.0, "V2": 35.0, "V3": 10.0, "V4": 15.0, "T": 15.0, "rt": 1.7, "f": 1.0, "qs": 1.72, "k": 1.0, "Es": 0.6, "Cms": 37.4, "t3": 0.8, "t4": 0.42, "t5": 0.5, "t6": 1.5, } #: 로직 입력 이름 → 셈 결과 칸 FILL = {"Cm": "cm", "사이클시간": "cm", "t2": "t2", "운반거리": "L"} def r2(value: float) -> float: """소수 둘째 자리 반올림(0.5 올림 · TS `Math.round` 와 같음).""" return math.floor(value * 100 + 0.5) / 100 def _div(a: float, b: float) -> float: return a / b if b else 0.0 def _num(value: Any) -> float | None: if isinstance(value, bool) or not isinstance(value, (int, float)): return None return float(value) if math.isfinite(value) else None def cycle_time(inputs: dict[str, Any] | None) -> dict[str, float]: """사이클 입력(빈 칸 = 기본값 · `t1` 있으면 그 값) → {q, n, t1, t2, cm, L}.""" given = {k: _num(v) for k, v in (inputs or {}).items()} v = {k: given[k] if given.get(k) is not None else d for k, d in DEFAULTS.items()} q = r2(_div(v["T"], v["rt"]) * v["f"]) n = r2(_div(q, v["qs"] * v["k"])) t1 = given.get("t1") t1 = r2(_div(v["Cms"] * n, 60 * v["Es"])) if t1 is None else t1 l1, l2 = v["L1"], v["L2"] t2 = r2((_div(l1, v["V1"]) + _div(l1, v["V2"]) + _div(l2, v["V3"]) + _div(l2, v["V4"])) * 60) cm = r2(t1 + t2 + v["t3"] + v["t4"] + v["t5"] + v["t6"]) return { "q": q, "n": n, "t1": t1, "t2": t2, "cm": cm, "L": math.floor((l1 + l2) * 1000 + 0.5) / 1000, } def cycle_inputs(ref: dict[str, Any] | None, dist: dict[str, Any] | None) -> dict[str, Any]: """연결 `ref.cycle` + 거리이정표 거리(`dist{L1, L2}`) — 덮은 L1 · L2(`ref.own`)는 그대로.""" cycle = dict((ref or {}).get("cycle") or {}) own = set((ref or {}).get("own") or []) for name in ("L1", "L2"): if name not in own and _num((dist or {}).get(name)) is not None: cycle[name] = dist[name] if "t1" not in own: cycle.pop("t1", None) return cycle def fill_given( ref: dict[str, Any], names: list[str], dist: dict[str, Any] | None ) -> dict[str, Any]: """로직 입력값 다시 채움 — `names` = 그 로직 입력 이름 · 덮은 칸(`ref.own`)은 그대로.""" own = set(ref.get("own") or []) got = cycle_time(cycle_inputs(ref, dist)) given = dict(ref.get("given") or {}) for name, slot in FILL.items(): if name in names and name not in own: given[name] = got[slot] return given