Files
Vermix-Gate/lib/data/rfid.cpp
T
2025-10-06 18:27:50 +02:00

282 lines
7.7 KiB
C++

#include "rfid.hpp"
#include <logger.hpp>
RfidDB rfidDB;
RfidDB::RfidDB(const String& filename)
: filename_(filename), tmpFilename_(filename + ".tmp"), initialized_(false) {}
RfidDB::~RfidDB() {}
void printDatabaseContents() {
LOG_DEBUG("--- RFID Database Contents (Stored Values) ---");
rfidDB.iterate([](uint32_t stored_id) {
// 'stored_id' ist der Wert, wie er in der Datei steht (Byte-geswappt)
Serial.printf("Stored HEX: 0x%08X\n", stored_id);
});
LOG_DEBUG("-------------------------------------------");
}
bool RfidDB::begin() {
if (initialized_) {
return true;
}
if (!LittleFS.begin()) {
LOG_ERROR("RfidDB: LittleFS mount failed");
return false;
}
// Ensure the file exists, creating it if necessary.
if (!LittleFS.exists(filename_)) {
File f = LittleFS.open(filename_, "w");
if (!f) {
return false; // Could not create the file
}
f.close();
}
initialized_ = true;
printDatabaseContents();
return true;
}
uint32_t RfidDB::count() {
if (!initialized_) return 0;
File f = LittleFS.open(filename_, "r");
if (!f) return 0;
uint32_t n = fileEntryCount(f);
f.close();
return n;
}
bool RfidDB::contains(uint32_t raw_id) {
if (!initialized_) {
LOG_DEBUG("RfidDB: contains not initialized");
return false;
}
File f = LittleFS.open(filename_, "r");
if (!f) {
LOG_DEBUG("RfidDB: contains failed to open file");
return false;
}
uint32_t id = byteSwap(raw_id);
uint32_t idx;
bool found = false;
bool ok = binarySearch(f, idx, id, found);
f.close();
LOG_DEBUG("RfidDB: contains id=%08X, ok=%d, found=%d", raw_id, ok, found);
return ok && found;
}
void RfidDB::iterate(std::function<void(uint32_t)> callback) {
if (!initialized_ || !callback) return;
File f = LittleFS.open(filename_, "r");
if (!f) return;
uint32_t n = fileEntryCount(f);
for (uint32_t i = 0; i < n; ++i) {
uint32_t v;
if (readEntryAt(f, i, v)) {
callback(v);
}
}
f.close();
}
uint32_t RfidDB::fileEntryCount(File &f) {
return f.size() / ENTRY_SIZE;
}
// 🚀 More efficient read/write methods
bool RfidDB::readEntryAt(File &f, uint32_t index, uint32_t &out) {
if (!f.seek(index * ENTRY_SIZE, SeekSet)) return false;
return f.read(reinterpret_cast<uint8_t*>(&out), ENTRY_SIZE) == ENTRY_SIZE;
}
bool RfidDB::writeEntryAt(File &f, uint32_t index, uint32_t value) {
if (!f.seek(index * ENTRY_SIZE, SeekSet)) return false;
return f.write(reinterpret_cast<const uint8_t*>(&value), ENTRY_SIZE) == ENTRY_SIZE;
}
bool RfidDB::binarySearch(File &f, uint32_t &outIndex, uint32_t key, bool &found) {
// Get the number of entries in the file (each entry is 4 bytes)
uint32_t n = fileEntryCount(f);
// If the file is empty, return with outIndex = 0 and found = false
if (n == 0) {
outIndex = 0;
found = false;
return true;
}
// Initialize search boundaries for binary search
uint32_t left = 0;
uint32_t right = n - 1;
// Perform binary search on the sorted database
// Note: The database must contain values in ascending order of their *swapped* (Little-Endian) representation
// to ensure correct search results. The 'key' parameter is the byte-swapped (Little-Endian) value of the
// raw RFID ID (e.g., raw ID 0x635C426D is swapped to 0x6D425C63 for comparison).
while (left <= right) {
// Calculate the middle index
uint32_t mid = left + (right - left) / 2;
uint32_t v;
// Read the entry at index 'mid' from the file
// The file stores IDs as Big-Endian (e.g., bytes 63 5C 42 6D for original ID 0x635C426D).
// On this Little-Endian platform (e.g., ESP32), reading 4 bytes into 'v' interprets them as
// Little-Endian, so bytes 63 5C 42 6D become v = 0x6D425C63 (swapped).
if (!readEntryAt(f, mid, v)) {
return false; // Failed to read entry
}
// Compare the read value 'v' (Little-Endian, swapped) with the search key (also Little-Endian, swapped)
if (v == key) {
outIndex = mid;
found = true;
return true; // Found the ID at index 'mid'
}
// Since the database is sorted by the swapped (Little-Endian) values,
// adjust the search boundaries based on the comparison
if (v < key) {
left = mid + 1; // Search in the right half
} else {
if (mid == 0) break; // Prevent underflow when right = mid - 1
right = mid - 1; // Search in the left half
}
}
// If not found, set outIndex to the insertion point where the key would be added
// to maintain the sorted order of swapped (Little-Endian) values
outIndex = left;
found = false;
return true;
}
uint32_t RfidDB::byteSwap(uint32_t x) const
{
return ((x & 0xFF000000) >> 24) |
((x & 0x00FF0000) >> 8) |
((x & 0x0000FF00) << 8) |
((x & 0x000000FF) << 24);
}
bool RfidDB::add(uint32_t raw_id) {
if (!initialized_) return false;
File src = LittleFS.open(filename_, "r");
if (!src) return false;
uint32_t id = byteSwap(raw_id);
uint32_t idx;
bool found;
if (!binarySearch(src, idx, id, found)) {
src.close();
return false;
}
if (found) {
src.close();
return true; // Already present, we consider this a success
}
File dst = LittleFS.open(tmpFilename_, "w");
if (!dst) {
src.close();
return false;
}
bool success = true;
uint32_t n = fileEntryCount(src);
uint32_t v;
// Copy entries before the insertion point
for (uint32_t i = 0; i < idx; ++i) {
if (!readEntryAt(src, i, v) || !writeEntryAt(dst, i, v)) {
success = false;
break;
}
}
// Insert the new entry
if (success && !writeEntryAt(dst, idx, id)) {
success = false;
}
// Copy the remaining entries
if (success) {
for (uint32_t i = idx; i < n; ++i) {
if (!readEntryAt(src, i, v) || !writeEntryAt(dst, i + 1, v)) {
success = false;
break;
}
}
}
src.close();
dst.close();
if (!success) {
LittleFS.remove(tmpFilename_);
return false;
}
// Atomic replace
if (!LittleFS.rename(tmpFilename_, filename_)) {
// Fallback in case rename fails
LittleFS.remove(tmpFilename_);
return false;
}
return true;
}
bool RfidDB::remove(uint32_t raw_id) {
if (!initialized_) return false;
File src = LittleFS.open(filename_, "r");
if (!src) return false;
uint32_t id = byteSwap(raw_id);
uint32_t idx;
bool found;
if (!binarySearch(src, idx, id, found) || !found) {
src.close();
return false; // Not found, so nothing to remove
}
File dst = LittleFS.open(tmpFilename_, "w");
if (!dst) {
src.close();
return false;
}
bool success = true;
uint32_t n = fileEntryCount(src);
uint32_t written = 0;
uint32_t v;
for (uint32_t i = 0; i < n; ++i) {
if (i == idx) continue; // Skip the entry to be deleted
if (!readEntryAt(src, i, v) || !writeEntryAt(dst, written, v)) {
success = false;
break;
}
written++;
}
src.close();
dst.close();
if (!success) {
LittleFS.remove(tmpFilename_);
return false;
}
if (!LittleFS.rename(tmpFilename_, filename_)) {
LittleFS.remove(tmpFilename_);
return false;
}
return true;
}