282 lines
7.7 KiB
C++
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;
|
|
} |