"""완료조건 검증: 플랜지간극·와이어끝·노즐↔TCP mm / 토치끝 가시 구 0개 / CEO 홈자세 캡처."""
import asyncio, json
from playwright.async_api import async_playwright
URL='http://localhost:8090/DOZIKWORKS_OS_v5_locked.dc.html'
OUT=r'E:\도진팩토리\3D스캔및티칭시스템\shots\ceo_feedback'
HOME={"S":-0.7,"L":-59.4,"U":-44.4,"R":-1.0,"B":-92.9,"T":-1.1}   # CEO가 맞춘 홈
async def main():
    async with async_playwright() as p:
        br=await p.chromium.launch(channel='msedge',headless=True)
        pg=await br.new_page(viewport={'width':1500,'height':950})
        errs=[]; logs=[]
        pg.on('pageerror',lambda e: errs.append(str(e)))
        pg.on('console',lambda m: logs.append(m.text) if 'torch' in m.text else None)
        await pg.goto(URL)
        await pg.wait_for_function("window.__dzw && window.__dzw._torchMesh", timeout=15000)
        res=await pg.evaluate("""(HOME)=>{ const d=window.__dzw, T=d._T; const out={};
          if(d.setJoints) d.setJoints(HOME);   // CEO 홈자세
          const tt=d._truthTool; tt.updateMatrixWorld(true);
          const me=d._torchMesh; me.updateMatrixWorld(true);
          // 플랜지 간극: 모델 플랜지중심(-137,0,-522) 월드 → truthTool로컬 (0이어야)
          const flL=tt.worldToLocal(new T.Vector3(-137,0,-522).applyMatrix4(me.matrixWorld));
          out.flangeGap_mm=+flL.length().toFixed(2);
          // 와이어끝(모델원점) → 용접점(tcp+15)
          const stick=d.WIRE_STICKOUT_MM||15;
          const W=new T.Vector3(d.tcpOffset[0],d.tcpOffset[1],d.tcpOffset[2]+stick);
          const wtL=tt.worldToLocal(new T.Vector3(0,0,0).applyMatrix4(me.matrixWorld));
          out.wireTip_to_weld_mm=+wtL.distanceTo(W).toFixed(2);
          // 노즐끝↔TCP: 모델 노즐 근사 = 와이어끝에서 배럴 -15 (모델 z=-15). 월드로 → tcpOffset 비교
          const nozM=new T.Vector3(0,0,-stick); // 모델 배럴 -15 (노즐 exit)
          const nozL=tt.worldToLocal(nozM.applyMatrix4(me.matrixWorld));
          out.nozzle_to_TCP_mm=+nozL.distanceTo(new T.Vector3(d.tcpOffset[0],d.tcpOffset[1],d.tcpOffset[2])).toFixed(2);
          // 토치끝(tcpOffset±30mm) 부근 '가시 구' 카운트
          const tcp=new T.Vector3(d.tcpOffset[0],d.tcpOffset[1],d.tcpOffset[2]);
          let vis=[]; tt.traverse(o=>{ if(o.isMesh && o.geometry && o.geometry.type==='SphereGeometry' && o.visible){
            const pl=o.position.clone(); if(pl.distanceTo(tcp)<40) vis.push(o.name||'(sphere r'+ (o.geometry.parameters?o.geometry.parameters.radius:'?') +')'); } });
          out.visibleSpheresNearTip=vis;
          out.weldVisible=d._weldPoint?d._weldPoint.visible:null;
          out.tcpMarkerVisible=d._tcpMarker?d._tcpMarker.visible:null;
          out.arcDotVisible=d.arcDot?d.arcDot.visible:null;
          // 카메라: 토치끝 클로즈업
          const wtW=new T.Vector3(0,0,0).applyMatrix4(me.matrixWorld);
          if(d.orbit){ d.orbit.tgt.copy(wtW); d.orbit.r=0.32; d.orbit.theta=-0.7; d.orbit.phi=1.4; d.updateCam&&d.updateCam(); }
          return out;
        }""", HOME)
        await pg.wait_for_timeout(500)
        await pg.screenshot(path=OUT+r'\fix_sphere_flange_1.png')
        print(json.dumps(res,ensure_ascii=False,indent=1))
        print("errs:", errs if errs else "없음")
        print("logs:", logs[:2])
        await br.close()
asyncio.run(main())
