can logging 멈춤 해결, 파일 정상 로깅
This commit is contained in:
@@ -250,9 +250,9 @@ File serial2LogFile; // ⭐ Serial2 추가
|
|||||||
char currentFilename[MAX_FILENAME_LEN];
|
char currentFilename[MAX_FILENAME_LEN];
|
||||||
char currentSerialFilename[MAX_FILENAME_LEN];
|
char currentSerialFilename[MAX_FILENAME_LEN];
|
||||||
char currentSerial2Filename[MAX_FILENAME_LEN]; // ⭐ Serial2 추가
|
char currentSerial2Filename[MAX_FILENAME_LEN]; // ⭐ Serial2 추가
|
||||||
uint16_t bufferIndex = 0;
|
uint32_t bufferIndex = 0; // ⭐ uint16_t → uint32_t (오버플로우 방지)
|
||||||
uint16_t serialCsvIndex = 0;
|
uint32_t serialCsvIndex = 0; // ⭐ uint16_t → uint32_t
|
||||||
uint16_t serial2CsvIndex = 0; // ⭐ Serial2 추가
|
uint32_t serial2CsvIndex = 0; // ⭐ uint16_t → uint32_t // ⭐ Serial2 추가
|
||||||
volatile uint32_t currentFileSize = 0;
|
volatile uint32_t currentFileSize = 0;
|
||||||
volatile uint32_t currentSerialFileSize = 0;
|
volatile uint32_t currentSerialFileSize = 0;
|
||||||
volatile uint32_t currentSerial2FileSize = 0; // ⭐ Serial2 추가
|
volatile uint32_t currentSerial2FileSize = 0; // ⭐ Serial2 추가
|
||||||
@@ -837,6 +837,28 @@ void canRxTask(void *parameter) {
|
|||||||
struct can_frame frame;
|
struct can_frame frame;
|
||||||
CANMessage msg;
|
CANMessage msg;
|
||||||
|
|
||||||
|
Serial.println("✓ CAN RX Task 시작 (Core 0, Priority 24 - 절대 최고!)");
|
||||||
|
|
||||||
|
// ⭐⭐⭐ 초기 버퍼 확인
|
||||||
|
if (digitalRead(CAN_INT_PIN) == LOW) {
|
||||||
|
Serial.println("⚠️ 초기 CAN 인터럽트 핀 LOW - 버퍼 클리어");
|
||||||
|
int readCount = 0;
|
||||||
|
while (mcp2515.readMessage(&frame) == MCP2515::ERROR_OK && readCount < 100) {
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
msg.timestamp_us = (uint64_t)tv.tv_sec * 1000000ULL + tv.tv_usec;
|
||||||
|
msg.id = frame.can_id & 0x1FFFFFFF;
|
||||||
|
msg.dlc = frame.can_dlc;
|
||||||
|
memcpy(msg.data, frame.data, 8);
|
||||||
|
|
||||||
|
if (xQueueSend(canQueue, &msg, 0) == pdTRUE) {
|
||||||
|
totalMsgCount++;
|
||||||
|
readCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.printf("✓ 초기 버퍼에서 %d개 읽음\n", readCount);
|
||||||
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||||
|
|
||||||
@@ -848,7 +870,7 @@ void canRxTask(void *parameter) {
|
|||||||
msg.dlc = frame.can_dlc;
|
msg.dlc = frame.can_dlc;
|
||||||
memcpy(msg.data, frame.data, 8);
|
memcpy(msg.data, frame.data, 8);
|
||||||
|
|
||||||
if (xQueueSend(canQueue, &msg, 0) == pdTRUE) {
|
if (xQueueSend(canQueue, &msg, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||||
totalMsgCount++;
|
totalMsgCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -892,7 +914,7 @@ void sdWriteTask(void *parameter) {
|
|||||||
|
|
||||||
// CAN 로깅
|
// CAN 로깅
|
||||||
if (loggingEnabled && sdCardReady) {
|
if (loggingEnabled && sdCardReady) {
|
||||||
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
|
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(50)) == pdTRUE) {
|
||||||
if (canLogFormatCSV) {
|
if (canLogFormatCSV) {
|
||||||
char csvLine[128];
|
char csvLine[128];
|
||||||
uint64_t relativeTime = canMsg.timestamp_us - canLogStartTime;
|
uint64_t relativeTime = canMsg.timestamp_us - canLogStartTime;
|
||||||
@@ -913,26 +935,27 @@ void sdWriteTask(void *parameter) {
|
|||||||
currentFileSize += lineLen;
|
currentFileSize += lineLen;
|
||||||
|
|
||||||
static int csvFlushCounter = 0;
|
static int csvFlushCounter = 0;
|
||||||
if (++csvFlushCounter >= 100) {
|
if (++csvFlushCounter >= 20) { // 50 → 20으로 더 자주 플러시
|
||||||
logFile.flush();
|
logFile.flush();
|
||||||
csvFlushCounter = 0;
|
csvFlushCounter = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// BIN 형식
|
// BIN 형식
|
||||||
if (bufferIndex + sizeof(CANMessage) <= FILE_BUFFER_SIZE) {
|
// ⭐⭐⭐ 1단계: 버퍼 가득 찼으면 먼저 플러시
|
||||||
memcpy(&fileBuffer[bufferIndex], &canMsg, sizeof(CANMessage));
|
if (bufferIndex + sizeof(CANMessage) > FILE_BUFFER_SIZE) {
|
||||||
bufferIndex += sizeof(CANMessage);
|
|
||||||
currentFileSize += sizeof(CANMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bufferIndex >= FILE_BUFFER_SIZE - sizeof(CANMessage)) {
|
|
||||||
if (logFile) {
|
if (logFile) {
|
||||||
logFile.write(fileBuffer, bufferIndex);
|
size_t written = logFile.write(fileBuffer, bufferIndex);
|
||||||
logFile.flush();
|
logFile.flush();
|
||||||
|
Serial.printf("✓ BIN 버퍼 플러시: %d bytes written\n", written);
|
||||||
bufferIndex = 0;
|
bufferIndex = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ⭐⭐⭐ 2단계: 이제 공간 확보됨, 데이터 추가
|
||||||
|
memcpy(&fileBuffer[bufferIndex], &canMsg, sizeof(CANMessage));
|
||||||
|
bufferIndex += sizeof(CANMessage);
|
||||||
|
currentFileSize += sizeof(CANMessage);
|
||||||
}
|
}
|
||||||
xSemaphoreGive(sdMutex);
|
xSemaphoreGive(sdMutex);
|
||||||
}
|
}
|
||||||
@@ -946,7 +969,7 @@ void sdWriteTask(void *parameter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void sdMonitorTask(void *parameter) {
|
void sdMonitorTask(void *parameter) {
|
||||||
const TickType_t xDelay = pdMS_TO_TICKS(1000);
|
const TickType_t xDelay = pdMS_TO_TICKS(500);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
uint32_t currentTime = millis();
|
uint32_t currentTime = millis();
|
||||||
@@ -1182,7 +1205,7 @@ void sequenceTask(void *parameter) {
|
|||||||
// ========================================
|
// ========================================
|
||||||
void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) {
|
void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) {
|
||||||
if (type == WStype_TEXT) {
|
if (type == WStype_TEXT) {
|
||||||
DynamicJsonDocument doc(2048);
|
DynamicJsonDocument doc(44384);
|
||||||
DeserializationError error = deserializeJson(doc, payload);
|
DeserializationError error = deserializeJson(doc, payload);
|
||||||
|
|
||||||
if (error) return;
|
if (error) return;
|
||||||
@@ -1268,16 +1291,29 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length)
|
|||||||
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
|
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
|
||||||
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, ext);
|
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, ext);
|
||||||
|
|
||||||
|
// ⭐⭐⭐ 파일 생성 (헤더 쓰기)
|
||||||
logFile = SD.open(currentFilename, FILE_WRITE);
|
logFile = SD.open(currentFilename, FILE_WRITE);
|
||||||
|
|
||||||
if (logFile) {
|
if (logFile) {
|
||||||
if (canLogFormatCSV) {
|
if (canLogFormatCSV) {
|
||||||
logFile.println("Time_us,CAN_ID,DLC,Data");
|
logFile.println("Time_us,CAN_ID,DLC,Data");
|
||||||
|
logFile.flush();
|
||||||
}
|
}
|
||||||
|
logFile.close(); // ⭐ 헤더 쓰고 닫기
|
||||||
|
|
||||||
|
// ⭐⭐⭐ APPEND 모드로 다시 열기
|
||||||
|
logFile = SD.open(currentFilename, FILE_APPEND);
|
||||||
|
|
||||||
|
if (logFile) {
|
||||||
loggingEnabled = true;
|
loggingEnabled = true;
|
||||||
bufferIndex = 0;
|
bufferIndex = 0;
|
||||||
currentFileSize = logFile.size();
|
currentFileSize = logFile.size();
|
||||||
|
Serial.printf("✓ 로깅 파일 열림 (APPEND): %s\n", currentFilename);
|
||||||
|
} else {
|
||||||
|
Serial.println("✗ APPEND 모드로 파일 열기 실패");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Serial.println("✗ 파일 생성 실패");
|
||||||
}
|
}
|
||||||
|
|
||||||
xSemaphoreGive(sdMutex);
|
xSemaphoreGive(sdMutex);
|
||||||
@@ -1410,10 +1446,20 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length)
|
|||||||
int speedIndex = doc["speed"];
|
int speedIndex = doc["speed"];
|
||||||
if (speedIndex >= 0 && speedIndex < 4) {
|
if (speedIndex >= 0 && speedIndex < 4) {
|
||||||
currentCanSpeed = canSpeedValues[speedIndex];
|
currentCanSpeed = canSpeedValues[speedIndex];
|
||||||
mcp2515.reset();
|
// ⭐⭐⭐ MCP2515 리셋 제거! (CAN 수신 중단 방지)
|
||||||
mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ);
|
// 속도 변경은 로깅 중지 후 수동으로만 가능하도록 변경
|
||||||
setMCP2515Mode(currentMcpMode);
|
// mcp2515.reset();
|
||||||
|
// mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ);
|
||||||
|
// setMCP2515Mode(currentMcpMode);
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
|
||||||
|
// 사용자에게 안내 메시지
|
||||||
|
StaticJsonDocument<256> response;
|
||||||
|
response["type"] = "info";
|
||||||
|
response["message"] = "CAN speed saved. Stop logging and restart to apply.";
|
||||||
|
String json;
|
||||||
|
serializeJson(response, json);
|
||||||
|
webSocket.sendTXT(num, json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcmp(cmd, "startSerial2Logging") == 0) {
|
else if (strcmp(cmd, "startSerial2Logging") == 0) {
|
||||||
@@ -1525,10 +1571,20 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length)
|
|||||||
int speedIndex = doc["speed"];
|
int speedIndex = doc["speed"];
|
||||||
if (speedIndex >= 0 && speedIndex < 4) {
|
if (speedIndex >= 0 && speedIndex < 4) {
|
||||||
currentCanSpeed = canSpeedValues[speedIndex];
|
currentCanSpeed = canSpeedValues[speedIndex];
|
||||||
mcp2515.reset();
|
// ⭐⭐⭐ MCP2515 리셋 제거! (CAN 수신 중단 방지)
|
||||||
mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ);
|
// 속도 변경은 로깅 중지 후 수동으로만 가능하도록 변경
|
||||||
setMCP2515Mode(currentMcpMode);
|
// mcp2515.reset();
|
||||||
|
// mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ);
|
||||||
|
// setMCP2515Mode(currentMcpMode);
|
||||||
saveSettings();
|
saveSettings();
|
||||||
|
|
||||||
|
// 사용자에게 안내 메시지
|
||||||
|
StaticJsonDocument<256> response;
|
||||||
|
response["type"] = "info";
|
||||||
|
response["message"] = "CAN speed saved. Stop logging and restart to apply.";
|
||||||
|
String json;
|
||||||
|
serializeJson(response, json);
|
||||||
|
webSocket.sendTXT(num, json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcmp(cmd, "setMcpMode") == 0) {
|
else if (strcmp(cmd, "setMcpMode") == 0) {
|
||||||
@@ -1735,7 +1791,7 @@ void webUpdateTask(void *parameter) {
|
|||||||
webSocket.loop();
|
webSocket.loop();
|
||||||
|
|
||||||
if (webSocket.connectedClients() > 0) {
|
if (webSocket.connectedClients() > 0) {
|
||||||
DynamicJsonDocument doc(8192); // ⭐ 4096 → 8192로 증가
|
DynamicJsonDocument doc(16384); // ⭐ 4096 → 8192로 증가
|
||||||
doc["type"] = "update";
|
doc["type"] = "update";
|
||||||
doc["logging"] = loggingEnabled;
|
doc["logging"] = loggingEnabled;
|
||||||
doc["serialLogging"] = serialLoggingEnabled;
|
doc["serialLogging"] = serialLoggingEnabled;
|
||||||
@@ -1837,7 +1893,7 @@ void webUpdateTask(void *parameter) {
|
|||||||
serialMsg.isTx ? "TX" : "RX",
|
serialMsg.isTx ? "TX" : "RX",
|
||||||
dataStr);
|
dataStr);
|
||||||
|
|
||||||
if (serialCsvIndex + lineLen < SERIAL_CSV_BUFFER_SIZE) {
|
if (serialCsvIndex + lineLen <= SERIAL_CSV_BUFFER_SIZE) { // < → <=
|
||||||
memcpy(&serialCsvBuffer[serialCsvIndex], csvLine, lineLen);
|
memcpy(&serialCsvBuffer[serialCsvIndex], csvLine, lineLen);
|
||||||
serialCsvIndex += lineLen;
|
serialCsvIndex += lineLen;
|
||||||
currentSerialFileSize += lineLen;
|
currentSerialFileSize += lineLen;
|
||||||
@@ -1898,7 +1954,7 @@ void webUpdateTask(void *parameter) {
|
|||||||
serial2Msg.isTx ? "TX" : "RX",
|
serial2Msg.isTx ? "TX" : "RX",
|
||||||
dataStr);
|
dataStr);
|
||||||
|
|
||||||
if (serial2CsvIndex + lineLen < SERIAL2_CSV_BUFFER_SIZE) {
|
if (serial2CsvIndex + lineLen <= SERIAL2_CSV_BUFFER_SIZE) { // < → <=
|
||||||
memcpy(&serial2CsvBuffer[serial2CsvIndex], csvLine, lineLen);
|
memcpy(&serial2CsvBuffer[serial2CsvIndex], csvLine, lineLen);
|
||||||
serial2CsvIndex += lineLen;
|
serial2CsvIndex += lineLen;
|
||||||
currentSerial2FileSize += lineLen;
|
currentSerial2FileSize += lineLen;
|
||||||
@@ -1951,7 +2007,7 @@ void webUpdateTask(void *parameter) {
|
|||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
delay(1000);
|
delay(1000);
|
||||||
|
WiFi.setSleep(false);
|
||||||
Serial.println("\n========================================");
|
Serial.println("\n========================================");
|
||||||
Serial.println(" Byun CAN Logger + Serial Terminal");
|
Serial.println(" Byun CAN Logger + Serial Terminal");
|
||||||
Serial.println(" Version 2.3 - PSRAM Optimized");
|
Serial.println(" Version 2.3 - PSRAM Optimized");
|
||||||
@@ -1993,10 +2049,33 @@ void setup() {
|
|||||||
|
|
||||||
// MCP2515 초기화
|
// MCP2515 초기화
|
||||||
Serial.println("MCP2515 초기화...");
|
Serial.println("MCP2515 초기화...");
|
||||||
mcp2515.reset();
|
|
||||||
|
// ⭐⭐⭐ 하드 리셋
|
||||||
|
digitalWrite(HSPI_CS, LOW);
|
||||||
|
delay(10);
|
||||||
|
digitalWrite(HSPI_CS, HIGH);
|
||||||
delay(50);
|
delay(50);
|
||||||
|
|
||||||
|
mcp2515.reset();
|
||||||
|
delay(100);
|
||||||
mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ);
|
mcp2515.setBitrate(currentCanSpeed, MCP_8MHZ);
|
||||||
|
delay(10);
|
||||||
mcp2515.setNormalMode();
|
mcp2515.setNormalMode();
|
||||||
|
delay(50);
|
||||||
|
|
||||||
|
// 버퍼 클리어
|
||||||
|
Serial.println("CAN 버퍼 클리어...");
|
||||||
|
struct can_frame dummyFrame;
|
||||||
|
int clearCount = 0;
|
||||||
|
while (mcp2515.readMessage(&dummyFrame) == MCP2515::ERROR_OK) {
|
||||||
|
clearCount++;
|
||||||
|
if (clearCount > 100) break;
|
||||||
|
}
|
||||||
|
if (clearCount > 0) {
|
||||||
|
Serial.printf("✓ 버퍼 클리어 완료 (%d개)\n", clearCount);
|
||||||
|
}
|
||||||
|
mcp2515.clearRXnOVRFlags();
|
||||||
|
|
||||||
Serial.println("✓ MCP2515 초기화 완료");
|
Serial.println("✓ MCP2515 초기화 완료");
|
||||||
|
|
||||||
// Serial 통신 초기화
|
// Serial 통신 초기화
|
||||||
@@ -2132,13 +2211,13 @@ void setup() {
|
|||||||
attachInterrupt(digitalPinToInterrupt(CAN_INT_PIN), canISR, FALLING);
|
attachInterrupt(digitalPinToInterrupt(CAN_INT_PIN), canISR, FALLING);
|
||||||
|
|
||||||
// Task 생성
|
// Task 생성
|
||||||
xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 4096, NULL, 6, &canRxTaskHandle, 1);
|
xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 8192, NULL, configMAX_PRIORITIES - 1, &canRxTaskHandle, 1); // Core 0, Pri 24
|
||||||
xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 24576, NULL, 4, &sdWriteTaskHandle, 1);
|
xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 12576, NULL, 6, &sdWriteTaskHandle, 0);
|
||||||
xTaskCreatePinnedToCore(sequenceTask, "SEQ", 4096, NULL, 2, NULL, 1);
|
xTaskCreatePinnedToCore(sequenceTask, "SEQ", 4096, NULL, 2, NULL, 1);
|
||||||
xTaskCreatePinnedToCore(serialRxTask, "SERIAL_RX", 6144, NULL, 5, &serialRxTaskHandle, 0);
|
xTaskCreatePinnedToCore(serialRxTask, "SERIAL_RX", 6144, NULL, 5, &serialRxTaskHandle, 0);
|
||||||
xTaskCreatePinnedToCore(serial2RxTask, "SERIAL2_RX", 6144, NULL, 5, &serial2RxTaskHandle, 0); // ⭐ Serial2
|
xTaskCreatePinnedToCore(serial2RxTask, "SERIAL2_RX", 6144, NULL, 5, &serial2RxTaskHandle, 0); // ⭐ Serial2
|
||||||
xTaskCreatePinnedToCore(txTask, "TX", 4096, NULL, 3, NULL, 0);
|
xTaskCreatePinnedToCore(txTask, "TX", 4096, NULL, 3, NULL, 1);
|
||||||
xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 16384, NULL, 2, &webTaskHandle, 0); // ⭐ 10240 → 16384
|
xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 16384, NULL, 5, &webTaskHandle, 0); // ⭐ 10240 → 16384
|
||||||
xTaskCreatePinnedToCore(sdMonitorTask, "SD_MONITOR", 4096, NULL, 1, NULL, 0);
|
xTaskCreatePinnedToCore(sdMonitorTask, "SD_MONITOR", 4096, NULL, 1, NULL, 0);
|
||||||
|
|
||||||
if (timeSyncStatus.rtcAvailable) {
|
if (timeSyncStatus.rtcAvailable) {
|
||||||
|
|||||||
17
index.h
17
index.h
@@ -26,7 +26,7 @@ const char index_html[] PROGMEM = R"rawliteral(
|
|||||||
}
|
}
|
||||||
.header {
|
.header {
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
color: #666;
|
color: white;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@@ -883,7 +883,7 @@ const char index_html[] PROGMEM = R"rawliteral(
|
|||||||
const modeNames = {0: 'NORMAL (TX/RX+ACK)', 1: 'LISTEN-ONLY (RX)', 2: 'LOOPBACK', 3: 'TRANSMIT-ONLY (TX)'};
|
const modeNames = {0: 'NORMAL (TX/RX+ACK)', 1: 'LISTEN-ONLY (RX)', 2: 'LOOPBACK', 3: 'TRANSMIT-ONLY (TX)'};
|
||||||
let currentLoggingFile = '';
|
let currentLoggingFile = '';
|
||||||
let commentingFile = '';
|
let commentingFile = '';
|
||||||
let hasInitialSync = false;
|
// hasInitialSync 제거 - 매번 자동 동기화
|
||||||
|
|
||||||
function updateCurrentTime() {
|
function updateCurrentTime() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@@ -943,11 +943,18 @@ const char index_html[] PROGMEM = R"rawliteral(
|
|||||||
document.getElementById('sync-status').textContent = '연결됨';
|
document.getElementById('sync-status').textContent = '연결됨';
|
||||||
document.getElementById('sync-status').style.color = '#38ef7d';
|
document.getElementById('sync-status').style.color = '#38ef7d';
|
||||||
|
|
||||||
if (!hasInitialSync) {
|
// ⭐⭐⭐ 자동 시간 동기화 (1분에 1회로 제한)
|
||||||
|
const now = Date.now();
|
||||||
|
const lastSync = parseInt(localStorage.getItem('lastTimeSync') || '0');
|
||||||
|
|
||||||
|
if (now - lastSync > 60000) { // 1분 이상 경과
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
syncTimeFromPhone();
|
syncTimeFromPhone();
|
||||||
hasInitialSync = true;
|
localStorage.setItem('lastTimeSync', now.toString());
|
||||||
}, 500);
|
console.log('Auto time sync on connect');
|
||||||
|
}, 100);
|
||||||
|
} else {
|
||||||
|
console.log('Skipping time sync (last sync was recent)');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⭐ WebSocket 연결되면 즉시 파일 목록 요청
|
// ⭐ WebSocket 연결되면 즉시 파일 목록 요청
|
||||||
|
|||||||
Reference in New Issue
Block a user