|
|
|
|
@ -15,7 +15,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =================================================
|
|
|
|
|
// 1. 탭 전환 로직
|
|
|
|
|
// 1. 탭 전환 로직 (상단 메인 탭)
|
|
|
|
|
// =================================================
|
|
|
|
|
const tabVideo = document.getElementById('tab-video');
|
|
|
|
|
const tabModels = document.getElementById('tab-models');
|
|
|
|
|
@ -41,6 +41,54 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =================================================
|
|
|
|
|
// [신규] 오른쪽 사이드바 (Summary / Log) 탭 로직
|
|
|
|
|
// =================================================
|
|
|
|
|
const btnSummary = document.getElementById('btn-summary');
|
|
|
|
|
const btnLog = document.getElementById('btn-log');
|
|
|
|
|
const summaryListEl = document.getElementById('summary-list');
|
|
|
|
|
const logListEl = document.getElementById('log-list');
|
|
|
|
|
|
|
|
|
|
if (btnSummary && btnLog) {
|
|
|
|
|
btnSummary.addEventListener('click', () => {
|
|
|
|
|
btnSummary.classList.add('active');
|
|
|
|
|
btnLog.classList.remove('active');
|
|
|
|
|
if(summaryListEl) summaryListEl.classList.remove('hidden');
|
|
|
|
|
if(logListEl) logListEl.classList.add('hidden');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnLog.addEventListener('click', () => {
|
|
|
|
|
btnLog.classList.add('active');
|
|
|
|
|
btnSummary.classList.remove('active');
|
|
|
|
|
if(summaryListEl) summaryListEl.classList.add('hidden');
|
|
|
|
|
if(logListEl) logListEl.classList.remove('hidden');
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 로그 업데이트 함수 (최신 50개 유지)
|
|
|
|
|
function updateLogPanel(textData) {
|
|
|
|
|
if (!logListEl) return;
|
|
|
|
|
|
|
|
|
|
const now = new Date();
|
|
|
|
|
const timeStr = now.toTimeString().split(' ')[0]; // HH:MM:SS
|
|
|
|
|
|
|
|
|
|
const row = document.createElement('div');
|
|
|
|
|
row.className = 'log-entry';
|
|
|
|
|
|
|
|
|
|
// JSON 문자열이 너무 길 수 있으므로 적절히 자르거나 그대로 표시
|
|
|
|
|
// 가독성을 위해 시간 + 데이터 표시
|
|
|
|
|
row.innerHTML = `<span class="log-time">[${timeStr}]</span> <span class="log-msg">${textData}</span>`;
|
|
|
|
|
|
|
|
|
|
// 최신 로그가 위로 오게 하거나 아래로 오게 할 수 있음 (여기선 위로 쌓음)
|
|
|
|
|
logListEl.prepend(row);
|
|
|
|
|
|
|
|
|
|
// 메모리 관리를 위해 50개 넘어가면 삭제
|
|
|
|
|
if (logListEl.children.length > 50) {
|
|
|
|
|
logListEl.removeChild(logListEl.lastChild);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// =================================================
|
|
|
|
|
// [신규] Mission UI 로직
|
|
|
|
|
// =================================================
|
|
|
|
|
@ -146,14 +194,11 @@ const filterBar = document.getElementById('filter-bar');
|
|
|
|
|
zoomInput.id = 'zoom-toggle';
|
|
|
|
|
zoomInput.checked = false; // 기본값: 선택 안 됨
|
|
|
|
|
|
|
|
|
|
// 줌 기능 이벤트 리스너 (필요 시 로직 구현)
|
|
|
|
|
zoomInput.addEventListener('change', (e) => {
|
|
|
|
|
if(e.target.checked) {
|
|
|
|
|
console.log("Zoom In Activated");
|
|
|
|
|
// 여기에 줌 확대 로직 추가
|
|
|
|
|
} else {
|
|
|
|
|
console.log("Zoom In Deactivated");
|
|
|
|
|
// 여기에 줌 해제 로직 추가
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -162,13 +207,11 @@ const filterBar = document.getElementById('filter-bar');
|
|
|
|
|
filterBar.appendChild(zoomLabel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 초기 필터 상태 반영
|
|
|
|
|
updateActiveFilters(classInputs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateActiveFilters(classInputs) {
|
|
|
|
|
activeClassFilters.clear();
|
|
|
|
|
// 체크된 항목들만 Set에 추가
|
|
|
|
|
classInputs.forEach(inp => {
|
|
|
|
|
if(inp.checked) activeClassFilters.add(inp.dataset.cls);
|
|
|
|
|
});
|
|
|
|
|
@ -200,7 +243,7 @@ const filterBar = document.getElementById('filter-bar');
|
|
|
|
|
const fpsDisplayEl = document.getElementById("fps-display");
|
|
|
|
|
const resolutionEl = document.getElementById("resolution-display");
|
|
|
|
|
const connMsgEl = document.getElementById("connection-msg");
|
|
|
|
|
const summaryListEl = document.getElementById("summary-list");
|
|
|
|
|
// summaryListEl은 상단에서 정의됨
|
|
|
|
|
let ctx = null;
|
|
|
|
|
|
|
|
|
|
if (canvasEl) ctx = canvasEl.getContext('2d');
|
|
|
|
|
@ -222,6 +265,9 @@ const filterBar = document.getElementById('filter-bar');
|
|
|
|
|
ws.onmessage = (event) => {
|
|
|
|
|
const data = event.data;
|
|
|
|
|
if (typeof data === "string") {
|
|
|
|
|
// [Log 기능 연결] 문자열(JSON) 데이터 수신 시 로그 패널에 출력
|
|
|
|
|
updateLogPanel(data);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const meta = JSON.parse(data);
|
|
|
|
|
if (meta.type === "frame") {
|
|
|
|
|
@ -274,7 +320,7 @@ const filterBar = document.getElementById('filter-bar');
|
|
|
|
|
displayClassName = LABEL_MAP[tagId].classes[clsId];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [수정] 필터링 로직 강화: Set에 없으면 절대 그리지 않음 (All 체크 로직과 연동)
|
|
|
|
|
// 필터링
|
|
|
|
|
if (!activeClassFilters.has(displayClassName)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
@ -500,5 +546,3 @@ const filterBar = document.getElementById('filter-bar');
|
|
|
|
|
|
|
|
|
|
loadModelList();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
//2025-12-04 15:41
|