jeongsoo's picture
init
1c2d73c
/**
* 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,
// ์‚ฌ์šฉ์ž ์ •์˜ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰
deviceCustomControl: null,
customCommandInput: null,
executeCustomBtn: null,
customExecuteResult: null,
// ================== ํฌํŠธ ์Šค์บ” ์š”์†Œ ์ถ”๊ฐ€ ==================
devicePortScanControl: null, // ํฌํŠธ ์Šค์บ” ์„น์…˜ ์ปจํ…Œ์ด๋„ˆ
scanPortsBtn: null, // ํฌํŠธ ์Šค์บ” ๋ฒ„ํŠผ
portsStatus: null, // ํฌํŠธ ์Šค์บ” ์ƒํƒœ ํ‘œ์‹œ ์˜์—ญ
portsResults: null // ํฌํŠธ ์Šค์บ” ๊ฒฐ๊ณผ ํ‘œ์‹œ ์˜์—ญ
// ================== ์ถ”๊ฐ€ ๋ ============================
},
// ๋ชจ๋“ˆ ์ดˆ๊ธฐํ™”
init: function() {
console.log('์žฅ์น˜ ์ œ์–ด ๋ชจ๋“ˆ ์ดˆ๊ธฐํ™” ์ค‘...');
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');
// ์‚ฌ์šฉ์ž ์ •์˜ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰
this.elements.deviceCustomControl = document.getElementById('deviceCustomControl');
this.elements.customCommandInput = document.getElementById('customCommandInput');
this.elements.executeCustomBtn = document.getElementById('executeCustomBtn');
this.elements.customExecuteResult = document.getElementById('customExecuteResult');
// ================== ํฌํŠธ ์Šค์บ” ์š”์†Œ ์ดˆ๊ธฐํ™” ์ถ”๊ฐ€ ==================
this.elements.devicePortScanControl = document.getElementById('devicePortScanControl');
this.elements.scanPortsBtn = document.getElementById('scanPortsBtn');
this.elements.portsStatus = document.getElementById('portsStatus');
this.elements.portsResults = document.getElementById('portsResults');
// ================== ์ถ”๊ฐ€ ๋ =================================
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);
});
}
// ์‚ฌ์šฉ์ž ์ •์˜ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ๋ฒ„ํŠผ ํด๋ฆญ
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);
}
});
}
// ================== ํฌํŠธ ์Šค์บ” ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ถ”๊ฐ€ ==================
if (this.elements.scanPortsBtn) {
this.elements.scanPortsBtn.addEventListener('click', () => {
console.log('์žฅ์น˜ ํฌํŠธ ์Šค์บ” ๋ฒ„ํŠผ ํด๋ฆญ');
this.scanDevicePorts();
});
}
// ================== ์ถ”๊ฐ€ ๋ ==================================
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'));
// ์žฅ์น˜ ์ œ์–ด ํƒญ ํ™œ์„ฑํ™”
if(this.elements.deviceTab) this.elements.deviceTab.classList.add('active');
if(this.elements.deviceSection) this.elements.deviceSection.classList.add('active');
console.log('์žฅ์น˜ ์ œ์–ด ํƒญ์œผ๋กœ ์ „ํ™˜ ์™„๋ฃŒ');
},
// ์„œ๋ฒ„ ์—ฐ๊ฒฐ ํ•จ์ˆ˜
connectServer: async function() {
// URL ๊ฐ€์ ธ์˜ค๊ธฐ (์ž…๋ ฅ๋œ ๊ฒƒ์ด ์žˆ์œผ๋ฉด ๋ฐฑ์—…์œผ๋กœ ์‚ฌ์šฉ)
const inputUrl = this.elements.deviceServerUrlInput.value.trim();
// ์—ฐ๊ฒฐ ์‹œ๋„ ์ค‘ UI ์—…๋ฐ์ดํŠธ
if(this.elements.connectDeviceServerBtn) 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) {
// ํ™˜๊ฒฝ๋ณ€์ˆ˜ URL ์—ฐ๊ฒฐ ์„ฑ๊ณต
console.log('ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ • ์žฅ์น˜ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์„ฑ๊ณต:', data);
this.isConnected = true;
// ์„œ๋ฒ„ ์‘๋‹ต์—์„œ ์ƒํƒœ ๋ฉ”์‹œ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ
const serverStatusMsg = data?.data?.status || data?.server_status || '์ •์ƒ';
this.updateConnectionStatus('connected', `์„œ๋ฒ„ ์—ฐ๊ฒฐ ์„ฑ๊ณต! ์ƒํƒœ: ${serverStatusMsg}`);
// ๊ธฐ๋Šฅ UI ํ™œ์„ฑํ™”
if(this.elements.deviceBasicFunctions) this.elements.deviceBasicFunctions.classList.add('active');
if(this.elements.deviceProgramControl) this.elements.deviceProgramControl.classList.add('active');
if(this.elements.deviceCustomControl) this.elements.deviceCustomControl.classList.add('active');
// ================== ํฌํŠธ ์Šค์บ” UI ํ™œ์„ฑํ™” ์ถ”๊ฐ€ ==================
if(this.elements.devicePortScanControl) this.elements.devicePortScanControl.classList.add('active');
// ================== ์ถ”๊ฐ€ ๋ ==================================
// ์žฅ์น˜ ์ƒํƒœ ์ž๋™ ์ฒดํฌ
this.checkDeviceStatus();
// ํ”„๋กœ๊ทธ๋žจ ๋ชฉ๋ก ์ž๋™ ๋กœ๋“œ
this.loadProgramsList();
// ์‹œ์Šคํ…œ ์•Œ๋ฆผ
AppUtils.addSystemNotification(`์žฅ์น˜ ๊ด€๋ฆฌ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์„ฑ๊ณต! (ํ™˜๊ฒฝ๋ณ€์ˆ˜ URL)`);
} else {
// ํ™˜๊ฒฝ๋ณ€์ˆ˜ URL ์—ฐ๊ฒฐ ์‹คํŒจ, ์ž…๋ ฅ๋œ URL๋กœ ์‹œ๋„
console.warn('ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ • ์žฅ์น˜ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹คํŒจ, ์ž…๋ ฅ URL๋กœ ์žฌ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค:', data);
if (!inputUrl) {
console.error('์ž…๋ ฅ๋œ URL์ด ์—†์–ด ์—ฐ๊ฒฐ ์‹คํŒจ');
this.isConnected = false;
this.updateConnectionStatus('error', 'ํ™˜๊ฒฝ๋ณ€์ˆ˜ URL ์—ฐ๊ฒฐ ์‹คํŒจ ๋ฐ ์ž…๋ ฅ๋œ URL์ด ์—†์Šต๋‹ˆ๋‹ค. URL์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.');
return; // ์ž…๋ ฅ URL ์—†์œผ๋ฉด ์—ฌ๊ธฐ์„œ ์ข…๋ฃŒ
}
// ์ž…๋ ฅ URL๋กœ ์žฌ์‹œ๋„
await this.connectWithCustomUrl(inputUrl);
}
} catch (error) {
// ํ™˜๊ฒฝ๋ณ€์ˆ˜ URL ์—ฐ๊ฒฐ ์‹œ๋„ ์ค‘ ์˜ˆ์™ธ ๋ฐœ์ƒ
console.error('ํ™˜๊ฒฝ๋ณ€์ˆ˜ URL ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:', error);
this.isConnected = false;
if (inputUrl) {
console.warn('ํ™˜๊ฒฝ๋ณ€์ˆ˜ URL ์—ฐ๊ฒฐ ์‹œ ์˜ค๋ฅ˜ ๋ฐœ์ƒ, ์ž…๋ ฅ URL๋กœ ์žฌ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค');
await this.connectWithCustomUrl(inputUrl); // ์ž…๋ ฅ URL ์žฌ์‹œ๋„
} else {
// ์ž…๋ ฅ URL๋„ ์—†๋Š” ๊ฒฝ์šฐ ์ตœ์ข… ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ
if (error.message.includes('์‹œ๊ฐ„์ด ์ดˆ๊ณผ')) {
this.updateConnectionStatus('error', 'ํ™˜๊ฒฝ๋ณ€์ˆ˜ URL ์—ฐ๊ฒฐ ์‹œ๊ฐ„ ์ดˆ๊ณผ. URL์„ ์ž…๋ ฅํ•˜์—ฌ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.');
} else {
this.updateConnectionStatus('error', `ํ™˜๊ฒฝ๋ณ€์ˆ˜ URL ์—ฐ๊ฒฐ ์˜ค๋ฅ˜. URL์„ ์ž…๋ ฅํ•˜์—ฌ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”: ${error.message}`);
}
}
} finally {
// ๋ฒ„ํŠผ ๋‹ค์‹œ ํ™œ์„ฑํ™”
if(this.elements.connectDeviceServerBtn) this.elements.connectDeviceServerBtn.disabled = false;
}
},
// ์‚ฌ์šฉ์ž ์ž…๋ ฅ URL๋กœ ์—ฐ๊ฒฐ ์‹œ๋„ (connectServer ๋‚ด๋ถ€ ๋กœ์ง ๋ถ„๋ฆฌ)
connectWithCustomUrl: async function(url) {
this.updateConnectionStatus('connecting', `์ž…๋ ฅ URL(${url})๋กœ ์—ฐ๊ฒฐ ์‹œ๋„ ์ค‘...`);
console.log(`์ž…๋ ฅํ•œ URL๋กœ ์žฅ์น˜ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹œ๋„: ${url}`);
try {
// ๋ฐฑ์—”๋“œ API ํ˜ธ์ถœ - Flask ๋ฐฑ์—”๋“œ๊ฐ€ URL ๋ฐ›์•„์„œ ์ฒ˜๋ฆฌ
const customUrlResponse = await AppUtils.fetchWithTimeout('/api/device/connect', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: url })
}, 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');
if(this.elements.deviceCustomControl) this.elements.deviceCustomControl.classList.add('active');
// ================== ํฌํŠธ ์Šค์บ” UI ํ™œ์„ฑํ™” ์ถ”๊ฐ€ ==================
if(this.elements.devicePortScanControl) this.elements.devicePortScanControl.classList.add('active');
// ================== ์ถ”๊ฐ€ ๋ ==================================
// ์žฅ์น˜ ์ƒํƒœ ์ž๋™ ์ฒดํฌ
this.checkDeviceStatus();
// ํ”„๋กœ๊ทธ๋žจ ๋ชฉ๋ก ์ž๋™ ๋กœ๋“œ
this.loadProgramsList();
// ์‹œ์Šคํ…œ ์•Œ๋ฆผ
AppUtils.addSystemNotification(`์žฅ์น˜ ๊ด€๋ฆฌ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์„ฑ๊ณต! (${url})`);
} else {
// ์ž…๋ ฅ URL ์—ฐ๊ฒฐ ์‹คํŒจ
console.error('์ž…๋ ฅ URL ์žฅ์น˜ ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹คํŒจ:', customUrlData);
this.isConnected = false;
this.updateConnectionStatus('error', `์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹คํŒจ: ${customUrlData.error || '์„œ๋ฒ„ ์‘๋‹ต ์˜ค๋ฅ˜'}`);
}
} catch(error) {
// ์ž…๋ ฅ URL๋กœ ์‹œ๋„ ์ค‘ ์˜ค๋ฅ˜
console.error('์ž…๋ ฅ URL๋กœ ์žฌ์‹œ๋„ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:', error);
this.isConnected = false;
if (error.message.includes('์‹œ๊ฐ„์ด ์ดˆ๊ณผ')) {
this.updateConnectionStatus('error', '์„œ๋ฒ„ ์—ฐ๊ฒฐ ์‹œ๊ฐ„ ์ดˆ๊ณผ. ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰ ์ค‘์ธ์ง€ ํ™•์ธํ•ด์ฃผ์„ธ์š”.');
} else {
this.updateConnectionStatus('error', `์„œ๋ฒ„ ์—ฐ๊ฒฐ ์˜ค๋ฅ˜: ${error.message}`);
}
}
},
// ์—ฐ๊ฒฐ ์ƒํƒœ ์—…๋ฐ์ดํŠธ
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) {
if(this.elements.deviceStatusResult) this.elements.deviceStatusResult.value = '์˜ค๋ฅ˜: ๋จผ์ € ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.';
console.error('์žฅ์น˜ ์ƒํƒœ ํ™•์ธ ์‹œ๋„ ์ค‘ ์˜ค๋ฅ˜: ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์•ˆ๋จ');
return;
}
// ์ƒํƒœ ํ™•์ธ ์ค‘ UI ์—…๋ฐ์ดํŠธ
if(this.elements.checkDeviceStatusBtn) this.elements.checkDeviceStatusBtn.disabled = true;
if(this.elements.deviceStatusResult) 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 ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ธฐ ์ข‹๊ฒŒ ํฌ๋งทํŒ…ํ•˜์—ฌ ํ‘œ์‹œ
if(this.elements.deviceStatusResult) this.elements.deviceStatusResult.value = JSON.stringify(data.data || data, null, 2); // data.data ์šฐ์„  ํ™•์ธ
} else {
console.error('์žฅ์น˜ ์ƒํƒœ ํ™•์ธ ์‹คํŒจ:', data);
if(this.elements.deviceStatusResult) this.elements.deviceStatusResult.value = `์ƒํƒœ ํ™•์ธ ์‹คํŒจ: ${data.error || '์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜'}`;
}
} catch (error) {
console.error('์žฅ์น˜ ์ƒํƒœ ํ™•์ธ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:', error);
if(this.elements.deviceStatusResult) this.elements.deviceStatusResult.value = `์ƒํƒœ ํ™•์ธ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: ${error.message}`;
} finally {
if(this.elements.checkDeviceStatusBtn) 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', '์˜ค๋ฅ˜: ๋จผ์ € ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.');
return;
}
if (!programId) {
this.showExecuteResult('error', '์˜ค๋ฅ˜: ์‹คํ–‰ํ•  ํ”„๋กœ๊ทธ๋žจ์„ ์„ ํƒํ•ด์ฃผ์„ธ์š”.');
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 || dropdown.selectedIndex < 0) 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;
}
},
// ์‚ฌ์šฉ์ž ์ •์˜ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰
executeCustomProgram: async function(command) {
if (!this.isConnected) {
this.showCustomExecuteResult('error', '์˜ค๋ฅ˜: ๋จผ์ € ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.');
return;
}
if (!command || command.trim() === '') {
this.showCustomExecuteResult('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 || ''}`;
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 || '์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜'}`;
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');
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; // ๊ธฐ๋ณธ์€ ํ…์ŠคํŠธ๋งŒ ํ‘œ์‹œ
}
},
// ================== ํฌํŠธ ์Šค์บ” ํ•จ์ˆ˜ ์ถ”๊ฐ€ ==================
// ์žฅ์น˜ ํฌํŠธ ์Šค์บ” ํ•จ์ˆ˜
scanDevicePorts: async function() {
if (!this.isConnected) {
this.showPortsStatus('error', '์˜ค๋ฅ˜: ๋จผ์ € ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.');
console.error('์žฅ์น˜ ํฌํŠธ ์Šค์บ” ์‹œ๋„ ์ค‘ ์˜ค๋ฅ˜: ์„œ๋ฒ„ ์—ฐ๊ฒฐ ์•ˆ๋จ');
return;
}
// ์Šค์บ” ์ค‘ UI ์—…๋ฐ์ดํŠธ
if(this.elements.scanPortsBtn) this.elements.scanPortsBtn.disabled = true;
this.showPortsStatus('loading', '์žฅ์น˜ ํฌํŠธ ์Šค์บ” ์ค‘...');
try {
console.log('์žฅ์น˜ ํฌํŠธ ์Šค์บ” ์š”์ฒญ ์ „์†ก');
// ๋ฐฑ์—”๋“œ API ํ˜ธ์ถœ
const response = await AppUtils.fetchWithTimeout('/api/device/scan-ports', {
method: 'GET'
}, 30000); // ์žฅ์น˜ ์Šค์บ”์€ ์‹œ๊ฐ„์ด ์˜ค๋ž˜ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ํƒ€์ž„์•„์›ƒ 30์ดˆ๋กœ ์„ค์ •
const data = await response.json();
if (response.ok && data.success) {
// ์Šค์บ” ์„ฑ๊ณต
console.log('์žฅ์น˜ ํฌํŠธ ์Šค์บ” ์„ฑ๊ณต:', data);
// ๊ฒฐ๊ณผ ํ‘œ์‹œ
this.displayPortsResults(data);
this.showPortsStatus('success', '์žฅ์น˜ ํฌํŠธ ์Šค์บ”์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.');
} else {
// ์Šค์บ” ์‹คํŒจ
console.error('์žฅ์น˜ ํฌํŠธ ์Šค์บ” ์‹คํŒจ:', data);
this.showPortsStatus('error', `์Šค์บ” ์‹คํŒจ: ${data.error || '์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜'}`);
}
} catch (error) {
// ์˜ˆ์™ธ ๋ฐœ์ƒ
console.error('์žฅ์น˜ ํฌํŠธ ์Šค์บ” ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ:', error);
if (error.message.includes('์‹œ๊ฐ„์ด ์ดˆ๊ณผ')) {
this.showPortsStatus('error', '์žฅ์น˜ ํฌํŠธ ์Šค์บ” ์š”์ฒญ ์‹œ๊ฐ„ ์ดˆ๊ณผ. ์„œ๋ฒ„ ์‘๋‹ต์ด ์—†์Šต๋‹ˆ๋‹ค.');
} else {
this.showPortsStatus('error', `์žฅ์น˜ ํฌํŠธ ์Šค์บ” ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: ${error.message}`);
}
} finally {
// ๋ฒ„ํŠผ ๋‹ค์‹œ ํ™œ์„ฑํ™”
if(this.elements.scanPortsBtn) this.elements.scanPortsBtn.disabled = false;
}
},
// ์žฅ์น˜ ํฌํŠธ ์Šค์บ” ๊ฒฐ๊ณผ ํ‘œ์‹œ
displayPortsResults: function(data) {
const portsResultsElement = this.elements.portsResults;
if (!portsResultsElement) return; // ์š”์†Œ ์—†์œผ๋ฉด ์ข…๋ฃŒ
// ์‹œ์Šคํ…œ ์ •๋ณด
const systemInfo = data.system_info || {};
const devices = data.devices || {};
const serialPorts = devices.serial || [];
const usbDevices = devices.usb || [];
const windowsUsbDevices = devices.windows_usb || []; // windows_usb ํ‚ค ์ถ”๊ฐ€
// HTML ์ƒ์„ฑ ์‹œ์ž‘
let html = `<div class="ports-results-container">`;
// ์‹œ์Šคํ…œ ์ •๋ณด ์„น์…˜
html += `<div class="ports-section">
<h4>์‹œ์Šคํ…œ ์ •๋ณด</h4>
<div class="info-row"><span class="info-label">OS:</span><span class="info-value">${AppUtils.escapeHtml(systemInfo.os || '์•Œ ์ˆ˜ ์—†์Œ')} ${AppUtils.escapeHtml(systemInfo.version || '')}</span></div>
<div class="info-row"><span class="info-label">์‹œ์Šคํ…œ:</span><span class="info-value">${AppUtils.escapeHtml(systemInfo.platform || '์•Œ ์ˆ˜ ์—†์Œ')}</span></div>
<div class="info-row"><span class="info-label">์กฐํšŒ ์‹œ๊ฐ„:</span><span class="info-value">${AppUtils.escapeHtml(data.timestamp || '์•Œ ์ˆ˜ ์—†์Œ')}</span></div>
</div>`;
// ์‹œ๋ฆฌ์–ผ ํฌํŠธ ์„น์…˜
html += `<div class="ports-section"><h4>์‹œ๋ฆฌ์–ผ ํฌํŠธ (${serialPorts.length}๊ฐœ)</h4>`;
if (serialPorts.length === 0) {
html += `<p class="no-ports-message">๋ฐœ๊ฒฌ๋œ ์‹œ๋ฆฌ์–ผ ํฌํŠธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</p>`;
} else {
html += `<table class="ports-table"><thead><tr><th>ํฌํŠธ</th><th>์„ค๋ช…</th><th>์ œ์กฐ์‚ฌ</th><th>VID:PID</th></tr></thead><tbody>`;
serialPorts.forEach(port => {
const vidPid = (port.vid && port.pid) ? `${port.vid}:${port.pid}` : '์•Œ ์ˆ˜ ์—†์Œ';
html += `<tr>
<td>${AppUtils.escapeHtml(port.port || '')}</td>
<td>${AppUtils.escapeHtml(port.description || '')}</td>
<td>${AppUtils.escapeHtml(port.manufacturer || '')}</td>
<td>${AppUtils.escapeHtml(vidPid)}</td>
</tr>`;
});
html += `</tbody></table>`;
}
html += `</div>`;
// USB ์žฅ์น˜ ์„น์…˜ (pyserial list_ports ๊ธฐ๋ฐ˜)
html += `<div class="ports-section"><h4>USB ์žฅ์น˜ (pyserial) (${usbDevices.length}๊ฐœ)</h4>`;
if (usbDevices.length === 0) {
html += `<p class="no-ports-message">๋ฐœ๊ฒฌ๋œ USB ์žฅ์น˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.</p>`;
} else {
html += `<table class="ports-table"><thead><tr><th>VID:PID</th><th>์ œํ’ˆ๋ช…</th><th>์ œ์กฐ์‚ฌ</th><th>์œ„์น˜</th></tr></thead><tbody>`;
usbDevices.forEach(device => {
const vidPid = (device.vid && device.pid) ? `${device.vid}:${device.pid}` : '์•Œ ์ˆ˜ ์—†์Œ';
html += `<tr>
<td>${AppUtils.escapeHtml(vidPid)}</td>
<td>${AppUtils.escapeHtml(device.product || '')}</td>
<td>${AppUtils.escapeHtml(device.manufacturer || '')}</td>
<td>${AppUtils.escapeHtml(device.location || '')}</td>
</tr>`;
});
html += `</tbody></table>`;
}
html += `</div>`;
// Windows WMI USB ์žฅ์น˜ ์„น์…˜ (์žˆ๋Š” ๊ฒฝ์šฐ)
if (windowsUsbDevices && windowsUsbDevices.length > 0) {
html += `<div class="ports-section"><h4>Windows USB ์žฅ์น˜ (WMI) (${windowsUsbDevices.length}๊ฐœ)</h4>`;
html += `<table class="ports-table"><thead><tr><th>์ด๋ฆ„</th><th>์„ค๋ช…</th><th>์ œ์กฐ์‚ฌ</th><th>VID:PID</th></tr></thead><tbody>`;
windowsUsbDevices.forEach(device => {
const vidPid = (device.vid && device.pid) ? `${device.vid}:${device.pid}` : '์•Œ ์ˆ˜ ์—†์Œ';
html += `<tr>
<td>${AppUtils.escapeHtml(device.name || '')}</td>
<td>${AppUtils.escapeHtml(device.description || '')}</td>
<td>${AppUtils.escapeHtml(device.manufacturer || '')}</td>
<td>${AppUtils.escapeHtml(vidPid)}</td>
</tr>`;
});
html += `</tbody></table></div>`;
}
// ์ปจํ…Œ์ด๋„ˆ ๋‹ซ๊ธฐ
html += `</div>`;
// HTML ์„ค์ •
portsResultsElement.innerHTML = html;
},
// ํฌํŠธ ์Šค์บ” ์ƒํƒœ ํ‘œ์‹œ
showPortsStatus: function(status, message) {
const statusElement = this.elements.portsStatus;
if (!statusElement) return; // ์š”์†Œ ์—†์œผ๋ฉด ์ข…๋ฃŒ
// ๋ชจ๋“  ์ƒํƒœ ํด๋ž˜์Šค ์ œ๊ฑฐ
statusElement.classList.remove('success', 'error', 'warning');
// ๋‚ด์šฉ ์ดˆ๊ธฐํ™”
statusElement.innerHTML = '';
// ์ƒํƒœ์— ๋”ฐ๋ผ ์ฒ˜๋ฆฌ
switch (status) {
case 'success':
statusElement.classList.add('success');
statusElement.innerHTML = `<i class="fas fa-check-circle"></i> ${message}`;
break;
case 'error':
statusElement.classList.add('error');
statusElement.innerHTML = `<i class="fas fa-exclamation-circle"></i> ${message}`;
break;
case 'warning':
statusElement.classList.add('warning');
statusElement.innerHTML = `<i class="fas fa-exclamation-triangle"></i> ${message}`;
break;
case 'loading':
statusElement.innerHTML = `${AppUtils.createLoadingSpinner()} ${message}`;
break;
default:
statusElement.textContent = message;
}
}
// ================== ์ถ”๊ฐ€ ๋ ============================
}; // DeviceControl ๊ฐ์ฒด ๋
// ํŽ˜์ด์ง€ ๋กœ๋“œ ์™„๋ฃŒ ์‹œ ๋ชจ๋“ˆ ์ดˆ๊ธฐํ™”
document.addEventListener('DOMContentLoaded', function() {
console.log('์žฅ์น˜ ์ œ์–ด ๋ชจ๋“ˆ ๋กœ๋“œ๋จ');
// DOM์ด ์™„์ „ํžˆ ๋กœ๋“œ๋œ ํ›„ ์•ฝ๊ฐ„์˜ ์ง€์—ฐ์„ ๋‘๊ณ  ์ดˆ๊ธฐํ™”
// DOM ์š”์†Œ๊ฐ€ ํ™•์‹คํžˆ ๋กœ๋“œ๋œ ํ›„์— ์ดˆ๊ธฐํ™”ํ•˜๊ธฐ ์œ„ํ•จ
setTimeout(() => {
// AppUtils๊ฐ€ ์ •์˜๋˜์—ˆ๋Š”์ง€ ํ™•์ธ (์˜์กด์„ฑ)
if (typeof AppUtils === 'undefined') {
console.error('AppUtils๊ฐ€ ์ •์˜๋˜์ง€ ์•Š์•„ DeviceControl ์ดˆ๊ธฐํ™” ์‹คํŒจ.');
// ํ•„์š”ํ•˜๋‹ค๋ฉด ์‚ฌ์šฉ์ž์—๊ฒŒ ์•Œ๋ฆผ ํ‘œ์‹œ
alert('ํŽ˜์ด์ง€ ์ดˆ๊ธฐํ™” ์˜ค๋ฅ˜: ํ•„์ˆ˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ(AppUtils)๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.');
return;
}
DeviceControl.init();
}, 100);
});