forked from Akcelerometry_drgania_WMT/PI_mikrokontroler
605 lines
20 KiB
C++
605 lines
20 KiB
C++
#include <Measure.h>
|
||
|
||
static const char *TAG_CAPTURE = "CAPTURE";
|
||
|
||
// Był na sztywno przydzielony bufor 128 KB. Zmieniam tak, aby zaalokować 4 MB lub 6 MB (trzeba zostawić resztę na potrzeby systemu).
|
||
DataCapture::DataCapture(ADXL345FastSPI &adxl, Display &display, RTC_DS3231 &rtc, fs::FS &storage, size_t bufferSize)
|
||
: adxl_(adxl), display_(display), rtc_(rtc), _fs(storage){
|
||
// Ustawiamy duży bufor dla PSRAM (np. 4 MB = 4194304 bajty)
|
||
// 4 * 1024 * 1024 / 12 bajtów = ~349 525 próbek
|
||
// Możesz to przekazać jako parametr lub wpisać na sztywno:
|
||
if (bufferSize < 4 * 1024 * 1024) bufferSize = 4 * 1024 * 1024;
|
||
bufferSize_ = bufferSize;
|
||
|
||
#if defined(ESP32)
|
||
// ps_malloc jest kluczowy dla PSRAM. Wymuszamy alokację w zewnętrznym PSRAM
|
||
buffer_ = (uint8_t*)ps_malloc(bufferSize_);
|
||
#endif
|
||
|
||
if (!buffer_) buffer_ = (uint8_t*)malloc(bufferSize_);
|
||
if (!buffer_) {
|
||
ESP_LOGE(TAG_CAPTURE, "No mem for buffer (%u B)", (unsigned)bufferSize_);
|
||
} else {
|
||
ESP_LOGI(TAG_CAPTURE, "Capture buffer: %u B", (unsigned)bufferSize_);
|
||
}
|
||
}
|
||
|
||
DataCapture::~DataCapture() {
|
||
if (buffer_) { free(buffer_); buffer_ = nullptr; }
|
||
}
|
||
|
||
void DataCapture::stop() { measurementActive_ = false; }
|
||
|
||
// --- ZAPIS BUFORA NA SD ---
|
||
bool DataCapture::flushToFile(File& f) {
|
||
if (bufferIndex_ == 0) return true;
|
||
Watchdog::feed();
|
||
size_t written = f.write(buffer_, bufferIndex_);
|
||
if (written != bufferIndex_) {
|
||
ESP_LOGE(TAG_CAPTURE, "SD save error (written=%u, expected=%u)", (unsigned)written, (unsigned)bufferIndex_);
|
||
display_.textStatus("SD save error");
|
||
bufferIndex_ = 0;
|
||
return false;
|
||
}
|
||
bufferIndex_ = 0;
|
||
return true;
|
||
}
|
||
|
||
// CRC8 (zostawione – nieużywane domyślnie)
|
||
uint8_t DataCapture::crc8(const uint8_t* data, size_t len) {
|
||
uint8_t crc = 0x00;
|
||
for (size_t i = 0; i < len; ++i) {
|
||
crc ^= data[i];
|
||
for (uint8_t b = 0; b < 8; ++b) {
|
||
if (crc & 0x80) crc = (crc << 1) ^ 0x07; else crc <<= 1;
|
||
}
|
||
}
|
||
return crc;
|
||
}
|
||
|
||
void DataCapture::setTestingIndicator_(bool on, String myDir, String myFile) {
|
||
display_.displayOnOffM(on, myDir, myFile);
|
||
}
|
||
|
||
// --- automatyczna nazwa pliku ---
|
||
bool DataCapture::captureAuto(uint32_t captureSeconds, const char * /*baseDirectory*/) {
|
||
isExit = false;
|
||
Watchdog::feed();
|
||
String path = allocateNextFilePath();
|
||
if (path.isEmpty()) {
|
||
ESP_LOGE(TAG_CAPTURE, "Error generate file name in %s", _baseDir.c_str());
|
||
display_.textStatus("Error file");
|
||
return false;
|
||
}
|
||
ESP_LOGI(TAG_CAPTURE, "Autogenerate file: %s", path.c_str());
|
||
display_.textStatus(path.c_str());
|
||
Watchdog::feed();
|
||
return capture(captureSeconds, path.c_str());
|
||
}
|
||
|
||
// --- main measure function ---
|
||
// bool DataCapture::capture(uint32_t captureSeconds, const char *filename) {
|
||
// Watchdog::feed();
|
||
// if (!buffer_) {
|
||
// ESP_LOGE(TAG_CAPTURE, "No buffer - cancel.");
|
||
// display_.textStatus("Buffer error");
|
||
// return false;
|
||
// }
|
||
// ESP_LOGI(TAG_CAPTURE, "Capture %u sec. -> %s", (unsigned)captureSeconds, filename);
|
||
|
||
// File dataFile = _fs.open(filename, FILE_WRITE);
|
||
// if (!dataFile) {
|
||
// ESP_LOGE(TAG_CAPTURE, "Can't open file: %s", filename);
|
||
// char buf[100];
|
||
// snprintf(buf, sizeof(buf), "Can't open: %s", filename);
|
||
// display_.textStatus(buf);
|
||
// //s2etTestingIndicator_(false, _baseDir, filename);
|
||
// return false;
|
||
// }
|
||
|
||
// // Bufory RAMKI (po 1 próbce z każdego sensora)
|
||
// static const uint8_t MAXN = 7; // zgodnie z projektem
|
||
// int16_t X[MAXN]{}, Y[MAXN]{}, Z[MAXN]{};
|
||
// uint32_t TS[MAXN]{};
|
||
|
||
// Watchdog::feed();
|
||
// const uint32_t tStart_us = micros();
|
||
// const uint32_t captureDuration_us = captureSeconds * 1000000UL;
|
||
|
||
// // Czas startu (UTC) — tylko w nagłówku!
|
||
// const DateTime now = rtc_.now();
|
||
// const int32_t unix_start = now.unixtime();
|
||
|
||
// // Nagłówek
|
||
// FileHeader hdr;
|
||
// memcpy(hdr.magic, "WMT", 3);
|
||
// hdr.version = 1;
|
||
// hdr.headerSize = sizeof(FileHeader);
|
||
// hdr.sampleSize = sizeof(Sample);
|
||
// hdr.timestamp = unix_start;
|
||
// hdr.reccount = 0;
|
||
|
||
// if (dataFile.write(reinterpret_cast<const uint8_t*>(&hdr), sizeof(hdr)) != sizeof(hdr)) {
|
||
// ESP_LOGE(TAG_CAPTURE, "Header write failed");
|
||
// display_.textStatus("Header SD failed");
|
||
// dataFile.close();
|
||
// return false;
|
||
// }
|
||
|
||
// measurementActive_ = true;
|
||
// bufferIndex_ = 0;
|
||
// setTestingIndicator_(true, _baseDir, filename);
|
||
|
||
// uint32_t frames = 0; // liczba zebranych „ramek”
|
||
// const uint8_t presentCnt = adxl_.size(); // wykryte sensory (wg begin)
|
||
|
||
// // --- Pętla akwizycji wyrównanych ramek ---
|
||
// while (measurementActive_) {
|
||
// if(isEscape()) break; // wyjście z pętli gdy OK przerwie
|
||
// uint32_t now_us = micros();
|
||
// if ((now_us - tStart_us) >= captureDuration_us) break;
|
||
|
||
// // 1) Czekamy aż KAŻDY obecny sensor ma >= 1 próbkę w FIFO (DATA_READY)
|
||
// while (!adxl_.availableAll()) {
|
||
// if(isEscape()) break; //????? sprawdź kHz pomiaru!
|
||
// // krótki spin; unikamy delay(1), aby nie zrywać 3.2 kHz
|
||
// // (opcjonalnie yield(); jeśli system wymaga)
|
||
// }
|
||
|
||
// // 2) Zdejmij po 1 NAJSTARSZEJ próbce z każdego sensora (STREAM FIFO)
|
||
// const uint8_t got = adxl_.readAlignedOnce(X, Y, Z, TS);
|
||
// if (got != presentCnt) {
|
||
// // rzadki przypadek – niepełna ramka; pomiń
|
||
// continue;
|
||
// }
|
||
|
||
// // 3) Wspólny timestamp ramki: minimalny z TS[i]
|
||
// uint32_t tmin = UINT32_MAX;
|
||
// for (uint8_t i = 0; i < MAXN; ++i) {
|
||
// if (!adxl_.isPresent(i)) continue;
|
||
// if (TS[i] < tmin) tmin = TS[i];
|
||
// }
|
||
// const uint32_t frame_offset_us = tmin - tStart_us;
|
||
|
||
// // 4) Zapis 7 rekordów Sample do bufora
|
||
// for (uint8_t i = 0; i < MAXN; ++i) {
|
||
// if (!adxl_.isPresent(i)) continue;
|
||
|
||
// // Konstruuj rekord bezpośrednio w buforze (bez memcpy)
|
||
// if (bufferIndex_ + sizeof(Sample) > bufferSize_) {
|
||
// if (!flushToFile(dataFile)) { measurementActive_ = false; break; }
|
||
// }
|
||
// Sample *dst = reinterpret_cast<Sample*>(buffer_ + bufferIndex_);
|
||
// dst->offset = frame_offset_us;
|
||
// dst->sensor_id = i;
|
||
// dst->x = X[i]; dst->y = Y[i]; dst->z = Z[i];
|
||
// dst->ready = true;
|
||
// bufferIndex_ += sizeof(Sample);
|
||
// }
|
||
|
||
// if (!measurementActive_) break;
|
||
// frames++;
|
||
// // Watchdog rzadziej, żeby nie zwiększać jittera
|
||
// if ((frames & 0xFF) == 0) Watchdog::feed();
|
||
// } // while
|
||
// // Statystyki
|
||
// ESP_LOGI(TAG_CAPTURE, "Frames: %u, sensors: %u", (unsigned)frames, (unsigned)presentCnt);
|
||
// printSamplingRate(frames, captureSeconds); // liczymy ramki/sek.
|
||
|
||
// // Domknij bufor
|
||
// if (bufferIndex_ > 0) {
|
||
// if (!flushToFile(dataFile)) {
|
||
// Watchdog::feed();
|
||
// ESP_LOGE(TAG_CAPTURE, "Finish save error.");
|
||
// display_.textStatus("Save SD error");
|
||
// delay(1000);
|
||
// }
|
||
// }
|
||
// ESP_LOGI(TAG_CAPTURE, "Saved to SD successful");
|
||
// display_.textStatus("Saved SD OK");
|
||
// delay(700);
|
||
// // Uzupełnij nagłówek o liczbę rekordów (Sample)
|
||
// hdr.reccount = frames * presentCnt;
|
||
// dataFile.seek(0);
|
||
// dataFile.write(reinterpret_cast<const uint8_t*>(&hdr), sizeof(hdr));
|
||
// dataFile.flush();
|
||
// dataFile.close();
|
||
|
||
// //setTestingIndicator_(false, _baseDir, filename);
|
||
|
||
// bool ok = measurementActive_;
|
||
// measurementActive_ = false;
|
||
|
||
// if (ok) {
|
||
// ESP_LOGI(TAG_CAPTURE, "Successful");
|
||
// display_.textStatus("Successfull");
|
||
// Watchdog::feed();
|
||
// return true;
|
||
// } else {
|
||
// ESP_LOGE(TAG_CAPTURE, "Measurement aborted");
|
||
// display_.textStatus("Aborted");
|
||
// Watchdog::feed();
|
||
// return false;f
|
||
// }
|
||
// }
|
||
|
||
// --- main measure function --- V2
|
||
bool DataCapture::capture(uint32_t captureSeconds, const char *filename) {
|
||
Watchdog::feed();
|
||
if (!buffer_) {
|
||
ESP_LOGE(TAG_CAPTURE, "No buffer - cancel.");
|
||
display_.textStatus("Buffer error");
|
||
return false;
|
||
}
|
||
|
||
// Obliczamy ile danych maksymalnie może wejść do bufora
|
||
const uint8_t presentCnt = adxl_.size();
|
||
const size_t frameSize = presentCnt * sizeof(Sample);
|
||
|
||
ESP_LOGI(TAG_CAPTURE, "Start RAM capture: %u sec.", (unsigned)captureSeconds);
|
||
display_.textStatus("Sampling...");
|
||
|
||
static const uint8_t MAXN = 7;
|
||
int16_t X[MAXN]{}, Y[MAXN]{}, Z[MAXN]{};
|
||
uint32_t TS[MAXN]{};
|
||
|
||
measurementActive_ = true;
|
||
bufferIndex_ = 0;
|
||
uint32_t frames = 0;
|
||
|
||
const uint32_t tStart_us = micros();
|
||
const uint32_t captureDuration_us = captureSeconds * 1000000UL;
|
||
const DateTime now_start = rtc_.now(); // Czas dla nagłówka
|
||
|
||
// --- KRYTYCZNA PĘTLA POMIAROWA (ZERO SD) ---
|
||
while (measurementActive_) {
|
||
if(isEscape()) break;
|
||
uint32_t now_us = micros();
|
||
if ((now_us - tStart_us) >= captureDuration_us) break;
|
||
|
||
// Sprawdzenie czy mamy miejsce w PSRAM na kolejną pełną ramkę
|
||
if (bufferIndex_ + frameSize > bufferSize_) {
|
||
ESP_LOGW(TAG_CAPTURE, "PSRAM Buffer Full! Stopping.");
|
||
break;
|
||
}
|
||
|
||
// Czekamy na dane z sensora
|
||
uint32_t start_wait = millis();
|
||
while (!adxl_.availableAll()) {
|
||
if (isEscape()) break;
|
||
if (millis() - start_wait > 500) {
|
||
ESP_LOGE(TAG_CAPTURE, "Sensor data timeout - aborting capture.");
|
||
measurementActive_ = false;
|
||
break;
|
||
}
|
||
Watchdog::feed();
|
||
yield();
|
||
}
|
||
|
||
if (!measurementActive_) break;
|
||
|
||
const uint8_t got = adxl_.readAlignedOnce(X, Y, Z, TS);
|
||
if (got == 0 || got != presentCnt) continue;
|
||
|
||
// Wspólny timestamp ramki
|
||
uint32_t tmin = UINT32_MAX;
|
||
for (uint8_t i = 0; i < MAXN; ++i) {
|
||
if (!adxl_.isPresent(i)) continue;
|
||
if (TS[i] < tmin) tmin = TS[i];
|
||
}
|
||
const uint32_t frame_offset_us = tmin - tStart_us;
|
||
|
||
// Zapis do PSRAM
|
||
for (uint8_t i = 0; i < MAXN; ++i) {
|
||
if (!adxl_.isPresent(i)) continue;
|
||
Sample *dst = reinterpret_cast<Sample*>(buffer_ + bufferIndex_);
|
||
dst->offset = frame_offset_us;
|
||
dst->sensor_id = i;
|
||
dst->x = X[i]; dst->y = Y[i]; dst->z = Z[i];
|
||
dst->ready = true;
|
||
bufferIndex_ += sizeof(Sample);
|
||
}
|
||
frames++;
|
||
if ((frames & 0x3FF) == 0) Watchdog::feed(); // Rzadziej, by nie siać jittera
|
||
} // --- KONIEC PĘTLI POMIAROWEJ ---
|
||
|
||
measurementActive_ = false;
|
||
ESP_LOGI(TAG_CAPTURE, "Capture finished. Samples in RAM: %u. Writing to SD...", (unsigned)frames);
|
||
display_.textStatus("Saving to SD...");
|
||
|
||
// Dopiero teraz otwieramy plik na SD
|
||
File dataFile = _fs.open(filename, FILE_WRITE);
|
||
if (!dataFile) {
|
||
ESP_LOGE(TAG_CAPTURE, "Can't open SD file!");
|
||
display_.textStatus("SD Open Error");
|
||
return false;
|
||
}
|
||
|
||
// Przygotowanie nagłówka
|
||
FileHeader hdr;
|
||
memcpy(hdr.magic, "WMT", 3);
|
||
hdr.version = 1;
|
||
hdr.headerSize = sizeof(FileHeader);
|
||
hdr.sampleSize = sizeof(Sample);
|
||
hdr.timestamp = now_start.unixtime();
|
||
hdr.reccount = frames * presentCnt;
|
||
|
||
// Zapis nagłówka
|
||
dataFile.write(reinterpret_cast<const uint8_t*>(&hdr), sizeof(hdr));
|
||
|
||
// Zapis całego bufora PSRAM jednym ciągiem
|
||
size_t written = dataFile.write(buffer_, bufferIndex_);
|
||
|
||
if (written == bufferIndex_) {
|
||
ESP_LOGI(TAG_CAPTURE, "SD Save Successful: %u bytes", (unsigned)written);
|
||
//display_.textStatus("Save OK");
|
||
display_.textStatus(basenameFromPath(filename));
|
||
delay(2000); // Tutaj zrob to inaczej
|
||
} else {
|
||
ESP_LOGE(TAG_CAPTURE, "SD Write Error!");
|
||
display_.textStatus("SD Write Err");
|
||
}
|
||
|
||
dataFile.close();
|
||
printSamplingRate(frames, captureSeconds, filename);
|
||
return (written == bufferIndex_);
|
||
}
|
||
|
||
|
||
// --- Narzędzia SD i pierdoły ---
|
||
String DataCapture::unixToDateTime(uint32_t ts) {
|
||
DateTime dt(ts);
|
||
char buf[25];
|
||
snprintf(buf, sizeof(buf), "%04u-%02u-%02u %02u:%02u:%02u",
|
||
dt.year(), dt.month(), dt.day(), dt.hour(), dt.minute(), dt.second());
|
||
return String(buf);
|
||
}
|
||
|
||
void DataCapture::readHeaderAndPrint(const char *path) {
|
||
Watchdog::feed();
|
||
File f = SD.open(path, FILE_READ);
|
||
if (!f) { ESP_LOGE(TAG_CAPTURE,"Error open file %s", path); return; }
|
||
|
||
FileHeader hdr;
|
||
size_t rd = f.read((uint8_t*)&hdr, sizeof(hdr));
|
||
f.close();
|
||
if (rd != sizeof(hdr)) { ESP_LOGE(TAG_CAPTURE,"Header error"); return; }
|
||
|
||
if (memcmp(hdr.magic, "WMT", 3) != 0) { ESP_LOGE(TAG_CAPTURE,"File signature error"); return; }
|
||
|
||
ESP_LOGI(TAG_CAPTURE,"===== WMT file header =====");
|
||
ESP_LOGI(TAG_CAPTURE,"Signature: %.3s", hdr.magic);
|
||
ESP_LOGI(TAG_CAPTURE,"Version: %u", hdr.version);
|
||
ESP_LOGI(TAG_CAPTURE,"Header size:%u B", hdr.headerSize);
|
||
ESP_LOGI(TAG_CAPTURE,"Sample size:%u B", hdr.sampleSize);
|
||
ESP_LOGI(TAG_CAPTURE,"Timestamp: %d", hdr.timestamp);
|
||
ESP_LOGI(TAG_CAPTURE,"Reccount: %u", hdr.reccount);
|
||
ESP_LOGI(TAG_CAPTURE,"===========================");
|
||
}
|
||
|
||
void DataCapture::printSamplingRate(uint32_t reccount, uint32_t captureSeconds, String filename) {
|
||
Watchdog::feed();
|
||
if (captureSeconds == 0) { ESP_LOGW(TAG_CAPTURE, "Sampling time = 0!"); return; }
|
||
float fs = (float)reccount / (float)captureSeconds; // Hz (ramki/s)
|
||
float fs_kHz = fs / 1000.0f;
|
||
ESP_LOGI(TAG_CAPTURE,"Frames: %u", (unsigned)reccount);
|
||
ESP_LOGI(TAG_CAPTURE,"Time: %u s", (unsigned)captureSeconds);
|
||
ESP_LOGI(TAG_CAPTURE,"Rate: %.3f kHz (frames)", fs_kHz);
|
||
display_.displaySampleRateSummary(reccount, captureSeconds, fs_kHz, filename);
|
||
display_.textStatus("Summary");
|
||
delay(2000);
|
||
}
|
||
|
||
// --- Zarządzanie katalogami/plikiem ---
|
||
String DataCapture::allocateNextFilePath() {
|
||
Watchdog::feed();
|
||
uint32_t highestDir = findHighestNumericDir();
|
||
if (highestDir == 0) {
|
||
highestDir = 1;
|
||
if (!ensureDir(highestDir)) return String();
|
||
}
|
||
uint32_t count = 0, highestIdx = 0;
|
||
scanDirForWmt(highestDir, count, highestIdx);
|
||
|
||
uint32_t targetDir = highestDir;
|
||
uint32_t nextIdx = 0;
|
||
|
||
if (count > _maxFilesPerDir) {
|
||
targetDir = highestDir + 1;
|
||
if (!ensureDir(targetDir)) return String();
|
||
nextIdx = 1;
|
||
} else {
|
||
nextIdx = (highestIdx == 0) ? 1 : (highestIdx + 1);
|
||
}
|
||
return joinPath(dirPath(targetDir), makeIndexedName(nextIdx));
|
||
}
|
||
|
||
String DataCapture::generateNextFilename() {
|
||
Watchdog::feed();
|
||
uint32_t highestDir = findHighestNumericDir();
|
||
if (highestDir == 0) return String();
|
||
|
||
uint32_t count = 0, highestIdx = 0;
|
||
scanDirForWmt(highestDir, count, highestIdx);
|
||
ESP_LOGI(TAG_CAPTURE, "highestDir %u, count %u, highestIdx %u", (unsigned)highestDir, (unsigned)count, (unsigned)highestIdx);
|
||
if (highestIdx == 0) return String();
|
||
return joinPath(dirPath(highestDir), makeIndexedName(highestIdx));
|
||
}
|
||
|
||
FileInfo DataCapture::getLastFileInfo() {
|
||
FileInfo info{String(), 0u, false};
|
||
Watchdog::feed();
|
||
String lastPath = generateNextFilename();
|
||
if (lastPath.isEmpty()) return info;
|
||
|
||
if (_fs.exists(lastPath)) {
|
||
File f = _fs.open(lastPath, FILE_READ);
|
||
if (f) {
|
||
info.exists = true;
|
||
info.path = lastPath;
|
||
info.size = f.size();
|
||
f.close();
|
||
}
|
||
}
|
||
return info;
|
||
}
|
||
|
||
bool DataCapture::deleteAllOnSD() {
|
||
Watchdog::feed();
|
||
File root = _fs.open(_baseDir);
|
||
if (!root || !root.isDirectory()) return false;
|
||
|
||
for (File e = root.openNextFile(); e; e = root.openNextFile()) {
|
||
Watchdog::feed();
|
||
String child = String(e.name());
|
||
e.close();
|
||
if (!recursiveDelete(child)) return false;
|
||
}
|
||
root.close();
|
||
return true;
|
||
}
|
||
|
||
SpaceInfo DataCapture::freeSpaceMB() {
|
||
Watchdog::feed();
|
||
SpaceInfo si{0.0, "UNKNOWN"};
|
||
#if defined(ESP32)
|
||
uint64_t total = SD.totalBytes();
|
||
uint64_t used = SD.usedBytes();
|
||
if (total >= used) {
|
||
uint64_t freeB = total - used;
|
||
const double oneGB = 1024.0 * 1024.0 * 1024.0;
|
||
const double oneMB = 1024.0 * 1024.0;
|
||
if (freeB >= (uint64_t)oneGB) {
|
||
si.value = (double)freeB / oneGB; si.unit = "GB";
|
||
} else {
|
||
si.value = (double)freeB / oneMB; si.unit = "MB";
|
||
}
|
||
}
|
||
#endif
|
||
return si;
|
||
}
|
||
|
||
float DataCapture::freeSpaceFloat(bool* isGB) {
|
||
Watchdog::feed();
|
||
SpaceInfo si = freeSpaceMB();
|
||
if (!si.unit || strcmp(si.unit, "UNKNOWN") == 0) {
|
||
if (isGB) *isGB = false;
|
||
return -1.0f;
|
||
}
|
||
if (isGB) *isGB = (strcmp(si.unit, "GB") == 0);
|
||
return static_cast<float>(si.value);
|
||
}
|
||
|
||
// --- helpery pomocnicze ---
|
||
|
||
bool DataCapture::isAllDigits(const char *s) {
|
||
if (!s || !*s) return false;
|
||
while (*s) { if (*s < '0' || *s > '9') return false; ++s; }
|
||
return true;
|
||
}
|
||
|
||
uint32_t DataCapture::toUint(const char *s) {
|
||
uint32_t v = 0; while (*s) { v = v*10u + uint32_t(*s - '0'); ++s; } return v;
|
||
}
|
||
|
||
String DataCapture::dirPath(uint32_t dirNum) const { return joinPath(_baseDir, String(dirNum)); }
|
||
|
||
const char *DataCapture::basenameFromPath(const char *full) { const char *slash = strrchr(full, '/'); return slash ? slash + 1 : full; }
|
||
|
||
bool DataCapture::isWmtWithDigits(const char* name, uint32_t& idxOut) const {
|
||
size_t nlen = strlen(name), extLen = _ext.length();
|
||
if (nlen != (size_t)_digits + extLen) return false;
|
||
if (strncmp(name + _digits, _ext.c_str(), extLen) != 0) return false;
|
||
for (uint8_t i = 0; i < _digits; ++i) if (name[i] < '0' || name[i] > '9') return false;
|
||
char buf[16]; memcpy(buf, name, _digits); buf[_digits] = '\0';
|
||
idxOut = (uint32_t)strtoul(buf, nullptr, 10); return true;
|
||
}
|
||
|
||
String DataCapture::makeIndexedName(uint32_t idx) const {
|
||
char fmt[8]; snprintf(fmt, sizeof(fmt), "%%0%ulu", (unsigned long)_digits);
|
||
char num[24]; snprintf(num, sizeof(num), fmt, (unsigned long)idx);
|
||
return String(num) + _ext;
|
||
}
|
||
|
||
String DataCapture::joinPath(const String &a, const String &b) { if (a.endsWith("/")) return a + b; return a + "/" + b; }
|
||
bool DataCapture::ensureDir(uint32_t dirNum) {
|
||
String path = dirPath(dirNum);
|
||
if (_fs.exists(path)) { File f = _fs.open(path); bool ok = f && f.isDirectory(); if (f) f.close(); return ok; }
|
||
return _fs.mkdir(path);
|
||
}
|
||
|
||
uint32_t DataCapture::findHighestNumericDir() {
|
||
File root = _fs.open(_baseDir);
|
||
if (!root || !root.isDirectory()) return 0;
|
||
uint32_t maxDir = 0;
|
||
for (File f = root.openNextFile(); f; f = root.openNextFile()) {
|
||
if (f.isDirectory()) {
|
||
const char *nm = basenameFromPath(f.name());
|
||
if (isAllDigits(nm)) { uint32_t v = toUint(nm); if (v > maxDir) maxDir = v; }
|
||
}
|
||
}
|
||
return maxDir;
|
||
}
|
||
|
||
void DataCapture::scanDirForWmt(uint32_t dirNum, uint32_t &count, uint32_t &highestIdx) {
|
||
Watchdog::feed();
|
||
count = 0; highestIdx = 0;
|
||
String path = dirPath(dirNum);
|
||
File dir = _fs.open(path);
|
||
if (!dir || !dir.isDirectory()) return;
|
||
for (File f = dir.openNextFile(); f; f = dir.openNextFile()) {
|
||
if (f.isDirectory()) { Watchdog::feed(); f.close(); continue; }
|
||
const char* base = basenameFromPath(f.name());
|
||
uint32_t idx = 0;
|
||
if (isWmtWithDigits(base, idx)) { count++; if (idx > highestIdx) highestIdx = idx; }
|
||
f.close();
|
||
}
|
||
}
|
||
|
||
bool DataCapture::recursiveDelete(const String &path) {
|
||
File e = _fs.open(path);
|
||
if (!e) return false;
|
||
if (!e.isDirectory()) { e.close(); return _fs.remove(path); }
|
||
// katalog
|
||
for (File c = e.openNextFile(); c; c = e.openNextFile()) {
|
||
String child = String(c.name()); c.close();
|
||
if (!recursiveDelete(child)) { e.close(); return false; }
|
||
}
|
||
e.close();
|
||
if (path == _baseDir) return true; // nie usuwamy katalogu bazowego
|
||
return _fs.rmdir(path);
|
||
}
|
||
|
||
void DataCapture::printLastFileInfoSerial() {
|
||
Watchdog::feed();
|
||
const FileInfo info = getLastFileInfo();
|
||
if (!info.exists) {
|
||
ESP_LOGE(TAG_CAPTURE, "No .wmt file exists. Last file not exists");
|
||
return;
|
||
}
|
||
|
||
const double kb = static_cast<double>(info.size) / 1024.0;
|
||
const double mb = kb / 1024.0;
|
||
|
||
#if defined(ESP32)
|
||
// ESP32 wspiera Serial.printf z %llu
|
||
ESP_LOGI(TAG_CAPTURE, "Last file: %s", info.path.c_str());
|
||
ESP_LOGI(TAG_CAPTURE, "Size: %llu B (%.2f KB, %.2f MB)", static_cast<unsigned long long>(info.size), kb, mb);
|
||
char buf[100];
|
||
snprintf(buf, sizeof(buf), "Size:%.2f KB", kb);
|
||
display_.textStatus(buf);
|
||
#else
|
||
// Wariant zachowawczy dla platform bez %llu; pokazujemy rozmiar w MB/KB.
|
||
ESP_LOGI(TAG_CAPTURE, "Last file info: "));
|
||
ESP_LOGI(TAG_CAPTURE, info.path);
|
||
ESP_LOGI(TAG_CAPTURE, "Size: "));
|
||
ESP_LOGI(TAG_CAPTURE, mb);
|
||
//ESP_LOGI(TAG_CAPTURE, " MB"));
|
||
//Serial.print(F(" ("));
|
||
//Serial.print(kb, 2);
|
||
//Serial.println(F(" KB)"));
|
||
#endif
|
||
|
||
}
|
||
|