Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 70 additions & 28 deletions examples/companion_radio/DataStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
#define MAX_BLOBRECS 20
#endif

// Atomic writes require ~2x storage for contacts file
// Only enable on platforms with sufficient flash
#if !defined(NRF52_PLATFORM) || defined(EXTRAFS) || defined(QSPIFLASH)
#define HAS_ATOMIC_WRITE_SUPPORT
#endif

DataStore::DataStore(FILESYSTEM& fs, mesh::RTCClock& clock) : _fs(&fs), _fsExtra(nullptr), _clock(&clock),
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
identity_store(fs, "")
Expand Down Expand Up @@ -269,42 +275,63 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_
}

void DataStore::loadContacts(DataStoreHost* host) {
File file = openRead(_getContactsChannelsFS(), "/contacts3");
if (file) {
bool full = false;
while (!full) {
ContactInfo c;
uint8_t pub_key[32];
uint8_t unused;

bool success = (file.read(pub_key, 32) == 32);
success = success && (file.read((uint8_t *)&c.name, 32) == 32);
success = success && (file.read(&c.type, 1) == 1);
success = success && (file.read(&c.flags, 1) == 1);
success = success && (file.read(&unused, 1) == 1);
success = success && (file.read((uint8_t *)&c.sync_since, 4) == 4); // was 'reserved'
success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1);
success = success && (file.read((uint8_t *)&c.last_advert_timestamp, 4) == 4);
success = success && (file.read(c.out_path, 64) == 64);
success = success && (file.read((uint8_t *)&c.lastmod, 4) == 4);
success = success && (file.read((uint8_t *)&c.gps_lat, 4) == 4);
success = success && (file.read((uint8_t *)&c.gps_lon, 4) == 4);

if (!success) break; // EOF
FILESYSTEM* fs = _getContactsChannelsFS();
File file = openRead(fs, "/contacts3");

#ifdef HAS_ATOMIC_WRITE_SUPPORT
// If main file doesn't exist or is empty, try backup
if (!file || file.size() == 0) {
if (file) file.close();
if (fs->exists("/contacts3.bak")) {
MESH_DEBUG_PRINTLN("WARN: contacts3 missing/empty, loading from backup");
file = openRead(fs, "/contacts3.bak");
}
}
#endif

c.id = mesh::Identity(pub_key);
if (!host->onContactLoaded(c)) full = true;
}
file.close();
if (file) {
bool full = false;
while (!full) {
ContactInfo c;
uint8_t pub_key[32];
uint8_t unused;

bool success = (file.read(pub_key, 32) == 32);
success = success && (file.read((uint8_t *)&c.name, 32) == 32);
success = success && (file.read(&c.type, 1) == 1);
success = success && (file.read(&c.flags, 1) == 1);
success = success && (file.read(&unused, 1) == 1);
success = success && (file.read((uint8_t *)&c.sync_since, 4) == 4); // was 'reserved'
success = success && (file.read((uint8_t *)&c.out_path_len, 1) == 1);
success = success && (file.read((uint8_t *)&c.last_advert_timestamp, 4) == 4);
success = success && (file.read(c.out_path, 64) == 64);
success = success && (file.read((uint8_t *)&c.lastmod, 4) == 4);
success = success && (file.read((uint8_t *)&c.gps_lat, 4) == 4);
success = success && (file.read((uint8_t *)&c.gps_lon, 4) == 4);

if (!success) break; // EOF

c.id = mesh::Identity(pub_key);
if (!host->onContactLoaded(c)) full = true;
}
file.close();
}
}

void DataStore::saveContacts(DataStoreHost* host) {
File file = openWrite(_getContactsChannelsFS(), "/contacts3");
FILESYSTEM* fs = _getContactsChannelsFS();

#ifdef HAS_ATOMIC_WRITE_SUPPORT
File file = openWrite(fs, "/contacts3.tmp");
#else
File file = openWrite(fs, "/contacts3");
#endif

if (file) {
uint32_t idx = 0;
ContactInfo c;
uint8_t unused = 0;
bool write_success = true;

while (host->getContactForSave(idx, c)) {
bool success = (file.write(c.id.pub_key, 32) == 32);
Expand All @@ -320,11 +347,26 @@ void DataStore::saveContacts(DataStoreHost* host) {
success = success && (file.write((uint8_t *)&c.gps_lat, 4) == 4);
success = success && (file.write((uint8_t *)&c.gps_lon, 4) == 4);

if (!success) break; // write failed
if (!success) {
write_success = false;
break; // write failed
}

idx++; // advance to next contact
}
file.flush();
file.close();

#ifdef HAS_ATOMIC_WRITE_SUPPORT
if (write_success) {
fs->remove("/contacts3.bak");
fs->rename("/contacts3", "/contacts3.bak");
fs->rename("/contacts3.tmp", "/contacts3");
} else {
fs->remove("/contacts3.tmp");
MESH_DEBUG_PRINTLN("ERROR: saveContacts write failed");
}
#endif
}
}

Expand Down