Spaces:
Sleeping
Sleeping
| /** | |
| * 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, | |
| // ================== μΆκ° μμ 1/4 ================== | |
| // μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν | |
| deviceCustomControl: null, | |
| customCommandInput: null, | |
| executeCustomBtn: null, | |
| customExecuteResult: null | |
| // ================== μΆκ° λ 1/4 ==================== | |
| }, | |
| // λͺ¨λ μ΄κΈ°ν | |
| 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'); | |
| // ================== μΆκ° μμ 2/4 ================== | |
| // μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν | |
| this.elements.deviceCustomControl = document.getElementById('deviceCustomControl'); | |
| this.elements.customCommandInput = document.getElementById('customCommandInput'); | |
| this.elements.executeCustomBtn = document.getElementById('executeCustomBtn'); | |
| this.elements.customExecuteResult = document.getElementById('customExecuteResult'); | |
| // ================== μΆκ° λ 2/4 ==================== | |
| 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); | |
| }); | |
| } | |
| // ================== μΆκ° μμ 3/4 ================== | |
| // μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν λ²νΌ ν΄λ¦ | |
| if (this.elements.executeCustomBtn) { | |
| this.elements.executeCustomBtn.addEventListener('click', () => { | |
| const command = this.elements.customCommandInput.value; | |
| console.log(`μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν λ²νΌ ν΄λ¦, λͺ λ Ήμ΄: ${command}`); | |
| this.executeCustomProgram(command); | |
| }); | |
| } | |
| // μν° ν€λ‘ μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν | |
| if (this.elements.customCommandInput) { | |
| this.elements.customCommandInput.addEventListener('keydown', (event) => { | |
| if (event.key === 'Enter') { | |
| console.log('μ¬μ©μ μ μ λͺ λ Ήμ΄ μ λ ₯ νλμμ μν° ν€ κ°μ§'); | |
| event.preventDefault(); // νΌ μ μΆ λ°©μ§ λ± κΈ°λ³Έ λμ λ§κΈ° | |
| const command = this.elements.customCommandInput.value; | |
| this.executeCustomProgram(command); | |
| } | |
| }); | |
| } | |
| // ================== μΆκ° λ 3/4 ==================== | |
| 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 νμ±ν | |
| if(this.elements.deviceBasicFunctions) this.elements.deviceBasicFunctions.classList.add('active'); | |
| if(this.elements.deviceProgramControl) this.elements.deviceProgramControl.classList.add('active'); | |
| // ================== μΆκ° (connectServer μ±κ³΅ μ UI νμ±ν) ================== | |
| if(this.elements.deviceCustomControl) this.elements.deviceCustomControl.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 νμ±ν | |
| if(this.elements.deviceBasicFunctions) this.elements.deviceBasicFunctions.classList.add('active'); | |
| if(this.elements.deviceProgramControl) this.elements.deviceProgramControl.classList.add('active'); | |
| // ================== μΆκ° (connectServer μ±κ³΅ μ UI νμ±ν) ================== | |
| if(this.elements.deviceCustomControl) this.elements.deviceCustomControl.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 νμ±ν | |
| if(this.elements.deviceBasicFunctions) this.elements.deviceBasicFunctions.classList.add('active'); | |
| if(this.elements.deviceProgramControl) this.elements.deviceProgramControl.classList.add('active'); | |
| // ================== μΆκ° (connectServer μ±κ³΅ μ UI νμ±ν) ================== | |
| if(this.elements.deviceCustomControl) this.elements.deviceCustomControl.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; | |
| if (!statusElement) return; // μμ μμΌλ©΄ μ’ λ£ | |
| // λͺ¨λ μν ν΄λμ€ μ κ±° | |
| 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; | |
| // JSON λ°μ΄ν°λ₯Ό 보기 μ’κ² ν¬λ§·ν νμ¬ νμ | |
| this.elements.deviceStatusResult.value = JSON.stringify(data.data || data, null, 2); // data.data μ°μ νμΈ | |
| } 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; | |
| if(this.elements.getProgramsBtn) this.elements.getProgramsBtn.disabled = true; // λ²νΌ μ‘΄μ¬ μ¬λΆ νμΈ | |
| if(this.elements.programsList) { // λͺ©λ‘ μμ μ‘΄μ¬ μ¬λΆ νμΈ | |
| this.elements.programsList.innerHTML = ` | |
| <div class="loading-message"> | |
| ${AppUtils.createLoadingSpinner()} νλ‘κ·Έλ¨ λͺ©λ‘ λ‘λ μ€... | |
| </div> | |
| `; | |
| } | |
| 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; | |
| if(this.elements.getProgramsBtn) this.elements.getProgramsBtn.disabled = false; // λ²νΌ μ‘΄μ¬ μ¬λΆ νμΈ | |
| } | |
| }, | |
| // νλ‘κ·Έλ¨ λͺ©λ‘ νμ | |
| displayProgramsList: function() { | |
| const programsListElement = this.elements.programsList; | |
| if (!programsListElement) return; // μμ μμΌλ©΄ μ’ λ£ | |
| if (!this.programsList || this.programsList.length === 0) { | |
| programsListElement.innerHTML = ` | |
| <div class="no-programs-message"> | |
| <i class="fas fa-info-circle"></i> λ±λ‘λ νλ‘κ·Έλ¨μ΄ μμ΅λλ€. | |
| </div> | |
| `; | |
| return; | |
| } | |
| // ν μ΄λΈ ννλ‘ νλ‘κ·Έλ¨ λͺ©λ‘ νμ | |
| let html = ` | |
| <table class="program-list"> | |
| <thead> | |
| <tr> | |
| <th>μ΄λ¦</th> | |
| <th>μ€λͺ </th> | |
| <th>κ²½λ‘</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| `; | |
| // νλ‘κ·Έλ¨ νλͺ© μμ± | |
| this.programsList.forEach(program => { | |
| html += ` | |
| <tr> | |
| <td>${AppUtils.escapeHtml(program.name || 'μ μ μμ')}</td> | |
| <td>${AppUtils.escapeHtml(program.description || '-')}</td> | |
| <td>${AppUtils.escapeHtml(program.path || '-')}</td> | |
| </tr> | |
| `; | |
| }); | |
| html += ` | |
| </tbody> | |
| </table> | |
| <div style="margin-top: 10px; font-size: 0.9em; color: #666;"> | |
| μ΄ ${this.programsList.length}κ° νλ‘κ·Έλ¨ | |
| </div> | |
| `; | |
| programsListElement.innerHTML = html; | |
| }, | |
| // νλ‘κ·Έλ¨ λλ‘λ€μ΄ μ λ°μ΄νΈ | |
| updateProgramsDropdown: function() { | |
| const dropdown = this.elements.programSelectDropdown; | |
| if (!dropdown) return; // μμ μμΌλ©΄ μ’ λ£ | |
| // κΈ°μ‘΄ μ΅μ μ κ±° | |
| 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; | |
| if (!dropdown || !executeBtn) return; // μμ μμΌλ©΄ μ’ λ£ | |
| // μ νλ νλ‘κ·Έλ¨μ΄ μμ λλ§ λ²νΌ νμ±ν | |
| 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 μ λ°μ΄νΈ | |
| if(this.elements.executeProgramBtn) 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 { | |
| // λ²νΌ λ€μ νμ±ν | |
| if(this.elements.executeProgramBtn) this.elements.executeProgramBtn.disabled = false; | |
| } | |
| }, | |
| // μ νλ νλ‘κ·Έλ¨ μ΄λ¦ κ°μ Έμ€κΈ° | |
| getSelectedProgramName: function() { | |
| const dropdown = this.elements.programSelectDropdown; | |
| if (!dropdown) return 'μ μ μλ νλ‘κ·Έλ¨'; | |
| const selectedOption = dropdown.options[dropdown.selectedIndex]; | |
| return selectedOption ? selectedOption.textContent : 'μ μ μλ νλ‘κ·Έλ¨'; | |
| }, | |
| // νλ‘κ·Έλ¨ λͺ©λ‘ μ€λ₯ νμ | |
| showProgramsError: function(errorMessage) { | |
| const programsListElement = this.elements.programsList; | |
| if (!programsListElement) return; // μμ μμΌλ©΄ μ’ λ£ | |
| programsListElement.innerHTML = ` | |
| <div class="error-message"> | |
| <i class="fas fa-exclamation-circle"></i> ${errorMessage} | |
| <button class="retry-button" id="retryLoadProgramsBtn"> | |
| <i class="fas fa-sync"></i> λ€μ μλ | |
| </button> | |
| </div> | |
| `; | |
| // μ¬μλ λ²νΌ μ΄λ²€νΈ 리μ€λ | |
| // μ΄μ μ μΆκ°λ 리μ€λκ° μλ€λ©΄ μ κ±°νκ³ λ€μ μΆκ°νλ κ²μ΄ μμ ν μ μμ | |
| const retryBtn = document.getElementById('retryLoadProgramsBtn'); | |
| if (retryBtn) { | |
| retryBtn.replaceWith(retryBtn.cloneNode(true)); // 리μ€λ μ κ±° νΈλ¦ | |
| document.getElementById('retryLoadProgramsBtn').addEventListener('click', () => { | |
| console.log('νλ‘κ·Έλ¨ λͺ©λ‘ μ¬μλ λ²νΌ ν΄λ¦'); | |
| this.loadProgramsList(); | |
| }); | |
| } | |
| }, | |
| // μ€ν κ²°κ³Ό νμ (미리 μ μλ νλ‘κ·Έλ¨ μ©) | |
| showExecuteResult: function(status, message) { | |
| const resultElement = this.elements.executeResult; | |
| if (!resultElement) return; // μμ μμΌλ©΄ μ’ λ£ | |
| // λͺ¨λ μν ν΄λμ€ μ κ±° | |
| resultElement.classList.remove('success', 'error', 'warning'); | |
| // λ΄μ© μ΄κΈ°ν | |
| resultElement.innerHTML = ''; | |
| // μνμ λ°λΌ μ²λ¦¬ | |
| switch (status) { | |
| case 'success': | |
| resultElement.classList.add('success'); | |
| resultElement.innerHTML = `<i class="fas fa-check-circle"></i> ${message}`; | |
| break; | |
| case 'error': | |
| resultElement.classList.add('error'); | |
| resultElement.innerHTML = `<i class="fas fa-exclamation-circle"></i> ${message}`; | |
| break; | |
| case 'warning': | |
| resultElement.classList.add('warning'); | |
| resultElement.innerHTML = `<i class="fas fa-exclamation-triangle"></i> ${message}`; | |
| break; | |
| case 'loading': | |
| resultElement.innerHTML = `${AppUtils.createLoadingSpinner()} ${message}`; | |
| break; | |
| default: | |
| resultElement.textContent = message; | |
| } | |
| }, | |
| // ================== μΆκ° μμ 4/4 ================== | |
| // μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν | |
| executeCustomProgram: async function(command) { | |
| if (!this.isConnected) { | |
| this.showCustomExecuteResult('error', 'μ€λ₯: λ¨Όμ μλ²μ μ°κ²°ν΄μΌ ν©λλ€.'); | |
| console.error('μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν μλ μ€ μ€λ₯: μλ² μ°κ²° μλ¨'); | |
| return; | |
| } | |
| if (!command || command.trim() === '') { | |
| this.showCustomExecuteResult('error', 'μ€λ₯: μ€νν λͺ λ Ήμ΄λ₯Ό μ λ ₯ν΄μ£ΌμΈμ.'); | |
| console.error('μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν μλ μ€ μ€λ₯: λͺ λ Ήμ΄ μμ'); | |
| return; | |
| } | |
| // μ€ν μ€ UI μ λ°μ΄νΈ | |
| if(this.elements.executeCustomBtn) this.elements.executeCustomBtn.disabled = true; | |
| this.showCustomExecuteResult('loading', 'λͺ λ Ήμ΄ μ€ν μ€...'); | |
| try { | |
| console.log(`μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν μμ² μ μ‘: ${command}`); | |
| // λ°±μλ API νΈμΆ | |
| const response = await AppUtils.fetchWithTimeout('/api/device/execute-custom', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ command: command }) | |
| }, 15000); // 15μ΄ νμμμ | |
| const data = await response.json(); | |
| if (response.ok && data.success) { | |
| // μ€ν μ±κ³΅ | |
| console.log('μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν μ±κ³΅:', data); | |
| // κ²°κ³Ό νμ | |
| let successMessage = `λͺ λ Ήμ΄ μ€ν μ±κ³΅: ${data.message || ''}`; | |
| // μΆλ ₯ λ΄μ©μ΄ μμΌλ©΄ μΆκ° (HTML μμ μ²λ¦¬ ν¬ν¨) | |
| if (data.output && data.output.trim()) { | |
| successMessage += `<div class="command-output"><pre>${AppUtils.escapeHtml(data.output)}</pre></div>`; | |
| } | |
| this.showCustomExecuteResult('success', successMessage); | |
| // μμ€ν μλ¦Ό | |
| AppUtils.addSystemNotification(`μ¬μ©μ μ μ λͺ λ Ήμ΄ μ€ν μ±κ³΅: ${command}`); | |
| } else { | |
| // μ€ν μ€ν¨ | |
| console.error('μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν μ€ν¨:', data); | |
| let errorMessage = `μ€ν μ€ν¨: ${data.error || 'μ μ μλ μ€λ₯'}`; | |
| // μ€λ₯ μΆλ ₯μ΄ μμΌλ©΄ μΆκ° (HTML μμ μ²λ¦¬ ν¬ν¨) | |
| if (data.error_output && data.error_output.trim()) { | |
| errorMessage += `<div class="command-error"><pre>${AppUtils.escapeHtml(data.error_output)}</pre></div>`; | |
| } | |
| this.showCustomExecuteResult('error', errorMessage); | |
| } | |
| } catch (error) { | |
| // μμΈ λ°μ | |
| console.error('μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν μ€ μ€λ₯ λ°μ:', error); | |
| if (error.message.includes('μκ°μ΄ μ΄κ³Ό')) { | |
| this.showCustomExecuteResult('error', 'λͺ λ Ήμ΄ μ€ν μμ² μκ° μ΄κ³Ό. μλ² μλ΅μ΄ μμ΅λλ€.'); | |
| } else { | |
| this.showCustomExecuteResult('error', `λͺ λ Ήμ΄ μ€ν μ€ μ€λ₯ λ°μ: ${error.message}`); | |
| } | |
| } finally { | |
| // λ²νΌ λ€μ νμ±ν | |
| if(this.elements.executeCustomBtn) this.elements.executeCustomBtn.disabled = false; | |
| } | |
| }, | |
| // μ¬μ©μ μ μ νλ‘κ·Έλ¨ μ€ν κ²°κ³Ό νμ | |
| showCustomExecuteResult: function(status, message) { | |
| const resultElement = this.elements.customExecuteResult; | |
| if (!resultElement) return; // μμ μμΌλ©΄ μ’ λ£ | |
| // λͺ¨λ μν ν΄λμ€ μ κ±° | |
| resultElement.classList.remove('success', 'error', 'warning'); | |
| // λ΄μ© μ΄κΈ°ν | |
| resultElement.innerHTML = ''; | |
| // μνμ λ°λΌ μ²λ¦¬ | |
| switch (status) { | |
| case 'success': | |
| resultElement.classList.add('success'); | |
| // HTML λ©μμ§ μ½μ μ μ£Όμ (innerHTML μ¬μ©) | |
| resultElement.innerHTML = `<i class="fas fa-check-circle"></i> ${message}`; | |
| break; | |
| case 'error': | |
| resultElement.classList.add('error'); | |
| resultElement.innerHTML = `<i class="fas fa-exclamation-circle"></i> ${message}`; | |
| break; | |
| case 'warning': | |
| resultElement.classList.add('warning'); | |
| resultElement.innerHTML = `<i class="fas fa-exclamation-triangle"></i> ${message}`; | |
| break; | |
| case 'loading': | |
| // λ‘λ© μ€νΌλ ν¨μκ° AppUtilsμ μλ€κ³ κ°μ | |
| resultElement.innerHTML = `${AppUtils.createLoadingSpinner()} ${message}`; | |
| break; | |
| default: | |
| resultElement.textContent = message; // κΈ°λ³Έμ ν μ€νΈλ§ νμ | |
| } | |
| } | |
| // ================== μΆκ° λ 4/4 ==================== | |
| }; | |
| // νμ΄μ§ λ‘λ μλ£ μ λͺ¨λ μ΄κΈ°ν | |
| document.addEventListener('DOMContentLoaded', function() { | |
| console.log('μ₯μΉ μ μ΄ λͺ¨λ λ‘λλ¨'); | |
| // DOMμ΄ μμ ν λ‘λλ ν μ½κ°μ μ§μ°μ λκ³ μ΄κΈ°ν | |
| // DOM μμκ° νμ€ν λ‘λλ νμ μ΄κΈ°ννκΈ° μν¨ | |
| setTimeout(() => { | |
| // AppUtilsκ° μ μλμλμ§ νμΈ (μμ‘΄μ±) | |
| if (typeof AppUtils === 'undefined') { | |
| console.error('AppUtilsκ° μ μλμ§ μμ DeviceControl μ΄κΈ°ν μ€ν¨.'); | |
| // νμνλ€λ©΄ μ¬μ©μμκ² μλ¦Ό νμ | |
| alert('νμ΄μ§ μ΄κΈ°ν μ€λ₯: νμ μ νΈλ¦¬ν°(AppUtils)λ₯Ό λ‘λν μ μμ΅λλ€.'); | |
| return; | |
| } | |
| DeviceControl.init(); | |
| }, 100); | |
| }); |