Files
canFD_esp32_glm-oh-my-open-/sd_logger.cpp
2026-02-20 17:50:40 +00:00

343 lines
8.3 KiB
C++

// sd_logger.cpp - SD Card Logger Implementation with Batch Writing
#include <Arduino.h>
#include "sd_logger.h"
#include "task_config.h"
#include "can_handler.h"
#include "psram_buffer.h"
extern volatile uint32_t canErrorCount;
bool sdInitialized = false;
uint64_t sdCardSize = 0;
uint64_t sdFreeSpace = 0;
File currentLogFile;
char currentLogFileName[64] = {0};
uint32_t currentLogFileSize = 0;
static uint32_t fileSequenceNumber = 0;
static uint8_t* writeBuffer = nullptr;
static size_t bufferOffset = 0;
static uint32_t lastFlushTime = 0;
static uint32_t framesInBuffer = 0;
bool initSDCard() {
Serial.println("Initializing SD Card (SDIO 4-bit mode)...");
writeBuffer = getSDWriteBuffer();
if (writeBuffer == nullptr) {
if (psramFound()) {
writeBuffer = (uint8_t*)ps_malloc(PSRAM_SD_BUFFER_SIZE);
} else {
writeBuffer = (uint8_t*)malloc(PSRAM_SD_BUFFER_SIZE);
}
if (writeBuffer == nullptr) {
Serial.println("Failed to allocate SD write buffer!");
return false;
}
}
SD_MMC.setPins(SDIO_CLK_PIN, SDIO_CMD_PIN, SDIO_D0_PIN,
SDIO_D1_PIN, SDIO_D2_PIN, SDIO_D3_PIN);
if (!SD_MMC.begin("/sdcard", false)) {
Serial.println("SD Card mount failed!");
sdInitialized = false;
return false;
}
sdCardSize = SD_MMC.cardSize();
sdFreeSpace = SD_MMC.totalBytes() - SD_MMC.usedBytes();
Serial.println("SD Card mounted successfully!");
Serial.printf(" Card Size: %llu MB\n", sdCardSize / (1024 * 1024));
Serial.printf(" Free Space: %llu MB\n", sdFreeSpace / (1024 * 1024));
sdInitialized = true;
createLogDirectories();
return true;
}
bool createLogDirectories() {
if (!sdInitialized) return false;
// Create logs directory
if (!SD_MMC.exists(LOGS_DIR)) {
if (SD_MMC.mkdir(LOGS_DIR)) {
Serial.println("Created /logs directory");
} else {
Serial.println("Failed to create /logs directory");
return false;
}
}
// Create config directory
if (!SD_MMC.exists(CONFIG_DIR)) {
if (SD_MMC.mkdir(CONFIG_DIR)) {
Serial.println("Created /config directory");
} else {
Serial.println("Failed to create /config directory");
return false;
}
}
return true;
}
bool startLogFile() {
if (!sdInitialized) return false;
// Close current file if open
closeLogFile();
// Generate filename with timestamp
time_t now = time(NULL);
struct tm* timeinfo = localtime(&now);
snprintf(currentLogFileName, sizeof(currentLogFileName),
"%s/CAN_%04d%02d%02d_%02d%02d%02d_%03d.pcap",
LOGS_DIR,
timeinfo->tm_year + 1900,
timeinfo->tm_mon + 1,
timeinfo->tm_mday,
timeinfo->tm_hour,
timeinfo->tm_min,
timeinfo->tm_sec,
fileSequenceNumber++);
// Open file for writing
currentLogFile = SD_MMC.open(currentLogFileName, FILE_WRITE);
if (!currentLogFile) {
Serial.printf("Failed to open log file: %s\n", currentLogFileName);
return false;
}
// Write PCAP global header
PCAPGlobalHeader header;
header.magicNumber = PCAP_MAGIC_NUMBER;
header.versionMajor = 2;
header.versionMinor = 4;
header.thiszone = 0;
header.sigfigs = 0;
header.snaplen = 65535;
header.network = PCAP_LINK_TYPE;
currentLogFile.write((uint8_t*)&header, sizeof(header));
currentLogFileSize = sizeof(header);
Serial.printf("Started new log file: %s\n", currentLogFileName);
return true;
}
void closeLogFile() {
flushWriteBuffer();
if (currentLogFile) {
currentLogFile.close();
Serial.printf("Closed log file: %s\n", currentLogFileName);
currentLogFileName[0] = '\0';
currentLogFileSize = 0;
}
}
bool writeCANFrameToSD(const CanFrame& frame) {
if (!sdInitialized || !currentLogFile) {
return false;
}
if (currentLogFileSize >= FILE_SPLIT_SIZE) {
flushWriteBuffer();
closeLogFile();
if (!startLogFile()) {
return false;
}
}
PCAPPacketHeader pktHeader;
pktHeader.tsSec = (uint32_t)(frame.timestamp / 1000000ULL);
pktHeader.tsUsec = (uint32_t)(frame.timestamp % 1000000ULL);
CANFDFrame canFrame;
canFrame.canId = frame.id;
canFrame.len = frame.len;
canFrame.flags = frame.flags;
canFrame.reserved0 = 0;
canFrame.reserved1 = 0;
memcpy(canFrame.data, frame.data, frame.len);
uint32_t frameSize = sizeof(CANFDFrame) - 64 + frame.len;
pktHeader.inclLen = frameSize;
pktHeader.origLen = frameSize;
size_t totalSize = sizeof(pktHeader) + frameSize;
if (bufferOffset + totalSize > PSRAM_SD_BUFFER_SIZE) {
if (!flushWriteBuffer()) {
return false;
}
}
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
memcpy(writeBuffer + bufferOffset, &pktHeader, sizeof(pktHeader));
bufferOffset += sizeof(pktHeader);
memcpy(writeBuffer + bufferOffset, &canFrame, frameSize);
bufferOffset += frameSize;
framesInBuffer++;
currentLogFileSize += totalSize;
xSemaphoreGive(sdMutex);
uint32_t now = millis();
if (now - lastFlushTime >= SD_FLUSH_INTERVAL_MS) {
flushWriteBuffer();
lastFlushTime = now;
}
return true;
}
return false;
}
bool flushWriteBuffer() {
if (bufferOffset == 0 || !currentLogFile) {
return true;
}
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(500)) == pdTRUE) {
size_t written = currentLogFile.write(writeBuffer, bufferOffset);
currentLogFile.flush();
xSemaphoreGive(sdMutex);
if (written != bufferOffset) {
Serial.printf("SD write incomplete: %d of %d bytes\n", written, bufferOffset);
bufferOffset = 0;
framesInBuffer = 0;
return false;
}
bufferOffset = 0;
framesInBuffer = 0;
return true;
}
return false;
}
void sdWriteTask(void *pvParameters) {
Serial.println("SD Write Task started on Core 1");
CanFrame frame;
uint32_t lastStatusTime = 0;
uint32_t framesWritten = 0;
uint32_t framesFromPSRAM = 0;
while (1) {
bool gotFrame = false;
if (xQueueReceive(canRxQueue, &frame, pdMS_TO_TICKS(10)) == pdTRUE) {
gotFrame = true;
} else if (canFrameBuffer.available() > 0) {
if (canFrameBuffer.pop(frame)) {
gotFrame = true;
framesFromPSRAM++;
}
}
if (gotFrame) {
if (!writeCANFrameToSD(frame)) {
canErrorCount++;
} else {
framesWritten++;
}
}
uint32_t now = millis();
if (now - lastStatusTime > 10000) {
Serial.printf("[SD] Written: %d, From PSRAM: %d, Buffer: %d, Q: %d, PSRAM: %d\n",
framesWritten, framesFromPSRAM, bufferOffset,
uxQueueMessagesWaiting(canRxQueue),
canFrameBuffer.available());
lastStatusTime = now;
}
}
}
uint64_t getSDCardSize() {
return sdInitialized ? sdCardSize : 0;
}
uint64_t getFreeSpace() {
if (!sdInitialized) return 0;
return SD_MMC.totalBytes() - SD_MMC.usedBytes();
}
uint64_t getUsedSpace() {
if (!sdInitialized) return 0;
return SD_MMC.usedBytes();
}
int listLogFiles(char* buffer, size_t bufferSize) {
if (!sdInitialized) return 0;
File root = SD_MMC.open(LOGS_DIR);
if (!root || !root.isDirectory()) {
return 0;
}
int count = 0;
size_t offset = 0;
File file = root.openNextFile();
while (file && offset < bufferSize - 128) {
if (!file.isDirectory() && strstr(file.name(), ".pcap")) {
int len = snprintf(buffer + offset, bufferSize - offset,
"{\"name\":\"%s\",\"size\":%d},",
file.name(), file.size());
offset += len;
count++;
}
file = root.openNextFile();
}
// Remove trailing comma
if (offset > 0 && buffer[offset - 1] == ',') {
buffer[offset - 1] = '\0';
}
return count;
}
bool getLogFileInfo(const char* filename, uint32_t& size, uint32_t& timestamp) {
if (!sdInitialized) return false;
char path[128];
snprintf(path, sizeof(path), "%s/%s", LOGS_DIR, filename);
File file = SD_MMC.open(path);
if (!file) return false;
size = file.size();
timestamp = file.getLastWrite();
file.close();
return true;
}
bool deleteLogFile(const char* filename) {
if (!sdInitialized) return false;
char path[128];
snprintf(path, sizeof(path), "%s/%s", LOGS_DIR, filename);
return SD_MMC.remove(path);
}
const char* getCurrentLogFilename() {
return currentLogFileName;
}