/** * RAG 검색 챗봇 장치 제어 JavaScript */ // 장치 제어 모듈 const DeviceControl = { // 장치 제어 상태 isConnected: false, isStatusChecked: false, isLoadingPrograms: false, programsList: [], // DOM 요소들 elements: { // 탭 및 섹션 deviceTab: null, deviceSection: null, // 연결 관련 deviceServerUrlInput: null, connectDeviceServerBtn: null, deviceConnectionStatus: null, // 기본 기능 deviceBasicFunctions: null, checkDeviceStatusBtn: null, deviceStatusResult: null, // 프로그램 실행 deviceProgramControl: null, getProgramsBtn: null, programsList: null, programSelectDropdown: null, executeProgramBtn: null, executeResult: null }, // 모듈 초기화 init: function() { console.log('장치 제어 모듈 초기화 중...'); // DOM 요소 참조 가져오기 this.initElements(); // 이벤트 리스너 등록 this.initEventListeners(); console.log('장치 제어 모듈 초기화 완료'); }, // DOM 요소 참조 초기화 initElements: function() { // 탭 및 섹션 this.elements.deviceTab = document.getElementById('deviceTab'); this.elements.deviceSection = document.getElementById('deviceSection'); // 연결 관련 this.elements.deviceServerUrlInput = document.getElementById('deviceServerUrlInput'); this.elements.connectDeviceServerBtn = document.getElementById('connectDeviceServerBtn'); this.elements.deviceConnectionStatus = document.getElementById('deviceConnectionStatus'); // 기본 기능 this.elements.deviceBasicFunctions = document.getElementById('deviceBasicFunctions'); this.elements.checkDeviceStatusBtn = document.getElementById('checkDeviceStatusBtn'); this.elements.deviceStatusResult = document.getElementById('deviceStatusResult'); // 프로그램 실행 this.elements.deviceProgramControl = document.getElementById('deviceProgramControl'); this.elements.getProgramsBtn = document.getElementById('getProgramsBtn'); this.elements.programsList = document.getElementById('programsList'); this.elements.programSelectDropdown = document.getElementById('programSelectDropdown'); this.elements.executeProgramBtn = document.getElementById('executeProgramBtn'); this.elements.executeResult = document.getElementById('executeResult'); console.log('장치 제어 DOM 요소 참조 초기화 완료'); }, // 이벤트 리스너 등록 initEventListeners: function() { // 탭 전환 if (this.elements.deviceTab) { this.elements.deviceTab.addEventListener('click', () => { console.log('장치 제어 탭 클릭'); this.switchToDeviceTab(); }); } // 서버 연결 if (this.elements.connectDeviceServerBtn) { this.elements.connectDeviceServerBtn.addEventListener('click', () => { console.log('장치 서버 연결 버튼 클릭'); this.connectServer(); }); } // 엔터 키로 연결 if (this.elements.deviceServerUrlInput) { this.elements.deviceServerUrlInput.addEventListener('keydown', (event) => { if (event.key === 'Enter') { console.log('장치 서버 URL 입력 필드에서 엔터 키 감지'); event.preventDefault(); this.connectServer(); } }); } // 장치 상태 확인 if (this.elements.checkDeviceStatusBtn) { this.elements.checkDeviceStatusBtn.addEventListener('click', () => { console.log('장치 상태 확인 버튼 클릭'); this.checkDeviceStatus(); }); } // 프로그램 목록 조회 if (this.elements.getProgramsBtn) { this.elements.getProgramsBtn.addEventListener('click', () => { console.log('프로그램 목록 새로고침 버튼 클릭'); this.loadProgramsList(); }); } // 프로그램 선택 변경 if (this.elements.programSelectDropdown) { this.elements.programSelectDropdown.addEventListener('change', (event) => { console.log(`프로그램 선택 변경: ${event.target.value}`); this.updateExecuteButton(); }); } // 프로그램 실행 if (this.elements.executeProgramBtn) { this.elements.executeProgramBtn.addEventListener('click', () => { const programId = this.elements.programSelectDropdown.value; console.log(`프로그램 실행 버튼 클릭, 선택된 ID: ${programId}`); this.executeProgram(programId); }); } console.log('장치 제어 이벤트 리스너 등록 완료'); }, // 장치 제어 탭으로 전환 switchToDeviceTab: function() { // 모든 탭과 탭 콘텐츠 비활성화 const tabs = document.querySelectorAll('.tab'); const tabContents = document.querySelectorAll('.tab-content'); tabs.forEach(tab => tab.classList.remove('active')); tabContents.forEach(content => content.classList.remove('active')); // 장치 제어 탭 활성화 this.elements.deviceTab.classList.add('active'); this.elements.deviceSection.classList.add('active'); console.log('장치 제어 탭으로 전환 완료'); }, // 서버 연결 함수 connectServer: async function() { // URL 가져오기 (입력된 것이 있으면 백업으로 사용) const inputUrl = this.elements.deviceServerUrlInput.value.trim(); // 연결 시도 중 UI 업데이트 this.elements.connectDeviceServerBtn.disabled = true; this.updateConnectionStatus('connecting', '환경변수에 저장된 서버로 연결 시도 중...'); try { console.log('환경변수에 저장된 장치 서버로 연결 시도'); // 백엔드 API 호출하여 서버 상태 확인 const response = await AppUtils.fetchWithTimeout('/api/device/status', { method: 'GET' }, 10000); // 10초 타임아웃 const data = await response.json(); if (response.ok && data.success) { // 연결 성공 console.log('환경변수 설정 장치 서버 연결 성공:', data); this.isConnected = true; this.updateConnectionStatus('connected', `서버 연결 성공! 상태: ${data.server_status || '정상'}`); // 기능 UI 활성화 this.elements.deviceBasicFunctions.classList.add('active'); this.elements.deviceProgramControl.classList.add('active'); // 장치 상태 자동 체크 this.checkDeviceStatus(); // 프로그램 목록 자동 로드 this.loadProgramsList(); // 시스템 알림 AppUtils.addSystemNotification(`장치 관리 서버 연결 성공! (환경변수 URL)`); } else { // 환경변수 URL 연결 실패, 입력된 URL로 시도 console.warn('환경변수 설정 장치 서버 연결 실패, 입력 URL로 재시도합니다:', data); // 입력 URL이 있는지 확인 if (!inputUrl) { console.error('입력된 URL이 없어 연결 실패'); this.isConnected = false; this.updateConnectionStatus('error', '환경변수 URL 연결 실패 및 입력된 URL이 없습니다. URL을 입력해주세요.'); return; } // 입력 URL로 재시도 this.updateConnectionStatus('connecting', `입력 URL(${inputUrl})로 연결 시도 중...`); console.log(`입력한 URL로 장치 서버 연결 시도: ${inputUrl}`); // 백엔드 API 호출 - 입력 URL 사용 const customUrlResponse = await AppUtils.fetchWithTimeout('/api/device/connect', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: inputUrl }) }, 10000); const customUrlData = await customUrlResponse.json(); if (customUrlResponse.ok && customUrlData.success) { // 입력 URL 연결 성공 console.log('입력 URL 장치 서버 연결 성공:', customUrlData); this.isConnected = true; this.updateConnectionStatus('connected', `서버 연결 성공! 상태: ${customUrlData.server_status || '정상'}`); // 기능 UI 활성화 this.elements.deviceBasicFunctions.classList.add('active'); this.elements.deviceProgramControl.classList.add('active'); // 장치 상태 자동 체크 this.checkDeviceStatus(); // 프로그램 목록 자동 로드 this.loadProgramsList(); // 시스템 알림 AppUtils.addSystemNotification(`장치 관리 서버 연결 성공! (${inputUrl})`); } else { // 입력 URL 연결 실패 console.error('입력 URL 장치 서버 연결 실패:', customUrlData); this.isConnected = false; this.updateConnectionStatus('error', `서버 연결 실패: ${customUrlData.error || '서버 응답 오류'}`); } } } catch (error) { // 예외 발생 console.error('서버 연결 중 오류 발생:', error); this.isConnected = false; // 환경변수 URL 연결 실패, 입력된 URL로 시도 if (inputUrl) { console.warn('환경변수 URL 연결 시 오류 발생, 입력 URL로 재시도합니다'); try { // 입력 URL로 재시도 this.updateConnectionStatus('connecting', `입력 URL(${inputUrl})로 연결 시도 중...`); console.log(`입력한 URL로 장치 서버 연결 시도: ${inputUrl}`); // 백엔드 API 호출 - 입력 URL 사용 const customUrlResponse = await AppUtils.fetchWithTimeout('/api/device/connect', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ url: inputUrl }) }, 10000); const customUrlData = await customUrlResponse.json(); if (customUrlResponse.ok && customUrlData.success) { // 입력 URL 연결 성공 console.log('입력 URL 장치 서버 연결 성공:', customUrlData); this.isConnected = true; this.updateConnectionStatus('connected', `서버 연결 성공! 상태: ${customUrlData.server_status || '정상'}`); // 기능 UI 활성화 this.elements.deviceBasicFunctions.classList.add('active'); this.elements.deviceProgramControl.classList.add('active'); // 장치 상태 자동 체크 this.checkDeviceStatus(); // 프로그램 목록 자동 로드 this.loadProgramsList(); // 시스템 알림 AppUtils.addSystemNotification(`장치 관리 서버 연결 성공! (${inputUrl})`); return; // 성공하면 여기서 종료 } else { // 입력 URL 연결 실패 console.error('입력 URL 장치 서버 연결 실패:', customUrlData); this.updateConnectionStatus('error', `서버 연결 실패: ${customUrlData.error || '서버 응답 오류'}`); } } catch (inputUrlError) { // 입력 URL로 재시도 중 오류 console.error('입력 URL로 재시도 중 오류 발생:', inputUrlError); if (inputUrlError.message.includes('시간이 초과')) { this.updateConnectionStatus('error', '서버 연결 시간 초과. 서버가 실행 중인지 확인해주세요.'); } else { this.updateConnectionStatus('error', `서버 연결 오류: ${inputUrlError.message}`); } } } else { // 텍스트박스에 URL이 없는 경우 if (error.message.includes('시간이 초과')) { this.updateConnectionStatus('error', '환경변수 URL 연결 시간 초과. URL을 입력하여 다시 시도해주세요.'); } else { this.updateConnectionStatus('error', `환경변수 URL 연결 오류. URL을 입력하여 다시 시도해주세요: ${error.message}`); } } } finally { // 버튼 다시 활성화 this.elements.connectDeviceServerBtn.disabled = false; } }, // 연결 상태 업데이트 updateConnectionStatus: function(status, message) { const statusElement = this.elements.deviceConnectionStatus; // 모든 상태 클래스 제거 statusElement.classList.remove('connected', 'disconnected', 'error', 'connecting'); // 상태에 따라 클래스 추가 statusElement.classList.add(status); // 메시지 업데이트 statusElement.textContent = message; console.log(`연결 상태 업데이트: ${status} - ${message}`); }, // 장치 상태 확인 checkDeviceStatus: async function() { if (!this.isConnected) { this.elements.deviceStatusResult.value = '오류: 먼저 서버에 연결해야 합니다.'; console.error('장치 상태 확인 시도 중 오류: 서버 연결 안됨'); return; } // 상태 확인 중 UI 업데이트 this.elements.checkDeviceStatusBtn.disabled = true; this.elements.deviceStatusResult.value = '장치 상태 확인 중...'; try { console.log('장치 상태 확인 요청 전송'); // 백엔드 API 호출 const response = await AppUtils.fetchWithTimeout('/api/device/status', { method: 'GET' }); const data = await response.json(); if (response.ok && data.success) { // 상태 확인 성공 console.log('장치 상태 확인 성공:', data); this.isStatusChecked = true; this.elements.deviceStatusResult.value = JSON.stringify(data, null, 2); } else { // 상태 확인 실패 console.error('장치 상태 확인 실패:', data); this.elements.deviceStatusResult.value = `상태 확인 실패: ${data.error || '알 수 없는 오류'}`; } } catch (error) { // 예외 발생 console.error('장치 상태 확인 중 오류 발생:', error); this.elements.deviceStatusResult.value = `상태 확인 중 오류 발생: ${error.message}`; } finally { // 버튼 다시 활성화 this.elements.checkDeviceStatusBtn.disabled = false; } }, // 프로그램 목록 조회 loadProgramsList: async function() { if (!this.isConnected) { this.showProgramsError('오류: 먼저 서버에 연결해야 합니다.'); console.error('프로그램 목록 조회 시도 중 오류: 서버 연결 안됨'); return; } // 이미 로딩 중이면 중복 요청 방지 if (this.isLoadingPrograms) { console.log('이미 프로그램 목록 로딩 중'); return; } // 로딩 중 UI 업데이트 this.isLoadingPrograms = true; this.elements.getProgramsBtn.disabled = true; this.elements.programsList.innerHTML = `
${AppUtils.createLoadingSpinner()} 프로그램 목록 로드 중...
`; try { console.log('프로그램 목록 조회 요청 전송'); // 백엔드 API 호출 const response = await AppUtils.fetchWithTimeout('/api/device/programs', { method: 'GET' }); const data = await response.json(); if (response.ok && data.success) { // 목록 조회 성공 console.log('프로그램 목록 조회 성공:', data); this.programsList = data.programs || []; // 목록 표시 this.displayProgramsList(); // 드롭다운 업데이트 this.updateProgramsDropdown(); // 실행 버튼 상태 업데이트 this.updateExecuteButton(); } else { // 목록 조회 실패 console.error('프로그램 목록 조회 실패:', data); this.showProgramsError(`프로그램 목록 조회 실패: ${data.error || '알 수 없는 오류'}`); } } catch (error) { // 예외 발생 console.error('프로그램 목록 조회 중 오류 발생:', error); this.showProgramsError(`프로그램 목록 조회 중 오류 발생: ${error.message}`); } finally { // 로딩 상태 및 버튼 상태 복원 this.isLoadingPrograms = false; this.elements.getProgramsBtn.disabled = false; } }, // 프로그램 목록 표시 displayProgramsList: function() { const programsListElement = this.elements.programsList; if (!this.programsList || this.programsList.length === 0) { programsListElement.innerHTML = `
등록된 프로그램이 없습니다.
`; return; } // 테이블 형태로 프로그램 목록 표시 let html = ` `; // 프로그램 항목 생성 this.programsList.forEach(program => { html += ` `; }); html += `
이름 설명 경로
${AppUtils.escapeHtml(program.name || '알 수 없음')} ${AppUtils.escapeHtml(program.description || '-')} ${AppUtils.escapeHtml(program.path || '-')}
총 ${this.programsList.length}개 프로그램
`; programsListElement.innerHTML = html; }, // 프로그램 드롭다운 업데이트 updateProgramsDropdown: function() { const dropdown = this.elements.programSelectDropdown; // 기존 옵션 제거 dropdown.innerHTML = ''; // 기본 옵션 추가 const defaultOption = document.createElement('option'); defaultOption.value = ''; defaultOption.textContent = this.programsList.length > 0 ? '-- 실행할 프로그램 선택 --' : '-- 프로그램 없음 --'; dropdown.appendChild(defaultOption); // 프로그램 옵션 추가 this.programsList.forEach(program => { const option = document.createElement('option'); option.value = program.id || ''; option.textContent = program.name || '알 수 없음'; // 설명이 있으면 괄호로 추가 if (program.description) { option.textContent += ` (${program.description})`; } dropdown.appendChild(option); }); }, // 실행 버튼 상태 업데이트 updateExecuteButton: function() { const dropdown = this.elements.programSelectDropdown; const executeBtn = this.elements.executeProgramBtn; // 선택된 프로그램이 있을 때만 버튼 활성화 executeBtn.disabled = !dropdown.value; }, // 프로그램 실행 executeProgram: async function(programId) { if (!this.isConnected) { this.showExecuteResult('error', '오류: 먼저 서버에 연결해야 합니다.'); console.error('프로그램 실행 시도 중 오류: 서버 연결 안됨'); return; } if (!programId) { this.showExecuteResult('error', '오류: 실행할 프로그램을 선택해주세요.'); console.error('프로그램 실행 시도 중 오류: 프로그램 ID 없음'); return; } // 실행 중 UI 업데이트 this.elements.executeProgramBtn.disabled = true; this.showExecuteResult('loading', '프로그램 실행 중...'); try { console.log(`프로그램 실행 요청 전송: ${programId}`); // 백엔드 API 호출 const response = await AppUtils.fetchWithTimeout(`/api/device/programs/${programId}/execute`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}) }, 15000); // 15초 타임아웃 (실행에 시간이 더 걸릴 수 있음) const data = await response.json(); if (response.ok && data.success) { // 실행 성공 console.log('프로그램 실행 성공:', data); this.showExecuteResult('success', `실행 성공: ${data.message || '프로그램이 성공적으로 실행되었습니다.'}`); // 시스템 알림 AppUtils.addSystemNotification(`프로그램 실행 성공: ${this.getSelectedProgramName()}`); } else { // 실행 실패 console.error('프로그램 실행 실패:', data); this.showExecuteResult('error', `실행 실패: ${data.error || '알 수 없는 오류'}`); } } catch (error) { // 예외 발생 console.error('프로그램 실행 중 오류 발생:', error); if (error.message.includes('시간이 초과')) { this.showExecuteResult('error', '프로그램 실행 요청 시간 초과. 서버 응답이 없습니다.'); } else { this.showExecuteResult('error', `프로그램 실행 중 오류 발생: ${error.message}`); } } finally { // 버튼 다시 활성화 this.elements.executeProgramBtn.disabled = false; } }, // 선택된 프로그램 이름 가져오기 getSelectedProgramName: function() { const dropdown = this.elements.programSelectDropdown; const selectedOption = dropdown.options[dropdown.selectedIndex]; return selectedOption ? selectedOption.textContent : '알 수 없는 프로그램'; }, // 프로그램 목록 오류 표시 showProgramsError: function(errorMessage) { this.elements.programsList.innerHTML = `
${errorMessage}
`; // 재시도 버튼 이벤트 리스너 document.getElementById('retryLoadProgramsBtn').addEventListener('click', () => { console.log('프로그램 목록 재시도 버튼 클릭'); this.loadProgramsList(); }); }, // 실행 결과 표시 showExecuteResult: function(status, message) { const resultElement = this.elements.executeResult; // 모든 상태 클래스 제거 resultElement.classList.remove('success', 'error', 'warning'); // 내용 초기화 resultElement.innerHTML = ''; // 상태에 따라 처리 switch (status) { case 'success': resultElement.classList.add('success'); resultElement.innerHTML = ` ${message}`; break; case 'error': resultElement.classList.add('error'); resultElement.innerHTML = ` ${message}`; break; case 'warning': resultElement.classList.add('warning'); resultElement.innerHTML = ` ${message}`; break; case 'loading': resultElement.innerHTML = `${AppUtils.createLoadingSpinner()} ${message}`; break; default: resultElement.textContent = message; } } }; // 페이지 로드 완료 시 모듈 초기화 document.addEventListener('DOMContentLoaded', function() { console.log('장치 제어 모듈 로드됨'); // DOM이 완전히 로드된 후 약간의 지연을 두고 초기화 setTimeout(() => { DeviceControl.init(); }, 100); });