diff --git a/ESP32_CAN_Logger.ino b/ESP32_CAN_Logger.ino index dc39490..ac6740a 100644 --- a/ESP32_CAN_Logger.ino +++ b/ESP32_CAN_Logger.ino @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1541,6 +1542,11 @@ void setup() { // WiFi AP 시작 WiFi.softAP(wifiSSID, wifiPassword); + + // WiFi 성능 최적화 + WiFi.setSleep(false); // WiFi 절전 모드 비활성화 (신호 강도 개선) + esp_wifi_set_max_tx_power(84); // TX 출력 최대화 (20.5dBm = 84/4) + Serial.print("✓ AP SSID: "); Serial.println(wifiSSID); Serial.print("✓ AP IP: "); diff --git a/index.h b/index.h index 5b23885..05d27f6 100644 --- a/index.h +++ b/index.h @@ -377,6 +377,7 @@ const char index_html[] PROGMEM = R"rawliteral( } .file-item { background: white; + border: 1px solid #e0e0e0; padding: 12px; margin-bottom: 10px; border-radius: 8px; @@ -399,6 +400,15 @@ const char index_html[] PROGMEM = R"rawliteral( border: 2px solid #11998e; background: linear-gradient(135deg, rgba(17, 153, 142, 0.05) 0%, rgba(56, 239, 125, 0.05) 100%); } + .file-item.selected { + border: 2px solid #667eea; + background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%); + } + .file-checkbox { + width: 20px; + height: 20px; + cursor: pointer; + } .file-info { flex: 1; min-width: 200px; @@ -724,6 +734,12 @@ const char index_html[] PROGMEM = R"rawliteral(

Log Files

+
+ + + + +

Loading...

@@ -1068,6 +1084,9 @@ const char index_html[] PROGMEM = R"rawliteral( } fileItem.innerHTML = + '' + '
' + nameHtml + '
' + formatBytes(file.size) + '
' + @@ -1223,6 +1242,88 @@ const char index_html[] PROGMEM = R"rawliteral( } } + // 파일 선택 관리 함수들 + function toggleFileSelection(checkbox) { + const fileItem = checkbox.closest('.file-item'); + if (checkbox.checked) { + fileItem.classList.add('selected'); + } else { + fileItem.classList.remove('selected'); + } + } + + function selectAllFiles() { + const checkboxes = document.querySelectorAll('.file-checkbox:not(:disabled)'); + checkboxes.forEach(cb => { + cb.checked = true; + cb.closest('.file-item').classList.add('selected'); + }); + } + + function deselectAllFiles() { + const checkboxes = document.querySelectorAll('.file-checkbox'); + checkboxes.forEach(cb => { + cb.checked = false; + cb.closest('.file-item').classList.remove('selected'); + }); + } + + function getSelectedFiles() { + const selected = []; + const checkboxes = document.querySelectorAll('.file-checkbox:checked'); + checkboxes.forEach(cb => { + selected.push(cb.dataset.filename); + }); + return selected; + } + + function downloadSelectedFiles() { + const selected = getSelectedFiles(); + if (selected.length === 0) { + alert('Please select files to download.'); + return; + } + + // 각 파일을 순차적으로 다운로드 + selected.forEach((filename, index) => { + setTimeout(() => { + downloadFile(filename); + }, index * 500); // 500ms 간격으로 다운로드 + }); + + console.log('Downloading files:', selected); + } + + function deleteSelectedFiles() { + const selected = getSelectedFiles(); + if (selected.length === 0) { + alert('Please select files to delete.'); + return; + } + + const message = 'Are you sure you want to delete ' + selected.length + ' file(s)?\n\n' + + 'Files:\n' + selected.join('\n') + '\n\n' + + 'This action cannot be undone.'; + + if (!confirm(message)) { + return; + } + + // 각 파일 삭제 + selected.forEach(filename => { + if (ws && ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({cmd: 'deleteFile', filename: filename})); + } + }); + + console.log('Deleting files:', selected); + + // 선택 해제 + setTimeout(() => { + deselectAllFiles(); + }, 1000); + } + window.addEventListener('load', function() { loadCanSpeed(); loadMcpMode();