Unify SD persistence runtime

This commit is contained in:
Trail Mate Dev
2026-05-30 18:43:31 +08:00
parent 16228ecafe
commit 7dcccdf171
12 changed files with 372 additions and 190 deletions
@@ -4,7 +4,6 @@
#include "freertos/semphr.h"
#include <Arduino.h>
#include <SD.h>
namespace gps
{
@@ -60,8 +60,15 @@ class SdRuntimeFile
bool open(const char* path, const char* mode);
void close();
bool is_open() const;
int available() const;
int read(void* buffer, std::size_t bytes_to_read);
int read_byte();
std::size_t read_bytes(char* buffer, std::size_t bytes_to_read);
std::size_t write(const void* buffer, std::size_t bytes_to_write);
std::size_t write_byte(uint8_t value);
std::size_t print(const char* text);
std::size_t print(double value, int digits = 2);
std::size_t printf(const char* format, ...);
bool seek(uint64_t offset);
uint64_t position() const;
uint64_t size() const;
@@ -5,8 +5,9 @@
#include "platform/esp/arduino_common/chat/infra/contact_store.h"
#include "../internal/blob_store_io.h"
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
#include <SD.h>
#include <Arduino.h>
namespace chat
{
@@ -30,7 +31,9 @@ ContactStore::ContactStore()
void ContactStore::begin()
{
backend_ = (SD.cardType() != CARD_NONE) ? StorageBackend::Sd : StorageBackend::Flash;
backend_ = ::platform::esp::arduino_common::storage::sd_card_ready()
? StorageBackend::Sd
: StorageBackend::Flash;
CONTACT_STORE_LOG("[ContactStore] backend=%s\n",
backend_ == StorageBackend::Sd ? "sd" : "flash");
core_.begin();
@@ -6,8 +6,9 @@
#include "platform/esp/arduino_common/chat/infra/meshtastic/node_store.h"
#include "../../internal/blob_store_io.h"
#include "chat/infra/node_store_blob_format.h"
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
#include <SD.h>
#include <Arduino.h>
#include <cstdio>
#include <esp_err.h>
#include <nvs.h>
@@ -89,7 +90,9 @@ NodeStore::NodeStore()
void NodeStore::begin()
{
backend_ = (SD.cardType() != CARD_NONE) ? StorageBackend::Sd : StorageBackend::Nvs;
backend_ = ::platform::esp::arduino_common::storage::sd_card_ready()
? StorageBackend::Sd
: StorageBackend::Nvs;
NODE_STORE_LOG("[NodeStore] backend=%s\n",
backend_ == StorageBackend::Sd ? "sd" : "nvs");
core_.begin();
@@ -201,9 +204,10 @@ bool NodeStore::saveBlob(const uint8_t* data, size_t len)
void NodeStore::clearBlob()
{
if (SD.cardType() != CARD_NONE && SD.exists(kPersistNodesFile))
if (::platform::esp::arduino_common::storage::sd_card_ready() &&
::platform::esp::arduino_common::storage::sd_exists(kPersistNodesFile))
{
SD.remove(kPersistNodesFile);
::platform::esp::arduino_common::storage::sd_remove(kPersistNodesFile);
}
clearNvs();
}
@@ -300,14 +304,14 @@ bool NodeStore::loadFromNvs(std::vector<uint8_t>& out)
bool NodeStore::loadFromSd(std::vector<uint8_t>& out) const
{
if (SD.cardType() == CARD_NONE)
if (!::platform::esp::arduino_common::storage::sd_card_ready())
{
NODE_STORE_LOG("[NodeStore] load SD skipped: card none\n");
return false;
}
File file = SD.open(kPersistNodesFile, FILE_READ);
if (!file)
::platform::esp::arduino_common::storage::SdRuntimeFile file;
if (!file.open(kPersistNodesFile, "r"))
{
NODE_STORE_LOG("[NodeStore] load SD open failed path=%s\n", kPersistNodesFile);
return false;
@@ -452,7 +456,7 @@ bool NodeStore::saveToNvs(const uint8_t* data, size_t len) const
bool NodeStore::saveToSd(const uint8_t* data, size_t len) const
{
if (SD.cardType() == CARD_NONE)
if (!::platform::esp::arduino_common::storage::sd_card_ready())
{
return false;
}
@@ -463,13 +467,13 @@ bool NodeStore::saveToSd(const uint8_t* data, size_t len) const
}
const std::string temp_path = std::string(kPersistNodesFile) + ".tmp";
if (SD.exists(temp_path.c_str()))
if (::platform::esp::arduino_common::storage::sd_exists(temp_path.c_str()))
{
SD.remove(temp_path.c_str());
::platform::esp::arduino_common::storage::sd_remove(temp_path.c_str());
}
File file = SD.open(temp_path.c_str(), FILE_WRITE);
if (!file)
::platform::esp::arduino_common::storage::SdRuntimeFile file;
if (!file.open(temp_path.c_str(), "w"))
{
return false;
}
@@ -485,18 +489,18 @@ bool NodeStore::saveToSd(const uint8_t* data, size_t len) const
file.close();
if (!ok)
{
SD.remove(temp_path.c_str());
::platform::esp::arduino_common::storage::sd_remove(temp_path.c_str());
return false;
}
if (SD.exists(kPersistNodesFile))
if (::platform::esp::arduino_common::storage::sd_exists(kPersistNodesFile))
{
SD.remove(kPersistNodesFile);
::platform::esp::arduino_common::storage::sd_remove(kPersistNodesFile);
}
if (!SD.rename(temp_path.c_str(), kPersistNodesFile))
if (!::platform::esp::arduino_common::storage::sd_rename(temp_path.c_str(), kPersistNodesFile))
{
SD.remove(temp_path.c_str());
::platform::esp::arduino_common::storage::sd_remove(temp_path.c_str());
return false;
}
@@ -5,8 +5,9 @@
#include "blob_store_io.h"
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
#include <Preferences.h>
#include <SD.h>
#include <string>
namespace chat
@@ -87,13 +88,14 @@ bool savePreferencesMetadata(Preferences& prefs,
bool loadRawBlobFromSd(const char* path, std::vector<uint8_t>& out)
{
out.clear();
if (!path || path[0] == '\0' || SD.cardType() == CARD_NONE)
if (!path || path[0] == '\0' ||
!::platform::esp::arduino_common::storage::sd_card_ready())
{
return false;
}
File file = SD.open(path, FILE_READ);
if (!file)
::platform::esp::arduino_common::storage::SdRuntimeFile file;
if (!file.open(path, "r"))
{
return false;
}
@@ -118,7 +120,8 @@ bool loadRawBlobFromSd(const char* path, std::vector<uint8_t>& out)
bool saveRawBlobToSd(const char* path, const uint8_t* data, size_t len)
{
if (!path || path[0] == '\0' || SD.cardType() == CARD_NONE)
if (!path || path[0] == '\0' ||
!::platform::esp::arduino_common::storage::sd_card_ready())
{
return false;
}
@@ -129,13 +132,13 @@ bool saveRawBlobToSd(const char* path, const uint8_t* data, size_t len)
return false;
}
if (SD.exists(temp_path.c_str()))
if (::platform::esp::arduino_common::storage::sd_exists(temp_path.c_str()))
{
SD.remove(temp_path.c_str());
::platform::esp::arduino_common::storage::sd_remove(temp_path.c_str());
}
File file = SD.open(temp_path.c_str(), FILE_WRITE);
if (!file)
::platform::esp::arduino_common::storage::SdRuntimeFile file;
if (!file.open(temp_path.c_str(), "w"))
{
return false;
}
@@ -148,18 +151,18 @@ bool saveRawBlobToSd(const char* path, const uint8_t* data, size_t len)
file.close();
if (!ok)
{
SD.remove(temp_path.c_str());
::platform::esp::arduino_common::storage::sd_remove(temp_path.c_str());
return false;
}
if (SD.exists(path))
if (::platform::esp::arduino_common::storage::sd_exists(path))
{
SD.remove(path);
::platform::esp::arduino_common::storage::sd_remove(path);
}
if (!SD.rename(temp_path.c_str(), path))
if (!::platform::esp::arduino_common::storage::sd_rename(temp_path.c_str(), path))
{
SD.remove(temp_path.c_str());
::platform::esp::arduino_common::storage::sd_remove(temp_path.c_str());
return false;
}
@@ -1,4 +1,5 @@
#include "platform/esp/arduino_common/gps/track_recorder.h"
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
#include "platform/esp/common/shared_spi_lock.h"
#include <cmath>
@@ -9,6 +10,14 @@ namespace gps
namespace
{
using ::platform::esp::arduino_common::storage::SdRuntimeDir;
using ::platform::esp::arduino_common::storage::SdRuntimeFile;
using ::platform::esp::arduino_common::storage::sd_card_ready;
using ::platform::esp::arduino_common::storage::sd_exists;
using ::platform::esp::arduino_common::storage::sd_is_directory;
using ::platform::esp::arduino_common::storage::sd_mkdir;
using ::platform::esp::arduino_common::storage::sd_remove;
constexpr const char* kGpxHeader =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<gpx version=\"1.1\" creator=\"Trail-Mate\" xmlns=\"http://www.topografix.com/GPX/1/1\">\n"
@@ -58,15 +67,15 @@ TrackRecorder& TrackRecorder::getInstance()
bool TrackRecorder::ensureDir() const
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
if (SD.exists(kTrackDir))
if (sd_exists(kTrackDir))
{
return true;
return sd_is_directory(kTrackDir);
}
return SD.mkdir(kTrackDir);
return sd_mkdir(kTrackDir);
}
String TrackRecorder::makeTrackPath() const
@@ -126,8 +135,8 @@ const char* TrackRecorder::formatExtension() const
void TrackRecorder::beginNewFile()
{
current_path_ = makeTrackPath();
File f = SD.open(current_path_.c_str(), FILE_WRITE);
if (!f)
SdRuntimeFile f;
if (!f.open(current_path_.c_str(), "w"))
{
current_path_ = "";
return;
@@ -207,8 +216,8 @@ void TrackRecorder::stop()
if (recording_ && current_path_.length() > 0)
{
File f = SD.open(current_path_.c_str(), FILE_APPEND);
if (f)
SdRuntimeFile f;
if (f.open(current_path_.c_str(), "a"))
{
if (format_ == TrackFormat::GPX)
{
@@ -250,8 +259,8 @@ void TrackRecorder::setAutoRecording(bool enabled)
{
if (current_path_.length() > 0)
{
File f = SD.open(current_path_.c_str(), FILE_APPEND);
if (f)
SdRuntimeFile f;
if (f.open(current_path_.c_str(), "a"))
{
if (format_ == TrackFormat::GPX)
{
@@ -311,8 +320,8 @@ void TrackRecorder::setFormat(TrackFormat format)
if (current_path_.length() > 0)
{
File f = SD.open(current_path_.c_str(), FILE_APPEND);
if (f)
SdRuntimeFile f;
if (f.open(current_path_.c_str(), "a"))
{
if (prev_format == TrackFormat::GPX)
{
@@ -367,7 +376,7 @@ void TrackRecorder::appendPoint(const TrackPoint& pt)
return;
}
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
if (mutex_)
{
@@ -424,8 +433,8 @@ void TrackRecorder::appendPoint(const TrackPoint& pt)
}
}
File f = SD.open(current_path_.c_str(), FILE_APPEND);
if (f)
SdRuntimeFile f;
if (f.open(current_path_.c_str(), "a"))
{
if (format_ == TrackFormat::CSV)
{
@@ -489,16 +498,16 @@ bool TrackRecorder::restoreActiveSession()
bool ok = false;
do
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
break;
}
if (!SD.exists(kActivePath))
if (!sd_exists(kActivePath))
{
break;
}
File f = SD.open(kActivePath, FILE_READ);
if (!f)
SdRuntimeFile f;
if (!f.open(kActivePath, "r"))
{
break;
}
@@ -532,7 +541,7 @@ bool TrackRecorder::restoreActiveSession()
f.close();
String path(path_buf);
if (path.isEmpty() || !SD.exists(path.c_str()))
if (path.isEmpty() || !sd_exists(path.c_str()))
{
clearActiveStateLocked();
break;
@@ -577,7 +586,7 @@ void TrackRecorder::updateActiveStateLocked()
bool TrackRecorder::writeActiveStateLocked() const
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
@@ -586,12 +595,12 @@ bool TrackRecorder::writeActiveStateLocked() const
return false;
}
if (SD.exists(kActivePath))
if (sd_exists(kActivePath))
{
SD.remove(kActivePath);
sd_remove(kActivePath);
}
File f = SD.open(kActivePath, FILE_WRITE);
if (!f)
SdRuntimeFile f;
if (!f.open(kActivePath, "w"))
{
return false;
}
@@ -631,37 +640,38 @@ bool TrackRecorder::writeActiveStateLocked() const
void TrackRecorder::clearActiveStateLocked() const
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return;
}
if (SD.exists(kActivePath))
if (sd_exists(kActivePath))
{
SD.remove(kActivePath);
sd_remove(kActivePath);
}
}
size_t TrackRecorder::listTracks(String* out_names, size_t max_names) const
{
if (SD.cardType() == CARD_NONE || max_names == 0 || out_names == nullptr)
if (!sd_card_ready() || max_names == 0 || out_names == nullptr)
{
return 0;
}
File dir = SD.open(kTrackDir);
if (!dir || !dir.isDirectory())
SdRuntimeDir dir;
if (!dir.open(kTrackDir))
{
return 0;
}
size_t count = 0;
for (File f = dir.openNextFile(); f && count < max_names; f = dir.openNextFile())
char name_buf[128];
bool is_dir = false;
while (count < max_names && dir.read_next(name_buf, sizeof(name_buf), &is_dir))
{
if (!f.isDirectory())
if (!is_dir)
{
out_names[count++] = String(f.name());
out_names[count++] = String(name_buf);
}
f.close();
}
dir.close();
return count;
@@ -1,8 +1,8 @@
#include "platform/ui/route_storage.h"
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
#include "platform/ui/device_runtime.h"
#include <SD.h>
#include <algorithm>
#include <cctype>
@@ -40,24 +40,24 @@ bool list_routes(std::vector<std::string>& out_routes, std::size_t max_count)
return false;
}
File dir = SD.open(kRouteDir);
if (!dir || !dir.isDirectory())
::platform::esp::arduino_common::storage::SdRuntimeDir dir;
if (!dir.open(kRouteDir))
{
return false;
}
for (File file = dir.openNextFile(); file && out_routes.size() < max_count; file = dir.openNextFile())
char name_buf[128];
bool is_dir = false;
while (out_routes.size() < max_count && dir.read_next(name_buf, sizeof(name_buf), &is_dir))
{
if (!file.isDirectory())
if (!is_dir)
{
const char* raw_name = file.name();
std::string name = raw_name ? raw_name : "";
std::string name = name_buf;
if (has_kml_extension(name))
{
out_routes.push_back(name);
}
}
file.close();
}
dir.close();
@@ -71,7 +71,7 @@ bool remove_route(const std::string& path)
{
return false;
}
return SD.remove(path.c_str());
return ::platform::esp::arduino_common::storage::sd_remove(path.c_str());
}
const char* route_dir()
@@ -79,4 +79,4 @@ const char* route_dir()
return kRouteDir;
}
} // namespace platform::ui::route_storage
} // namespace platform::ui::route_storage
@@ -1,8 +1,9 @@
#include "platform/ui/settings_backup_runtime.h"
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
#include <Arduino.h>
#include <Preferences.h>
#include <SD.h>
#include <cmath>
#include <cstdio>
@@ -80,22 +81,17 @@ void set_status_message(Status& out, const char* message, const char* detail = n
bool sd_available()
{
return ::platform::ui::device::card_ready() && SD.cardType() != CARD_NONE;
return ::platform::ui::device::card_ready() &&
::platform::esp::arduino_common::storage::sd_card_ready();
}
bool ensure_backup_dir()
{
if (SD.exists(kBackupDir))
if (::platform::esp::arduino_common::storage::sd_exists(kBackupDir))
{
File dir = SD.open(kBackupDir, FILE_READ);
const bool ok = static_cast<bool>(dir) && dir.isDirectory();
if (dir)
{
dir.close();
}
return ok;
return ::platform::esp::arduino_common::storage::sd_is_directory(kBackupDir);
}
return SD.mkdir(kBackupDir);
return ::platform::esp::arduino_common::storage::sd_mkdir(kBackupDir);
}
const char* value_type_name(ValueType type)
@@ -808,12 +804,12 @@ bool write_text_atomic(const char* path, const char* temp_path, const char* text
{
return false;
}
if (SD.exists(temp_path))
if (::platform::esp::arduino_common::storage::sd_exists(temp_path))
{
SD.remove(temp_path);
::platform::esp::arduino_common::storage::sd_remove(temp_path);
}
File file = SD.open(temp_path, FILE_WRITE);
if (!file)
::platform::esp::arduino_common::storage::SdRuntimeFile file;
if (!file.open(temp_path, "w"))
{
return false;
}
@@ -821,16 +817,16 @@ bool write_text_atomic(const char* path, const char* temp_path, const char* text
file.close();
if (!wrote)
{
SD.remove(temp_path);
::platform::esp::arduino_common::storage::sd_remove(temp_path);
return false;
}
if (SD.exists(path))
if (::platform::esp::arduino_common::storage::sd_exists(path))
{
SD.remove(path);
::platform::esp::arduino_common::storage::sd_remove(path);
}
if (!SD.rename(temp_path, path))
if (!::platform::esp::arduino_common::storage::sd_rename(temp_path, path))
{
SD.remove(temp_path);
::platform::esp::arduino_common::storage::sd_remove(temp_path);
return false;
}
return true;
@@ -839,8 +835,8 @@ bool write_text_atomic(const char* path, const char* temp_path, const char* text
bool read_file_text(const char* path, std::string& out)
{
out.clear();
File file = SD.open(path, FILE_READ);
if (!file)
::platform::esp::arduino_common::storage::SdRuntimeFile file;
if (!file.open(path, "r"))
{
return false;
}
@@ -851,7 +847,7 @@ bool read_file_text(const char* path, std::string& out)
return false;
}
out.resize(size);
const std::size_t read = file.readBytes(&out[0], size);
const std::size_t read = file.read_bytes(&out[0], size);
file.close();
if (read != size)
{
@@ -900,7 +896,8 @@ Status status()
Status out{};
out.supported = true;
out.sd_present = sd_available();
out.has_backup = out.sd_present && SD.exists(kBackupPath);
out.has_backup = out.sd_present &&
::platform::esp::arduino_common::storage::sd_exists(kBackupPath);
out.busy = false;
if (!out.sd_present)
{
@@ -945,7 +942,7 @@ bool backup()
bool restore()
{
if (!sd_available() || !SD.exists(kBackupPath))
if (!sd_available() || !::platform::esp::arduino_common::storage::sd_exists(kBackupPath))
{
return false;
}
@@ -983,11 +980,12 @@ bool remove()
{
return false;
}
if (SD.exists(kBackupTempPath))
if (::platform::esp::arduino_common::storage::sd_exists(kBackupTempPath))
{
SD.remove(kBackupTempPath);
::platform::esp::arduino_common::storage::sd_remove(kBackupTempPath);
}
return !SD.exists(kBackupPath) || SD.remove(kBackupPath);
return !::platform::esp::arduino_common::storage::sd_exists(kBackupPath) ||
::platform::esp::arduino_common::storage::sd_remove(kBackupPath);
}
} // namespace platform::ui::settings_backup
@@ -1,10 +1,9 @@
#include "platform/ui/tracker_runtime.h"
#include "platform/esp/arduino_common/gps/track_recorder.h"
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
#include "platform/ui/device_runtime.h"
#include <SD.h>
namespace platform::ui::tracker
{
@@ -64,7 +63,7 @@ bool remove_track(const std::string& path)
{
return false;
}
return SD.remove(path.c_str());
return ::platform::esp::arduino_common::storage::sd_remove(path.c_str());
}
const char* track_dir()
@@ -92,4 +91,4 @@ void set_format(Format format)
::gps::TrackRecorder::getInstance().setFormat(static_cast<::gps::TrackFormat>(format));
}
} // namespace platform::ui::tracker
} // namespace platform::ui::tracker
@@ -24,8 +24,8 @@
#include "sys/clock.h"
#else
#include "boards/tlora_pager/tlora_pager_board.h"
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
#include "platform/esp/common/shared_spi_lock.h"
#include <SD.h>
#endif
#include <cmath>
@@ -337,6 +337,7 @@ const char* get_saved_path()
bool ensure_sstv_dir()
{
#if defined(TRAIL_MATE_ESP_BOARD_TAB5)
if (!SD.exists("/sstv"))
{
if (!SD.mkdir("/sstv"))
@@ -345,6 +346,13 @@ bool ensure_sstv_dir()
}
}
return true;
#else
if (::platform::esp::arduino_common::storage::sd_exists("/sstv"))
{
return ::platform::esp::arduino_common::storage::sd_is_directory("/sstv");
}
return ::platform::esp::arduino_common::storage::sd_mkdir("/sstv");
#endif
}
bool build_save_path(char* out_path, size_t out_len)
@@ -377,7 +385,11 @@ bool build_save_path(char* out_path, size_t out_len)
snprintf(out_path, out_len, "/sstv/%lu_%03d.bmp",
static_cast<unsigned long>(millis()), i);
}
#if defined(TRAIL_MATE_ESP_BOARD_TAB5)
if (!SD.exists(out_path))
#else
if (!::platform::esp::arduino_common::storage::sd_exists(out_path))
#endif
{
return true;
}
@@ -464,7 +476,11 @@ bool save_frame_to_sd()
return false;
}
::platform::esp::common::SharedSpiLockGuard guard;
#if defined(TRAIL_MATE_ESP_BOARD_TAB5)
if (SD.cardType() == CARD_NONE)
#else
if (!::platform::esp::arduino_common::storage::sd_card_ready())
#endif
{
set_error("SD not ready");
return false;
@@ -489,8 +505,13 @@ bool save_frame_to_sd()
const uint32_t file_size = 14 + 40 + pixel_bytes;
const uint32_t data_offset = 14 + 40;
#if defined(TRAIL_MATE_ESP_BOARD_TAB5)
File f = SD.open(path, FILE_WRITE);
if (!f)
#else
::platform::esp::arduino_common::storage::SdRuntimeFile f;
if (!f.open(path, "w"))
#endif
{
set_error("SD open failed");
return false;
@@ -8,9 +8,11 @@
#endif
#include <SdFat.h>
#include <algorithm>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <new>
#include <vector>
namespace platform::esp::arduino_common::storage
{
@@ -431,6 +433,23 @@ bool SdRuntimeFile::is_open() const
return impl_ != nullptr && impl_->backend != SdCardBackend::None;
}
int SdRuntimeFile::available() const
{
if (!is_open())
{
return 0;
}
if (impl_->backend == SdCardBackend::ArduinoSd)
{
return impl_->arduino_file.available();
}
if (impl_->backend == SdCardBackend::SdFat)
{
return impl_->sdfat_file.available();
}
return 0;
}
int SdRuntimeFile::read(void* buffer, std::size_t bytes_to_read)
{
if (!is_open() || buffer == nullptr || bytes_to_read == 0)
@@ -448,6 +467,41 @@ int SdRuntimeFile::read(void* buffer, std::size_t bytes_to_read)
return -1;
}
int SdRuntimeFile::read_byte()
{
if (!is_open())
{
return -1;
}
if (impl_->backend == SdCardBackend::ArduinoSd)
{
return impl_->arduino_file.read();
}
if (impl_->backend == SdCardBackend::SdFat)
{
return impl_->sdfat_file.read();
}
return -1;
}
std::size_t SdRuntimeFile::read_bytes(char* buffer, std::size_t bytes_to_read)
{
if (!is_open() || buffer == nullptr || bytes_to_read == 0)
{
return 0;
}
if (impl_->backend == SdCardBackend::ArduinoSd)
{
return impl_->arduino_file.readBytes(buffer, bytes_to_read);
}
if (impl_->backend == SdCardBackend::SdFat)
{
int result = impl_->sdfat_file.read(buffer, bytes_to_read);
return result > 0 ? static_cast<std::size_t>(result) : 0;
}
return 0;
}
std::size_t SdRuntimeFile::write(const void* buffer, std::size_t bytes_to_write)
{
if (!is_open() || buffer == nullptr || bytes_to_write == 0)
@@ -465,6 +519,83 @@ std::size_t SdRuntimeFile::write(const void* buffer, std::size_t bytes_to_write)
return 0;
}
std::size_t SdRuntimeFile::write_byte(uint8_t value)
{
if (!is_open())
{
return 0;
}
if (impl_->backend == SdCardBackend::ArduinoSd)
{
return impl_->arduino_file.write(value);
}
if (impl_->backend == SdCardBackend::SdFat)
{
return impl_->sdfat_file.write(value);
}
return 0;
}
std::size_t SdRuntimeFile::print(const char* text)
{
if (!is_open() || text == nullptr)
{
return 0;
}
if (impl_->backend == SdCardBackend::ArduinoSd)
{
return impl_->arduino_file.print(text);
}
if (impl_->backend == SdCardBackend::SdFat)
{
return impl_->sdfat_file.print(text);
}
return 0;
}
std::size_t SdRuntimeFile::print(double value, int digits)
{
if (!is_open())
{
return 0;
}
const uint8_t precision = digits < 0 ? 0 : static_cast<uint8_t>(digits);
if (impl_->backend == SdCardBackend::ArduinoSd)
{
return impl_->arduino_file.print(value, precision);
}
if (impl_->backend == SdCardBackend::SdFat)
{
return impl_->sdfat_file.print(value, precision);
}
return 0;
}
std::size_t SdRuntimeFile::printf(const char* format, ...)
{
if (!is_open() || format == nullptr)
{
return 0;
}
va_list args;
va_start(args, format);
va_list args_copy;
va_copy(args_copy, args);
int len = std::vsnprintf(nullptr, 0, format, args_copy);
va_end(args_copy);
if (len <= 0)
{
va_end(args);
return 0;
}
std::vector<char> buffer(static_cast<std::size_t>(len) + 1U);
std::vsnprintf(buffer.data(), buffer.size(), format, args);
va_end(args);
return write(buffer.data(), static_cast<std::size_t>(len));
}
bool SdRuntimeFile::seek(uint64_t offset)
{
if (!is_open())
@@ -7,7 +7,7 @@
#include "ui/team_persistence/team_ui_snapshot_codec.h"
#include "sys/clock.h"
#include <SD.h>
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
#include <algorithm>
#include <cctype>
#include <cmath>
@@ -26,6 +26,13 @@ TeamUiSnapshot TeamUiSnapshotMemoryStore::snapshot_{};
namespace
{
using ::platform::esp::arduino_common::storage::SdRuntimeFile;
using ::platform::esp::arduino_common::storage::sd_card_ready;
using ::platform::esp::arduino_common::storage::sd_exists;
using ::platform::esp::arduino_common::storage::sd_mkdir;
using ::platform::esp::arduino_common::storage::sd_remove;
using ::platform::esp::arduino_common::storage::sd_rename;
constexpr const char* kBaseDir = "/team";
constexpr const char* kCurrentPath = "/team/current.txt";
constexpr const char* kCurrentTmpPath = "/team/current.tmp";
@@ -144,11 +151,11 @@ std::string team_dir_from_id(const TeamId& id)
bool ensure_dir(const char* path)
{
if (SD.exists(path))
if (sd_exists(path))
{
return true;
}
return SD.mkdir(path);
return sd_mkdir(path);
}
std::string iso_time(time_t t)
@@ -186,12 +193,12 @@ std::string trim_copy(const std::string& value)
return value.substr(start, end - start);
}
std::string read_line(File& file)
std::string read_line(SdRuntimeFile& file)
{
std::string line;
while (file.available())
{
const int raw = file.read();
const int raw = file.read_byte();
if (raw < 0 || raw == '\n')
{
break;
@@ -206,12 +213,12 @@ std::string read_line(File& file)
bool read_current_dir(std::string& out_dir)
{
if (SD.cardType() == CARD_NONE || !SD.exists(kCurrentPath))
if (!sd_card_ready() || !sd_exists(kCurrentPath))
{
return false;
}
File f = SD.open(kCurrentPath, FILE_READ);
if (!f)
SdRuntimeFile f;
if (!f.open(kCurrentPath, "r"))
{
return false;
}
@@ -227,7 +234,7 @@ bool read_current_dir(std::string& out_dir)
bool write_current_dir(const std::string& dir)
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
@@ -235,8 +242,8 @@ bool write_current_dir(const std::string& dir)
{
return false;
}
File f = SD.open(kCurrentTmpPath, FILE_WRITE);
if (!f)
SdRuntimeFile f;
if (!f.open(kCurrentTmpPath, "w"))
{
return false;
}
@@ -244,33 +251,33 @@ bool write_current_dir(const std::string& dir)
f.print("\n");
f.flush();
f.close();
if (SD.exists(kCurrentPath))
if (sd_exists(kCurrentPath))
{
SD.remove(kCurrentPath);
sd_remove(kCurrentPath);
}
return SD.rename(kCurrentTmpPath, kCurrentPath);
return sd_rename(kCurrentTmpPath, kCurrentPath);
}
bool clear_current_dir()
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
if (SD.exists(kCurrentPath))
if (sd_exists(kCurrentPath))
{
SD.remove(kCurrentPath);
sd_remove(kCurrentPath);
}
if (SD.exists(kCurrentTmpPath))
if (sd_exists(kCurrentTmpPath))
{
SD.remove(kCurrentTmpPath);
sd_remove(kCurrentTmpPath);
}
return true;
}
bool ensure_team_dir_for_id_internal(const TeamId& id, std::string& out_dir_path, bool update_current)
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
@@ -296,12 +303,12 @@ bool ensure_team_dir_for_id(const TeamId& id, std::string& out_dir_path)
return ensure_team_dir_for_id_internal(id, out_dir_path, true);
}
void write_u8(File& f, uint8_t v)
void write_u8(SdRuntimeFile& f, uint8_t v)
{
f.write(&v, 1);
}
void write_u16(File& f, uint16_t v)
void write_u16(SdRuntimeFile& f, uint16_t v)
{
uint8_t b[2] = {
static_cast<uint8_t>(v & 0xFF),
@@ -309,7 +316,7 @@ void write_u16(File& f, uint16_t v)
f.write(b, 2);
}
void write_u32(File& f, uint32_t v)
void write_u32(SdRuntimeFile& f, uint32_t v)
{
uint8_t b[4];
for (int i = 0; i < 4; ++i)
@@ -319,7 +326,7 @@ void write_u32(File& f, uint32_t v)
f.write(b, 4);
}
void write_u64(File& f, uint64_t v)
void write_u64(SdRuntimeFile& f, uint64_t v)
{
uint8_t b[8];
for (int i = 0; i < 8; ++i)
@@ -508,12 +515,12 @@ void apply_key_event(TeamUiSnapshot& snap, TeamKeyEventType type, const std::vec
bool load_snapshot_from_path(const std::string& snapshot_path, TeamUiSnapshot& out)
{
if (!SD.exists(snapshot_path.c_str()))
if (!sd_exists(snapshot_path.c_str()))
{
return false;
}
File f = SD.open(snapshot_path.c_str(), FILE_READ);
if (!f)
SdRuntimeFile f;
if (!f.open(snapshot_path.c_str(), "r"))
{
return false;
}
@@ -533,7 +540,7 @@ bool load_snapshot_from_path(const std::string& snapshot_path, TeamUiSnapshot& o
bool save_snapshot_to_path(const std::string& dir_path, const TeamUiSnapshot& in)
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
@@ -555,8 +562,8 @@ bool save_snapshot_to_path(const std::string& dir_path, const TeamUiSnapshot& in
return false;
}
File f = SD.open(tmp_path.c_str(), FILE_WRITE);
if (!f)
SdRuntimeFile f;
if (!f.open(tmp_path.c_str(), "w"))
{
return false;
}
@@ -567,21 +574,21 @@ bool save_snapshot_to_path(const std::string& dir_path, const TeamUiSnapshot& in
f.close();
std::string out_path_c = out_path;
if (SD.exists(out_path_c.c_str()))
if (sd_exists(out_path_c.c_str()))
{
SD.remove(out_path_c.c_str());
sd_remove(out_path_c.c_str());
}
return SD.rename(tmp_path.c_str(), out_path_c.c_str());
return sd_rename(tmp_path.c_str(), out_path_c.c_str());
}
bool load_keys_from_path(const std::string& keys_path, TeamUiSnapshot& out)
{
if (!SD.exists(keys_path.c_str()))
if (!sd_exists(keys_path.c_str()))
{
return false;
}
File f = SD.open(keys_path.c_str(), FILE_READ);
if (!f)
SdRuntimeFile f;
if (!f.open(keys_path.c_str(), "r"))
{
return false;
}
@@ -634,8 +641,8 @@ bool save_keys_to_path(const std::string& dir_path, const TeamUiSnapshot& in)
}
std::string tmp_path = dir_path + "/" + kKeysTmpName;
std::string out_path = dir_path + "/" + kKeysName;
File f = SD.open(tmp_path.c_str(), FILE_WRITE);
if (!f)
SdRuntimeFile f;
if (!f.open(tmp_path.c_str(), "w"))
{
return false;
}
@@ -648,21 +655,21 @@ bool save_keys_to_path(const std::string& dir_path, const TeamUiSnapshot& in)
f.write(in.team_psk.data(), in.team_psk.size());
f.flush();
f.close();
if (SD.exists(out_path.c_str()))
if (sd_exists(out_path.c_str()))
{
SD.remove(out_path.c_str());
sd_remove(out_path.c_str());
}
return SD.rename(tmp_path.c_str(), out_path.c_str());
return sd_rename(tmp_path.c_str(), out_path.c_str());
}
bool load_events_apply(const std::string& events_path, TeamUiSnapshot& out)
{
if (!SD.exists(events_path.c_str()))
if (!sd_exists(events_path.c_str()))
{
return false;
}
File f = SD.open(events_path.c_str(), FILE_READ);
if (!f)
SdRuntimeFile f;
if (!f.open(events_path.c_str(), "r"))
{
return false;
}
@@ -731,7 +738,7 @@ bool append_event(const TeamId& team_id, TeamKeyEventType type,
uint32_t event_seq, uint32_t ts,
const uint8_t* payload, size_t len)
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
@@ -742,8 +749,8 @@ bool append_event(const TeamId& team_id, TeamKeyEventType type,
}
std::string events_path = dir_path + "/" + kEventsName;
File f = SD.open(events_path.c_str(), FILE_APPEND);
if (!f)
SdRuntimeFile f;
if (!f.open(events_path.c_str(), "a"))
{
return false;
}
@@ -817,7 +824,7 @@ bool should_write_pos(uint32_t member_id, int32_t lat_e7, int32_t lon_e7, uint32
return true;
}
bool init_posring(File& f)
bool init_posring(SdRuntimeFile& f)
{
f.seek(0);
f.write(reinterpret_cast<const uint8_t*>("PSR1"), 4);
@@ -833,7 +840,7 @@ bool init_posring(File& f)
return true;
}
bool read_posring_header(File& f, uint32_t& write_offset)
bool read_posring_header(SdRuntimeFile& f, uint32_t& write_offset)
{
f.seek(0);
uint8_t header[kPosHeaderSize];
@@ -869,7 +876,7 @@ bool read_posring_header(File& f, uint32_t& write_offset)
return true;
}
bool write_posring_header(File& f, uint32_t write_offset)
bool write_posring_header(SdRuntimeFile& f, uint32_t write_offset)
{
f.seek(0);
f.write(reinterpret_cast<const uint8_t*>("PSR1"), 4);
@@ -890,7 +897,7 @@ class TeamUiSnapshotStorePersisted : public ITeamUiSnapshotStore
public:
bool load(TeamUiSnapshot& out) override
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
@@ -938,7 +945,7 @@ class TeamUiSnapshotStorePersisted : public ITeamUiSnapshotStore
void save(const TeamUiSnapshot& in) override
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return;
}
@@ -1124,14 +1131,14 @@ bool team_ui_posring_append(const TeamId& team_id,
}
std::string path = dir_path + "/" + kPosringName;
File f = SD.open(path.c_str(), FILE_READ);
bool exists = f;
if (f)
SdRuntimeFile f;
bool exists = f.open(path.c_str(), "r");
if (exists)
{
f.close();
}
File rw = SD.open(path.c_str(), FILE_WRITE);
if (!rw)
SdRuntimeFile rw;
if (!rw.open(path.c_str(), exists ? "r+" : "w+"))
{
return false;
}
@@ -1173,19 +1180,19 @@ bool team_ui_posring_load_latest(const TeamId& team_id,
std::vector<TeamPosSample>& out)
{
out.clear();
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
std::string dir = team_dir_from_id(team_id);
std::string dir_path = std::string(kBaseDir) + "/" + dir;
std::string path = dir_path + "/" + kPosringName;
if (!SD.exists(path.c_str()))
if (!sd_exists(path.c_str()))
{
return false;
}
File f = SD.open(path.c_str(), FILE_READ);
if (!f)
SdRuntimeFile f;
if (!f.open(path.c_str(), "r"))
{
return false;
}
@@ -1294,27 +1301,27 @@ bool TeamUiSdChatLogStore::appendStructured(const TeamId& team_id,
std::string path = dir_path + "/" + kChatlogName;
size_t record_len = 2 + 1 + 1 + 4 + 4 + 1 + 3 + 2 + 2 + payload.size();
if (SD.exists(path.c_str()))
if (sd_exists(path.c_str()))
{
File f = SD.open(path.c_str(), FILE_READ);
if (f)
SdRuntimeFile f;
if (f.open(path.c_str(), "r"))
{
size_t size = f.size();
f.close();
if (size + record_len > kChatlogMaxBytes)
{
std::string old_path = dir_path + "/" + kChatlogOldName;
if (SD.exists(old_path.c_str()))
if (sd_exists(old_path.c_str()))
{
SD.remove(old_path.c_str());
sd_remove(old_path.c_str());
}
SD.rename(path.c_str(), old_path.c_str());
sd_rename(path.c_str(), old_path.c_str());
}
}
}
File out = SD.open(path.c_str(), FILE_APPEND);
if (!out)
SdRuntimeFile out;
if (!out.open(path.c_str(), "a"))
{
return false;
}
@@ -1359,7 +1366,7 @@ bool TeamUiSdChatLogStore::loadRecent(const TeamId& team_id,
std::vector<TeamChatLogEntry>& out)
{
out.clear();
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
@@ -1369,12 +1376,12 @@ bool TeamUiSdChatLogStore::loadRecent(const TeamId& team_id,
return false;
}
std::string path = dir_path + "/" + kChatlogName;
if (!SD.exists(path.c_str()))
if (!sd_exists(path.c_str()))
{
return false;
}
File f = SD.open(path.c_str(), FILE_READ);
if (!f)
SdRuntimeFile f;
if (!f.open(path.c_str(), "r"))
{
return false;
}
@@ -1492,7 +1499,7 @@ bool team_ui_save_keys_now(const TeamId& team_id,
uint32_t key_id,
const std::array<uint8_t, team::proto::kTeamChannelPskSize>& psk)
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
@@ -1518,7 +1525,7 @@ bool team_ui_get_member_track_path(const TeamId& team_id,
uint32_t member_id,
std::string& out_path)
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
@@ -1542,7 +1549,7 @@ bool team_ui_append_member_track(const TeamId& team_id,
uint32_t member_id,
const team::proto::TeamTrackMessage& track)
{
if (SD.cardType() == CARD_NONE)
if (!sd_card_ready())
{
return false;
}
@@ -1578,8 +1585,8 @@ bool team_ui_append_member_track(const TeamId& team_id,
snprintf(name, sizeof(name), "%08lX.gpx", static_cast<unsigned long>(member_id));
std::string path = tracks_dir + "/" + name;
File f = SD.open(path.c_str(), FILE_WRITE);
if (!f)
SdRuntimeFile f;
if (!f.open(path.c_str(), "a+"))
{
return false;
}