"""갈고리 직선 b 보정(R-17)과 Ramp-A P3-1 콘크리트 돌출 오류(M-9)를 엑셀에 반영한다 (2026-10-26). - 교량별 항목 합본 / 개별 항목: Ramp-A M-9 (P3-1 콘크리트 밖으로 튀어나감) 행 - 수정 내역 R-17, 수정 증거(마크별) 행, 미수정·보류 행, 수정 파일·해시(Ramp-A 수정5, 옥성 수정6 최신) 같은 R-17 이 이미 있으면 중단(이중 적용 방지). """ import copy import datetime import hashlib import json import sys from pathlib import Path import openpyxl from openpyxl.styles import PatternFill ROOT = Path(r"D:\★Pier 자료\프로그램\Pier_T") REV = ROOT / "검토파일" RES = ROOT / "Code" / "검토수정" / "결과" XLSX = REV / "4개교_공통사항_종합.xlsx" fx = json.loads((RES / "c1_hook_b_fix.json").read_text(encoding="utf-8")) pr = json.loads((RES / "c1_protrude_check.json").read_text(encoding="utf-8")) wb = openpyxl.load_workbook(XLSX) ws = wb["수정 내역"] if any(r[0].value == "R-17" for r in ws.iter_rows(min_row=4)): sys.exit("R-17 이 이미 있음 - 중단") GREEN = PatternFill("solid", fgColor="FFE2EFDA") ORANGE = PatternFill("solid", fgColor="FFF8CBAD") def append_like(sheet, values, like_row): r = sheet.max_row + 1 for j, v in enumerate(values, 1): c = sheet.cell(row=r, column=j, value=v) s = sheet.cell(row=like_row, column=j) c.font = copy.copy(s.font) c.fill = copy.copy(s.fill) c.border = copy.copy(s.border) c.alignment = copy.copy(s.alignment) c.number_format = s.number_format sheet.auto_filter.ref = "A3:%s%d" % (openpyxl.utils.get_column_letter(sheet.max_column), r) return r b4, a4 = pr["before(수정2)"]["bars"], pr["after(수정5)"]["bars"] p5, p3, p31 = fx["okseong"]["sample"]["P5"], fx["rampa"]["sample"]["P3"], fx["rampa"]["sample"]["P3-1"] n_p5, n_p3, n_p31 = fx["okseong"]["stat"]["P5_done"], fx["rampa"]["stat"]["P3_done"], fx["rampa"]["stat"]["P3-1_done"] CONTENT = ("Ramp-A교 P3-1(H16, 48본)이 기존 모델에서 콘크리트(기둥) 밖으로 튀어나가 있음 (사용자 지적 2026-10-26). " "측정(콘크리트 솔리드 기준): 기존 모델 P3-1 중심선이 기둥 표면에 놓여 중심 피복 최소 %.1f mm (< 철근 반지름 8 mm → 파이프가 표면 밖으로 돌출), P3 는 최소 %.1f mm (경미)" % (b4["P3-1"]["min_centre_cover_inside_mm"], b4["P3"]["min_centre_cover_inside_mm"])) DIFF = ("기존: P3-1 다리가 기둥 표면 위(중심 피복 0.1). b 보정(R-17) 후 수정5: P3-1 %d본 중 %d본의 갈고리 일부가 콘크리트 밖 (최대 %.0f mm 길이) — 악화" % (a4["P3-1"]["n"], a4["P3-1"]["bars_with_part_outside_concrete"], a4["P3-1"]["max_outside_length_mm"])) STATUS = ("미해결: 도면 치수(a 200, b 2,137, r135 c165 d150)대로 만들면 다리가 기둥 표면에 걸리고 갈고리가 밖으로 나감. " "기둥 단면(콘크리트 외곽)과 P3-1 배치 위치·방향을 도면에서 다시 확인해 위치를 조정해야 함 — 설계자 확인") # 합본 / 개별 wg = wb["교량별 항목 합본"] like = next(r[0].row for r in wg.iter_rows(min_row=4) if r[8].value == "도면↔모델") append_like(wg, ["Ramp-A교", "M-9", "P3-1", CONTENT, "콘크리트 솔리드 ↔ 철근 중심선 (샘플 15 mm 간격)", DIFF, "도면 상세 P3-1: r135 a200 b2,137 c165 d150 (갈고리 직선). 배치 높이·방향은 도면에 직접 표기 없음 [추론]", STATUS, "도면↔모델 (피복·돌출)", "개별"], like) wi = wb["개별 항목"] like_i = next(r[0].row for r in wi.iter_rows(min_row=4) if r[5].value == "도면↔모델") append_like(wi, ["Ramp-A교", "M-9", "P3-1", CONTENT, DIFF, "도면↔모델 (피복·돌출)", STATUS], like_i) # 수정 내역 r = append_like(ws, ["R-17", "옥성·Ramp-A", "갈고리 직선 b 보정 (R-15 후속)", "옥성 P5, Ramp-A P3·P3-1", "b = 꺾임 꼭짓점까지 잰 값: P5 %.1f / P3 %.1f / P3-1 %.1f (도면 b 보다 57.9 짧음 = R24×tan67.5°)" % (p5["b_before"], p3["b_before"], p31["b_before"]), "b = 다리선에서 호 시작점까지 (도면): P5 2,341 / P3 1,832 / P3-1 2,137. 중심선 길이 P5 %.1f / P3 %.1f / P3-1 %.1f" % (p5["len_after"], p3["len_after"], p31["len_after"]), n_p5 + n_p3 + n_p31, "사용자 질문 2026-10-26('P3, P3-1 형상 제대로 만들었니?') 점검에서 발견: 도면 b 는 다리선~호 시작점. 호 뒤쪽(호+끝)을 직선 방향으로 57.9 평행 이동", "옥성교P1_G5_수정6_20261026.3dm / ramp-A G5_수정5_20261026.3dm", "수정 후 재측정: a·b 가 도면과 일치(P5 160/2,341, P3 200/1,832, P3-1 200/2,137). 객체 수 동일", "P3-1 은 b 를 늘리자 갈고리가 콘크리트 밖으로 나감 (M-9, 미해결). P3·P5 는 콘크리트 안"], 4) ws.cell(row=r, column=1).fill = GREEN ev = wb["수정 증거(마크별)"] for m, br, s, n in (("P5", "옥성교", p5, n_p5), ("P3", "Ramp-A교", p3, n_p3), ("P3-1", "Ramp-A교", p31, n_p31)): append_like(ev, [br, m, "H16", "갈고리 직선 b 보정", n, "b %.1f / 길이 %.1f" % (s["b_before"], s["len_before"]), "b %.1f / 길이 %.1f" % (s["b_after"], s["len_after"]), round((s["len_after"] - s["len_before"]) * n, 1), "도면 b = 다리선~호 시작점. 호·끝을 +57.9 평행 이동"], 4) append_like(ev, ["Ramp-A교", "P3-1", "H16", "콘크리트 돌출 측정(기둥 솔리드)", 48, "중심 피복 최소 %.1f mm, 콘크리트 밖 0본" % b4["P3-1"]["min_centre_cover_inside_mm"], "수정5: 밖 %d본, 최대 %.0f mm" % (a4["P3-1"]["bars_with_part_outside_concrete"], a4["P3-1"]["max_outside_length_mm"]), None, "c1_protrude_check.py (콘크리트 솔리드 IsPointInside / ClosestPoint). 기존 = ramp-A G5_검토완료.3dm"], 4) hold = wb["미수정·보류"] append_like(hold, ["Ramp-A교", "P3-1 (H16)", "48본", "기존 모델에서 콘크리트 밖으로 튀어나감 (중심 피복 0.1 mm), 수정5 에서는 26본 갈고리가 밖으로 나감", "도면 치수(b 2,137, 갈고리)대로면 기둥 표면에 걸림. 배치 위치·방향 재확인 필요. 'M-9', '수정 내역' R-17", "도면에서 P3-1 배치와 기둥 단면 확인(설계자)", "M-9"], 4) fs = wb["수정 파일·해시"] def sha(p): h = hashlib.sha256() with open(p, "rb") as f: for blk in iter(lambda: f.read(1 << 20), b""): h.update(blk) return h.hexdigest() for row in fs.iter_rows(min_row=4): n = str(row[1].value).split("/")[-1] if n in ("ramp-A G5_수정3_20261026.3dm", "ramp-A G5_수정4_20261026.3dm", "옥성교P1_G5_수정5_20261026.3dm") and row[3].value == "최신": row[3].value = "중간본" row[3].fill = PatternFill(fill_type=None) for b, rel, what in (("Ramp-A교", "Ramp-A교P1/ramp-A G5_수정5_20261026.3dm", "P3·P3-1 b 보정 포함 (P3-1 은 콘크리트 돌출 미해결) — 전체 수정 포함"), ("옥성교", "검토_옥성교P1/옥성교P1_G5_수정6_20261026.3dm", "P5 b 보정 포함 — 전체 수정 포함(전단철근 수정3 제외)")): p = REV / rel st = p.stat() rr = append_like(fs, [b, "검토파일/" + rel, what, "최신", round(st.st_size / 1e6, 1), datetime.datetime.fromtimestamp(st.st_mtime).strftime("%Y-%m-%d %H:%M"), sha(p)], 4) fs.cell(row=rr, column=4).fill = GREEN wb.save(XLSX) print("saved")