flush삭제
This commit is contained in:
@@ -305,7 +305,17 @@ 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 추가
|
||||||
uint32_t bufferIndex = 0; // ⭐ uint16_t → uint32_t (오버플로우 방지)
|
|
||||||
|
// ⭐⭐⭐ 더블 버퍼링 변수
|
||||||
|
uint8_t* fileBuffer1 = NULL; // 쓰기 버퍼
|
||||||
|
uint8_t* fileBuffer2 = NULL; // flush 버퍼
|
||||||
|
uint8_t* currentWriteBuffer = NULL; // 현재 쓰기용
|
||||||
|
uint8_t* currentFlushBuffer = NULL; // 현재 flush용
|
||||||
|
uint32_t writeBufferIndex = 0; // 쓰기 버퍼 인덱스
|
||||||
|
uint32_t flushBufferSize = 0; // flush할 크기
|
||||||
|
volatile bool flushInProgress = false; // flush 진행중 플래그
|
||||||
|
TaskHandle_t sdFlushTaskHandle = NULL; // flush 전용 태스크
|
||||||
|
|
||||||
uint32_t serialCsvIndex = 0; // ⭐ uint16_t → uint32_t
|
uint32_t serialCsvIndex = 0; // ⭐ uint16_t → uint32_t
|
||||||
uint32_t serial2CsvIndex = 0; // ⭐ uint16_t → uint32_t // ⭐ Serial2 추가
|
uint32_t serial2CsvIndex = 0; // ⭐ uint16_t → uint32_t // ⭐ Serial2 추가
|
||||||
volatile uint32_t currentFileSize = 0;
|
volatile uint32_t currentFileSize = 0;
|
||||||
@@ -491,13 +501,27 @@ bool initPSRAM() {
|
|||||||
Serial.printf("✓ PSRAM 총 용량: %d MB\n", ESP.getPsramSize() / 1024 / 1024);
|
Serial.printf("✓ PSRAM 총 용량: %d MB\n", ESP.getPsramSize() / 1024 / 1024);
|
||||||
Serial.printf("✓ PSRAM 여유: %d KB\n\n", ESP.getFreePsram() / 1024);
|
Serial.printf("✓ PSRAM 여유: %d KB\n\n", ESP.getFreePsram() / 1024);
|
||||||
|
|
||||||
// 버퍼 할당
|
// ⭐⭐⭐ 더블 버퍼 할당
|
||||||
fileBuffer = (uint8_t*)ps_malloc(FILE_BUFFER_SIZE);
|
fileBuffer1 = (uint8_t*)ps_malloc(FILE_BUFFER_SIZE);
|
||||||
if (!fileBuffer) {
|
if (!fileBuffer1) {
|
||||||
Serial.println("✗ fileBuffer 할당 실패");
|
Serial.println("✗ fileBuffer1 할당 실패");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Serial.printf("✓ fileBuffer: %d KB\n", FILE_BUFFER_SIZE / 1024);
|
Serial.printf("✓ fileBuffer1: %d KB\n", FILE_BUFFER_SIZE / 1024);
|
||||||
|
|
||||||
|
fileBuffer2 = (uint8_t*)ps_malloc(FILE_BUFFER_SIZE);
|
||||||
|
if (!fileBuffer2) {
|
||||||
|
Serial.println("✗ fileBuffer2 할당 실패");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Serial.printf("✓ fileBuffer2: %d KB\n", FILE_BUFFER_SIZE / 1024);
|
||||||
|
|
||||||
|
// 초기 버퍼 포인터 설정
|
||||||
|
currentWriteBuffer = fileBuffer1;
|
||||||
|
currentFlushBuffer = fileBuffer2;
|
||||||
|
writeBufferIndex = 0;
|
||||||
|
flushBufferSize = 0;
|
||||||
|
Serial.println("✓ 더블 버퍼링 초기화 완료");
|
||||||
|
|
||||||
serialCsvBuffer = (char*)ps_malloc(SERIAL_CSV_BUFFER_SIZE);
|
serialCsvBuffer = (char*)ps_malloc(SERIAL_CSV_BUFFER_SIZE);
|
||||||
if (!serialCsvBuffer) {
|
if (!serialCsvBuffer) {
|
||||||
@@ -844,7 +868,12 @@ void checkAutoTriggers(struct can_frame &frame) {
|
|||||||
if (logFile) {
|
if (logFile) {
|
||||||
loggingEnabled = true;
|
loggingEnabled = true;
|
||||||
autoTriggerActive = true;
|
autoTriggerActive = true;
|
||||||
bufferIndex = 0;
|
|
||||||
|
// ⭐⭐⭐ 더블 버퍼 초기화
|
||||||
|
writeBufferIndex = 0;
|
||||||
|
flushBufferSize = 0;
|
||||||
|
flushInProgress = false;
|
||||||
|
|
||||||
currentFileSize = logFile.size();
|
currentFileSize = logFile.size();
|
||||||
|
|
||||||
Serial.println("🎯 Auto Trigger: 로깅 시작!");
|
Serial.println("🎯 Auto Trigger: 로깅 시작!");
|
||||||
@@ -889,12 +918,19 @@ void checkAutoTriggers(struct can_frame &frame) {
|
|||||||
if (anyMatch && result) {
|
if (anyMatch && result) {
|
||||||
// 🎯 파일 닫기 로직 추가
|
// 🎯 파일 닫기 로직 추가
|
||||||
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(2000)) == pdTRUE) {
|
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(2000)) == pdTRUE) {
|
||||||
// BIN 형식: 버퍼에 남은 데이터 강제 플러시
|
// ⭐⭐⭐ 더블 버퍼: 현재 쓰기 버퍼의 남은 데이터 flush
|
||||||
if (bufferIndex > 0 && logFile) {
|
if (writeBufferIndex > 0 && logFile) {
|
||||||
size_t written = logFile.write(fileBuffer, bufferIndex);
|
size_t written = logFile.write(currentWriteBuffer, writeBufferIndex);
|
||||||
logFile.flush();
|
logFile.flush();
|
||||||
Serial.printf("✓ Auto Trigger 최종 플러시: %d bytes\n", written);
|
Serial.printf("✓ Auto Trigger 최종 쓰기 버퍼 플러시: %d bytes\n", written);
|
||||||
bufferIndex = 0;
|
writeBufferIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ⭐ flush 태스크가 작업 중이면 완료 대기
|
||||||
|
int waitCount = 0;
|
||||||
|
while (flushInProgress && waitCount < 500) {
|
||||||
|
vTaskDelay(10);
|
||||||
|
waitCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSV 형식: 최종 플러시
|
// CSV 형식: 최종 플러시
|
||||||
@@ -911,7 +947,8 @@ void checkAutoTriggers(struct can_frame &frame) {
|
|||||||
loggingEnabled = false;
|
loggingEnabled = false;
|
||||||
autoTriggerActive = false;
|
autoTriggerActive = false;
|
||||||
currentFilename[0] = '\0';
|
currentFilename[0] = '\0';
|
||||||
bufferIndex = 0;
|
writeBufferIndex = 0;
|
||||||
|
flushBufferSize = 0;
|
||||||
|
|
||||||
Serial.println("🎯 Auto Trigger: 로깅 중지!");
|
Serial.println("🎯 Auto Trigger: 로깅 중지!");
|
||||||
Serial.printf(" 조건: ID 0x%03X 만족\n", frame.can_id);
|
Serial.printf(" 조건: ID 0x%03X 만족\n", frame.can_id);
|
||||||
@@ -1467,6 +1504,55 @@ void canRxTask(void *parameter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// SD Write Task
|
||||||
|
// ========================================
|
||||||
|
// ========================================
|
||||||
|
// ⭐⭐⭐ SD Flush 전용 태스크 (더블 버퍼링)
|
||||||
|
// ========================================
|
||||||
|
void sdFlushTask(void *parameter) {
|
||||||
|
Serial.println("✓ sdFlushTask 시작 (더블 버퍼링 모드)");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
uint32_t notifyValue;
|
||||||
|
|
||||||
|
// flush 알림 대기 (무한 대기)
|
||||||
|
if (xTaskNotifyWait(0, 0xFFFFFFFF, ¬ifyValue, portMAX_DELAY) == pdTRUE) {
|
||||||
|
uint32_t flushSize = notifyValue;
|
||||||
|
|
||||||
|
if (flushSize > 0 && flushSize <= FILE_BUFFER_SIZE) {
|
||||||
|
flushInProgress = true;
|
||||||
|
|
||||||
|
uint32_t startTime = millis();
|
||||||
|
|
||||||
|
// ⭐ SD 뮤텍스 획득 (최대 5초 대기)
|
||||||
|
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(5000)) == pdTRUE) {
|
||||||
|
if (logFile && sdCardReady && loggingEnabled) {
|
||||||
|
// ⭐ flush 버퍼를 SD에 쓰기
|
||||||
|
size_t written = logFile.write(currentFlushBuffer, flushSize);
|
||||||
|
|
||||||
|
// ⭐ 여기서 flush 호출 (200ms 블로킹되어도 CAN 수신은 계속!)
|
||||||
|
logFile.flush();
|
||||||
|
|
||||||
|
uint32_t elapsed = millis() - startTime;
|
||||||
|
Serial.printf("✓ SD Flush 완료: %d bytes, %d ms\n", written, elapsed);
|
||||||
|
|
||||||
|
// 통계
|
||||||
|
if (elapsed > 100) {
|
||||||
|
Serial.printf("⚠️ Flush 지연: %d ms (하지만 CAN은 정상!)\n", elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xSemaphoreGive(sdMutex);
|
||||||
|
} else {
|
||||||
|
Serial.println("✗ SD Flush: 뮤텍스 타임아웃");
|
||||||
|
}
|
||||||
|
|
||||||
|
flushInProgress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// SD Write Task
|
// SD Write Task
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -1528,7 +1614,7 @@ void sdWriteTask(void *parameter) {
|
|||||||
currentFileSize += lineLen;
|
currentFileSize += lineLen;
|
||||||
|
|
||||||
static int csvFlushCounter = 0;
|
static int csvFlushCounter = 0;
|
||||||
if (++csvFlushCounter >= 500) { // ⭐ 200 → 500 (최대 성능)
|
if (++csvFlushCounter >= 2000) { // ⭐ 500 → 2000 (SD 블로킹 최소화)
|
||||||
logFile.flush();
|
logFile.flush();
|
||||||
csvFlushCounter = 0;
|
csvFlushCounter = 0;
|
||||||
}
|
}
|
||||||
@@ -1539,41 +1625,40 @@ void sdWriteTask(void *parameter) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// BIN 형식
|
// ⭐⭐⭐ BIN 형식 - 더블 버퍼링
|
||||||
static uint32_t binMsgCounter = 0;
|
|
||||||
static uint32_t binReopenCounter = 0;
|
|
||||||
|
|
||||||
// ⭐⭐⭐ 1단계: 버퍼 가득 찼으면 먼저 플러시
|
// ⭐ 1단계: 버퍼 가득 찼으면 스왑
|
||||||
if (bufferIndex + sizeof(CANMessage) > FILE_BUFFER_SIZE) {
|
if (writeBufferIndex + sizeof(CANMessage) > FILE_BUFFER_SIZE) {
|
||||||
if (logFile) {
|
// flush가 진행중이면 잠시 대기 (매우 짧음, 버퍼 스왑만 기다림)
|
||||||
size_t written = logFile.write(fileBuffer, bufferIndex);
|
int waitCount = 0;
|
||||||
logFile.flush();
|
while (flushInProgress && waitCount < 100) {
|
||||||
Serial.printf("✓ BIN 버퍼 플러시 (FULL): %d bytes\n", written);
|
vTaskDelay(1);
|
||||||
bufferIndex = 0;
|
waitCount++;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⭐⭐⭐ 2단계: 이제 공간 확보됨, 데이터 추가
|
if (waitCount >= 100) {
|
||||||
memcpy(&fileBuffer[bufferIndex], &canMsg, sizeof(CANMessage));
|
Serial.println("⚠️ Flush 대기 타임아웃 (버퍼 손실 방지)");
|
||||||
bufferIndex += sizeof(CANMessage);
|
}
|
||||||
|
|
||||||
|
// ⭐ 버퍼 스왑 (즉시, 블로킹 없음!)
|
||||||
|
uint8_t* temp = currentWriteBuffer;
|
||||||
|
currentWriteBuffer = currentFlushBuffer;
|
||||||
|
currentFlushBuffer = temp;
|
||||||
|
flushBufferSize = writeBufferIndex;
|
||||||
|
writeBufferIndex = 0;
|
||||||
|
|
||||||
|
// ⭐ flush 태스크에 알림 (비동기)
|
||||||
|
if (sdFlushTaskHandle != NULL) {
|
||||||
|
xTaskNotify(sdFlushTaskHandle, flushBufferSize, eSetValueWithOverwrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.printf("✓ 버퍼 스왑: %d bytes → flush 태스크\n", flushBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ⭐ 2단계: 현재 쓰기 버퍼에 데이터 추가
|
||||||
|
memcpy(¤tWriteBuffer[writeBufferIndex], &canMsg, sizeof(CANMessage));
|
||||||
|
writeBufferIndex += sizeof(CANMessage);
|
||||||
currentFileSize += sizeof(CANMessage);
|
currentFileSize += sizeof(CANMessage);
|
||||||
binMsgCounter++;
|
|
||||||
binReopenCounter++;
|
|
||||||
|
|
||||||
// ⭐⭐⭐ 3단계: 1000개 메시지마다 주기적 플러시 (500→1000, 최대 성능)
|
|
||||||
if (binMsgCounter % 1000 == 0) {
|
|
||||||
if (logFile && bufferIndex > 0) {
|
|
||||||
size_t written = logFile.write(fileBuffer, bufferIndex);
|
|
||||||
logFile.flush();
|
|
||||||
Serial.printf("✓ BIN 주기 플러시: %d bytes (메시지: %d)\n", written, binMsgCounter);
|
|
||||||
bufferIndex = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ⭐⭐⭐ 4단계: 파일 재오픈 제거 (고부하 환경 최적화)
|
|
||||||
// 재오픈 시 SD 카드 블록으로 인한 오버플로우 방지
|
|
||||||
// 대신 주기적 flush로 데이터 안정성 확보
|
|
||||||
// if (binReopenCounter >= 2000) { ... } // 제거됨
|
|
||||||
|
|
||||||
}
|
}
|
||||||
xSemaphoreGive(sdMutex);
|
xSemaphoreGive(sdMutex);
|
||||||
@@ -1986,9 +2071,14 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length)
|
|||||||
|
|
||||||
if (logFile) {
|
if (logFile) {
|
||||||
loggingEnabled = true;
|
loggingEnabled = true;
|
||||||
bufferIndex = 0;
|
|
||||||
|
// ⭐⭐⭐ 더블 버퍼 초기화
|
||||||
|
writeBufferIndex = 0;
|
||||||
|
flushBufferSize = 0;
|
||||||
|
flushInProgress = false;
|
||||||
|
|
||||||
currentFileSize = logFile.size();
|
currentFileSize = logFile.size();
|
||||||
Serial.printf("✅ 로깅 시작!\n");
|
Serial.printf("✅ 로깅 시작! (더블 버퍼링 모드)\n");
|
||||||
Serial.printf(" 파일: %s\n", currentFilename);
|
Serial.printf(" 파일: %s\n", currentFilename);
|
||||||
Serial.printf(" 형식: %s\n", canLogFormatCSV ? "CSV" : "BIN");
|
Serial.printf(" 형식: %s\n", canLogFormatCSV ? "CSV" : "BIN");
|
||||||
Serial.printf(" 초기 크기: %lu bytes\n", currentFileSize);
|
Serial.printf(" 초기 크기: %lu bytes\n", currentFileSize);
|
||||||
@@ -2010,12 +2100,22 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length)
|
|||||||
Serial.println("🛑 로깅 중지 요청...");
|
Serial.println("🛑 로깅 중지 요청...");
|
||||||
|
|
||||||
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(2000)) == pdTRUE) {
|
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(2000)) == pdTRUE) {
|
||||||
// ⭐⭐⭐ BIN 형식: 버퍼에 남은 데이터 강제 플러시
|
// ⭐⭐⭐ 더블 버퍼: 현재 쓰기 버퍼의 남은 데이터 flush
|
||||||
if (bufferIndex > 0 && logFile) {
|
if (writeBufferIndex > 0 && logFile) {
|
||||||
size_t written = logFile.write(fileBuffer, bufferIndex);
|
size_t written = logFile.write(currentWriteBuffer, writeBufferIndex);
|
||||||
logFile.flush();
|
logFile.flush();
|
||||||
Serial.printf("✓ 최종 플러시: %d bytes\n", written);
|
Serial.printf("✓ 최종 쓰기 버퍼 플러시: %d bytes\n", written);
|
||||||
bufferIndex = 0;
|
writeBufferIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ⭐ flush 태스크가 작업 중이면 완료 대기
|
||||||
|
int waitCount = 0;
|
||||||
|
while (flushInProgress && waitCount < 500) {
|
||||||
|
vTaskDelay(10);
|
||||||
|
waitCount++;
|
||||||
|
}
|
||||||
|
if (waitCount > 0) {
|
||||||
|
Serial.printf("✓ Flush 태스크 완료 대기: %d ms\n", waitCount * 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⭐⭐⭐ CSV 형식: 최종 플러시
|
// ⭐⭐⭐ CSV 형식: 최종 플러시
|
||||||
@@ -2032,7 +2132,8 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length)
|
|||||||
|
|
||||||
loggingEnabled = false;
|
loggingEnabled = false;
|
||||||
currentFilename[0] = '\0';
|
currentFilename[0] = '\0';
|
||||||
bufferIndex = 0;
|
writeBufferIndex = 0;
|
||||||
|
flushBufferSize = 0;
|
||||||
xSemaphoreGive(sdMutex);
|
xSemaphoreGive(sdMutex);
|
||||||
} else {
|
} else {
|
||||||
Serial.println("✗ sdMutex 획득 실패!");
|
Serial.println("✗ sdMutex 획득 실패!");
|
||||||
@@ -3700,8 +3801,8 @@ void setup() {
|
|||||||
// - TX: CAN 전송
|
// - TX: CAN 전송
|
||||||
// - SEQ: 시퀀스 재생
|
// - SEQ: 시퀀스 재생
|
||||||
// - MONITOR: 상태 모니터링
|
// - MONITOR: 상태 모니터링
|
||||||
xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 12288, NULL, 20, &canRxTaskHandle, 1); // ⭐ 8KB → 12KB, Pri 24 (최고)
|
xTaskCreatePinnedToCore(canRxTask, "CAN_RX", 12288, NULL, 24, &canRxTaskHandle, 1); // ⭐ 8KB → 12KB, Pri 24 (최고)
|
||||||
xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 12288, NULL, 4, &webTaskHandle, 1); // ⭐ Core 0 → 1, Pri 4 (SD와 분리)
|
xTaskCreatePinnedToCore(webUpdateTask, "WEB_UPDATE", 12288, NULL, 8, &webTaskHandle, 0); // ⭐ Core 0 → 1, Pri 4 (SD와 분리)
|
||||||
xTaskCreatePinnedToCore(txTask, "TX", 4096, NULL, 3, NULL, 1); // Pri 3
|
xTaskCreatePinnedToCore(txTask, "TX", 4096, NULL, 3, NULL, 1); // Pri 3
|
||||||
xTaskCreatePinnedToCore(sequenceTask, "SEQ", 4096, NULL, 2, NULL, 1); // Pri 2
|
xTaskCreatePinnedToCore(sequenceTask, "SEQ", 4096, NULL, 2, NULL, 1); // Pri 2
|
||||||
xTaskCreatePinnedToCore(sdMonitorTask, "SD_MONITOR", 4096, NULL, 1, NULL, 1); // Pri 1
|
xTaskCreatePinnedToCore(sdMonitorTask, "SD_MONITOR", 4096, NULL, 1, NULL, 1); // Pri 1
|
||||||
@@ -3709,7 +3810,8 @@ void setup() {
|
|||||||
// Core 0: I/O 전용 (SD, Serial, RTC)
|
// Core 0: I/O 전용 (SD, Serial, RTC)
|
||||||
// - SD 쓰기와 Serial 수신만 처리
|
// - SD 쓰기와 Serial 수신만 처리
|
||||||
// - 웹은 Core 1에서 처리하므로 SD 지연 영향 없음
|
// - 웹은 Core 1에서 처리하므로 SD 지연 영향 없음
|
||||||
xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 18576, NULL, 8, &sdWriteTaskHandle, 0); // Pri 8 (최우선)
|
xTaskCreatePinnedToCore(sdFlushTask, "SD_FLUSH", 4096, NULL, 9, &sdFlushTaskHandle, 0); // ⭐ Pri 9 (최우선, flush 전용)
|
||||||
|
xTaskCreatePinnedToCore(sdWriteTask, "SD_WRITE", 18576, NULL, 8, &sdWriteTaskHandle, 0); // Pri 8
|
||||||
xTaskCreatePinnedToCore(serialRxTask, "SERIAL_RX", 6144, NULL, 6, &serialRxTaskHandle, 0); // Pri 6
|
xTaskCreatePinnedToCore(serialRxTask, "SERIAL_RX", 6144, NULL, 6, &serialRxTaskHandle, 0); // Pri 6
|
||||||
xTaskCreatePinnedToCore(serial2RxTask, "SERIAL2_RX", 6144, NULL, 6, &serial2RxTaskHandle,0); // Pri 6
|
xTaskCreatePinnedToCore(serial2RxTask, "SERIAL2_RX", 6144, NULL, 6, &serial2RxTaskHandle,0); // Pri 6
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user