diff --git a/web-ui/html/workshop-app.html b/web-ui/html/workshop-app.html
index 85963de..1590fb9 100644
--- a/web-ui/html/workshop-app.html
+++ b/web-ui/html/workshop-app.html
@@ -148,9 +148,12 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis
.rb-equip-dot.off{background:#ef4444;}
.rb-equip-refresh{margin-left:auto;background:none;border:none;cursor:pointer;font-size:12px;color:var(--muted);}
.rb-equip-refresh:hover{color:#f59e0b;}
-.rb-equip-frame-wrap{width:100%;aspect-ratio:16/9;background:#000;display:flex;align-items:center;justify-content:center;overflow:hidden;}
+.rb-equip-frame-wrap{width:100%;aspect-ratio:16/9;background:#000;display:flex;align-items:center;justify-content:center;overflow:hidden;position:relative;}
.rb-equip-frame-wrap img{width:100%;height:100%;object-fit:contain;}
.rb-equip-frame-wrap .rb-equip-placeholder{color:#666;font-size:11px;}
+.rb-equip-live-btn{position:absolute;right:8px;bottom:8px;background:rgba(0,0,0,.55);color:#fff;border:1px solid rgba(255,255,255,.3);border-radius:6px;padding:4px 10px;font-size:11px;cursor:pointer;z-index:2;}
+.rb-equip-live-btn:hover{background:rgba(245,158,11,.85);border-color:transparent;}
+.rb-equip-live-btn.on{background:rgba(239,68,68,.8);border-color:transparent;}
.rb-equip-status{padding:8px 12px;font-size:11px;color:var(--muted);line-height:1.7;}
.rb-equip-status b{color:var(--text);}
@@ -241,7 +244,12 @@ body{background:var(--bg);color:var(--text);font-family:system-ui,sans-serif;dis
๐จ๏ธ K2 ์ฝค๋ณด
-
๋ถ๋ฌ์ค๋ ์ค...
+
+
+
![]()
+
๋ถ๋ฌ์ค๋ ์ค...
+
+
-
@@ -578,6 +586,7 @@ function startEquipTimers(){
function stopEquipTimers(){
clearInterval(equipTimer);
equipTimer=null;
+ stopK2Live(); // ์ฅ๋น ํญ์ ๋ฒ์ด๋๋ฉด ๋ผ์ด๋ธ WebRTC ์ฐ๊ฒฐ๋ ๋ซ๋๋ค(์ ๋ณด๋ ๋์ ์์ ์ ์ฐ๊ธฐ)
}
function refreshMillWidget(){
@@ -653,6 +662,81 @@ function grabK2Frame(){
});
}
+// โโ K2 ๋ผ์ด๋ธ ๋ทฐ(2026-09-24) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+// โถ ๋ผ์ด๋ธ ๋ฒํผ์ผ๋ก ์นด๋ ์์์ ์์ WebRTC ์คํธ๋ฆฌ๋ฐ(k2-app.html์ ๊ฒ์ฆ๋ ์ฐ๊ฒฐ ๋ก์ง
+// ์ด์). ๋ผ์ด๋ธ ์ค์ 20์ด ์ค๋
์ท ๊ทธ๋ฉ์ ๋ฉ์ถ๊ณ (refreshK2Widget ์ฐธ์กฐ), ์ฅ๋น ํญ์
+// ๋ฒ์ด๋๋ฉด stopEquipTimers๊ฐ ์ฐ๊ฒฐ์ ๋ซ๋๋ค โ ์ ๋ณด๋ ๋์์ ์์์ ์ ์ด๋ค.
+let k2LivePC=null;
+function k2Note(msg){
+ const ph=document.getElementById('k2-placeholder');
+ const img=document.getElementById('k2-frame');
+ const video=document.getElementById('k2-live');
+ video.style.display='none';
+ img.style.display='none';
+ ph.style.display='block';
+ ph.textContent=msg;
+}
+function toggleK2Live(){
+ if(k2LivePC)stopK2Live();else startK2Live();
+}
+function startK2Live(){
+ if(k2LivePC)return;
+ const video=document.getElementById('k2-live');
+ const btn=document.getElementById('k2-live-btn');
+ btn.classList.add('on');btn.textContent='โน ๋ผ์ด๋ธ ์ข
๋ฃ';
+ const ph=document.getElementById('k2-placeholder');
+ ph.style.display='none';
+ // STUN์ ๋นผ๋ฉด(host candidate๋ง) ์ด ํ๋ฆฐํฐ์ ICE ์๋ต์ด ์ค์ ๋ก๋ ์ ์ด(DTLS failed,
+ // bytesReceived 0) โ k2-app.html์์ 2026-09-18 ํ์ธ๋ ํดํฌ๋ผ ๊ทธ๋๋ก ์ ์ง.
+ const pc=new RTCPeerConnection({iceServers:[{urls:'stun:stun.l.google.com:19302'}]});
+ k2LivePC=pc;
+ pc.ontrack=(ev)=>{
+ if(k2LivePC!==pc)return;
+ video.srcObject=ev.streams[0];
+ video.play().catch(()=>{});
+ ph.style.display='none';
+ };
+ pc.oniceconnectionstatechange=()=>{
+ if(k2LivePC!==pc)return;
+ const st=pc.iceConnectionState;
+ if(st==='failed'){
+ stopK2Live();
+ k2Note('๋ผ์ด๋ธ ์ฐ๊ฒฐ ์คํจ โ โถ ๋ผ์ด๋ธ๋ฅผ ๋ค์ ๋๋ฌ์ฃผ์ธ์');
+ }else if(st==='disconnected'){
+ k2Note('๋ผ์ด๋ธ ์ ํธ ๋๊น(์ผ์์ ) โ ๋ณต๊ตฌ๋๋ฉด ๊ทธ๋ ค์ง๋๋ค');
+ }
+ };
+ // ์คํผ๋ ICE gathering ์์ ์ข
๋ฃ(null candidate) ๋๋ 1.5์ด ์ค ๋น ๋ฅธ ์ชฝ์ ๋ณด๋ธ๋ค โ
+ // ์์ ์ข
๋ฃ๊ฐ ์ด ๋คํธ์ํฌ์์ 20์ด+ ๊ฑธ๋ฆฌ๋ k2-app.html์ ์ค์ธก ํดํฌ ๊ทธ๋๋ก.
+ let offerSent=false;
+ const sendOfferOnce=()=>{
+ if(offerSent||k2LivePC!==pc)return;
+ offerSent=true;
+ fetch('/api/k2/webrtc-signal',{method:'POST',headers:{'Content-Type':'text/plain',...authH()},body:btoa(JSON.stringify({type:'offer',sdp:pc.localDescription.sdp}))})
+ .then(r=>r.text()).then(txt=>{
+ const res=JSON.parse(atob(txt));
+ if(res.type==='answer'&&k2LivePC===pc)pc.setRemoteDescription(new RTCSessionDescription(res));
+ }).catch(()=>{if(k2LivePC===pc)stopK2Live();});
+ };
+ pc.onicecandidate=(ev)=>{if(ev.candidate===null)sendOfferOnce();};
+ pc.addTransceiver('video',{direction:'sendrecv'});
+ pc.createOffer().then(d=>pc.setLocalDescription(d)).then(()=>{setTimeout(sendOfferOnce,1500);}).catch(()=>{if(k2LivePC===pc)stopK2Live();});
+}
+function stopK2Live(){
+ if(!k2LivePC)return;
+ try{k2LivePC.close();}catch{}
+ k2LivePC=null;
+ const video=document.getElementById('k2-live');
+ try{video.pause();video.srcObject=null;}catch{}
+ video.style.display='none';
+ const btn=document.getElementById('k2-live-btn');
+ btn.classList.remove('on');btn.textContent='โถ ๋ผ์ด๋ธ';
+ // ์ค๋
์ท์ผ๋ก ์ฆ์ ๋ณต๊ท โ ๋จ, ์ฅ๋น ํญ์ ๋ฒ์ด๋๋ฉฐ stopEquipTimers๊ฐ ๋ถ๋ฅธ ๊ฒฝ์ฐ์
+ // ์ ๊ทธ๋๋ ๋๋ค(์ ๋ณด๋ ํญ์ ํ๋ ์ ๊ทธ๋ฉ์ ํธ๋ฆฌ๊ฑฐํ๋ ์
).
+ const pane=document.getElementById('pane-equip');
+ if(pane&&pane.classList.contains('active'))refreshK2Widget();
+}
+
function fmtK2Duration(sec){
const h=Math.floor(sec/3600),m=Math.floor((sec%3600)/60);
if(h>0)return h+'์๊ฐ '+m+'๋ถ';
@@ -663,10 +747,14 @@ const K2_STATE_LABEL={standby:'๋๊ธฐ์ค',printing:'์ถ๋ ฅ์ค',paused:'์ผ์์
async function refreshK2Widget(){
const img=document.getElementById('k2-frame');
const placeholder=document.getElementById('k2-placeholder');
- grabK2Frame().then(dataUrl=>{
- if(dataUrl){img.src=dataUrl;img.style.display='block';placeholder.style.display='none';}
- else if(img.style.display==='none'){placeholder.textContent='์นด๋ฉ๋ผ๋ฅผ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค';}
- });
+ // ๋ผ์ด๋ธ ์ฐ๊ฒฐ์ด ์ด์์์ผ๋ฉด ํ๋ ์ ๊ทธ๋ฉ(WebRTC ํํ์)์ ๊ฑด๋๋ด๋ค โ ๋น๋์ค๊ฐ ์ด๋ฏธ
+ // ์ค์๊ฐ์ผ๋ก ํ๋ฅด๊ณ ์๊ณ , ์งง์ ์ฐ๊ฒฐ์ ๋ ์ด๋ฉด ๋์ด ๊ฐ์ญํ๋ค. ์ํ ์กฐํ๋ ๊ณ์ํ๋ค.
+ if(!k2LivePC){
+ grabK2Frame().then(dataUrl=>{
+ if(dataUrl){img.src=dataUrl;img.style.display='block';placeholder.style.display='none';}
+ else if(img.style.display==='none'){placeholder.textContent='์นด๋ฉ๋ผ๋ฅผ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค';}
+ });
+ }
try{
const r=await fetch('/api/k2/status',{headers:authH()});
const d=await r.json();
@@ -684,6 +772,7 @@ async function refreshK2Widget(){
if(d.state==='printing'){
html+='
์งํ๋ฅ : '+d.progress+'% ยท ๊ฒฝ๊ณผ: '+fmtK2Duration(d.printDurationSec)+'';
}
+ if(k2LivePC)html+=' ยท โ ์ค์๊ฐ';
statusEl.innerHTML=html;
}catch{
document.getElementById('k2-status-text').textContent='์ํ ์กฐํ ์คํจ';