# 실제 제관 조립품 실전 테스트 — 부재 수를 늘려가며 요소 수·소요 시간 실측
import sys, json, time, glob, os
sys.path.insert(0, r'E:\도진팩토리\3D스캔및티칭시스템')
import numpy as np
import fea_server as fs

BASE = r'E:\도진팩토리\3D스캔및티칭시스템'
man = json.load(open(os.path.join(BASE, 'fab_models', '400q', 'manifest_400q.json'),
                    encoding='utf-8'))


def load_obj(path):
    V, F = [], []
    for ln in open(path, errors='replace'):
        p = ln.split()
        if not p:
            continue
        if p[0] == 'v':
            V.append([float(p[1]), float(p[2]), float(p[3])])
        elif p[0] == 'f':
            ix = [int(x.split('/')[0]) - 1 for x in p[1:]]
            for k in range(1, len(ix) - 1):
                F.append([ix[0], ix[k], ix[k + 1]])
    V = np.array(V)
    return V[np.array(F, dtype=int)].reshape(-1)


items = man['items']
print('제품 부재 %d개 중에서 서로 맞닿은 것들을 골라 조립품을 만든다\n' % len(items))

# 부재를 불러 바운딩박스가 서로 가까운 것들을 모은다
loaded = []
for it in items[:40]:
    fp = os.path.join(BASE, it['file'].replace('/', os.sep))
    if not os.path.isfile(fp):
        continue
    try:
        soup = load_obj(fp)
    except Exception:
        continue
    P = soup.reshape(-1, 3)
    loaded.append({'soup': soup, 'min': P.min(axis=0), 'max': P.max(axis=0),
                   'n': len(P), 'name': it['name']})
print('불러온 부재 %d개' % len(loaded))

# 첫 부재 기준으로 맞닿은(바운딩박스가 겹치거나 5mm 이내) 부재들을 모은다
base = loaded[0]
group = [base]
for o in loaded[1:]:
    gap = np.maximum(base['min'] - o['max'], o['min'] - base['max']).max()
    if gap < 5.0:
        group.append(o)
print('첫 부재와 맞닿은 부재 포함 → %d개\n' % len(group))

print('%-6s %-9s %-9s %-10s %-9s %-9s %-8s %s'
      % ('부재수', '정점', '부재인식', '요소(C3D10)', '절점', '용접접합', 'ccx', '전체'))
for k in (1, 2, 3, min(5, len(group)), min(8, len(group))):
    if k > len(group):
        break
    soup = np.concatenate([g['soup'] for g in group[:k]])
    t0 = time.time()
    try:
        r = fs.run_fea(soup, material='SS275', load_N=5000., weld_mode='bond',
                       part_sizes=[len(g['soup']) for g in group[:k]])
        tot = time.time() - t0
        m = r['mesh']
        print('%-6d %-9d %-9s %-10d %-9d %-9s %-8.1f %.1f초  %s'
              % (k, len(soup) // 3, m.get('parts', '-'), m['elements'], m['nodes'],
                 m.get('welds', 0), r['solve_sec'], tot,
                 '' if r['method'] == 'tet_c3d10' else '[외곽상자 후퇴]'))
        if k == min(8, len(group)):
            print('   최대응력 %.1f MPa (항복대비 %.0f%%) / 최대변위 %.3f mm'
                  % (r['result']['max_vonmises_MPa'], r['result']['yield_ratio_pct'],
                     r['result']['max_disp_mm']))
    except Exception as e:
        print('%-6d 실패: %s' % (k, str(e)[:110]))
