fix: retry SD archive recovery while stats are enabled

This commit is contained in:
Jared Dohrman committed 2026-05-14 17:58:08 +10:00
1 parent 78bacc3dab
commit 9415dc4107
4 files changed
+29 -1

No files matched your search

+1 -1
View File
@@ -128,7 +128,7 @@ OK
- `get web`
- `get web.status`: shows whether the local HTTPS panel is available.
- `get web.stats.status`: shows whether the dedicated stats page and history subsystem are enabled, whether recent history is active, whether PSRAM-backed history is available, and whether the SD-backed archive is mounted. When enabled, the history capture now covers supported environment telemetry too, not just the original battery/radio series. GPS-active boards also record per-minute satellites samples for the `/stats` history view.
- `get web.stats.status`: shows whether the dedicated stats page and history subsystem are enabled, whether recent history is active, whether PSRAM-backed history is available, and whether the SD-backed archive is mounted. When enabled, the history capture now covers supported environment telemetry too, not just the original battery/radio series. GPS-active boards also record per-minute satellites samples for the `/stats` history view. If archive access drops while stats remain enabled, the repeater retries the SD mount periodically.
- `set web on|off`
- `set.web on|off`: enables or disables the local HTTPS panel.
- `set web.stats on|off`
+2
View File
@@ -311,6 +311,8 @@ When `web.stats` is enabled, stats history starts capturing from boot or from th
Archive-backed restore requires `web.stats` enabled plus a mounted SD card on boards that support the EastMesh archive path.
If archive access drops at runtime, the repeater retries the SD mount periodically while `web.stats` remains enabled.
The main purpose of the SD card is to let the repeater retain and restore stats history for `/stats`. The archive keeps fast `.latest` snapshot files for quick restore and UTC-dated daily `.log` files for longer-term history. As a secondary option, those files can also be removed and inspected on a computer for deeper manual review.
On no-PSRAM boards, `/stats` can still show recent graphs, but the history is smaller and does not provide the same archive-backed behaviour as PSRAM-capable boards.
+24
View File
@@ -188,6 +188,7 @@ bool appendJsonFloatField(char* reply, size_t reply_size, size_t& offset, bool&
}
constexpr unsigned long kArchiveNeighboursFlushIntervalMs = 60UL * 1000UL;
constexpr unsigned long kArchiveRecoveryRetryIntervalMs = 5UL * 60UL * 1000UL;
constexpr const char* kArchiveNeighboursSnapshotPath = "/stats/neighbours.snapshot";
constexpr const char* kArchiveNeighboursLatestPath = "/stats/neighbours.latest";
@@ -1201,6 +1202,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
_archive = nullptr;
uptime_millis = 0;
next_archive_neighbours_flush_ms = 0;
next_archive_recover_ms = 0;
next_battery_sample_ms = 0;
next_history_sample_ms = 0;
next_local_advert = next_flood_advert = 0;
@@ -1789,9 +1791,11 @@ void MyMesh::updateStatsHistory(unsigned long now_ms) {
if (!_stats_history.isEnabled()) {
_stats_state.initialized = false;
_archive_neighbours_dirty = false;
next_archive_recover_ms = 0;
return;
}
_stats_history.maybeReleaseIdleBuffers(now_ms);
maybeRecoverArchive(now_ms);
const bool wifi_connected = network.isWifiConnected();
#ifdef WITH_MQTT_UPLINK
@@ -1970,6 +1974,7 @@ void MyMesh::updateStatsHistory(unsigned long now_ms) {
}
_stats_history.maybeFlush(now_ms);
maybeRecoverArchive(now_ms);
if (!_stats_history.isLiveOnly()) {
maybeFlushArchiveNeighbours(now_ms);
}
@@ -1978,6 +1983,25 @@ void MyMesh::updateStatsHistory(unsigned long now_ms) {
#endif
}
void MyMesh::maybeRecoverArchive(unsigned long now_ms) {
#if defined(ESP_PLATFORM) && WITH_WEB_PANEL
if (_archive == nullptr || _archive->isMounted()) {
next_archive_recover_ms = 0;
return;
}
if (next_archive_recover_ms != 0 && !millisHasNowPassed(next_archive_recover_ms)) {
return;
}
if (!_archive->recover()) {
next_archive_recover_ms = now_ms + kArchiveRecoveryRetryIntervalMs;
return;
}
next_archive_recover_ms = 0;
#else
(void)now_ms;
#endif
}
bool MyMesh::appendJsonEvents(char* reply, size_t reply_size, size_t& offset) const {
offset += snprintf(&reply[offset], reply_size - offset, "\"events\":[");
const size_t max_events = min<size_t>(_stats_history.getEventCount(), 6);
+2
View File
@@ -129,6 +129,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks, public WebPanelComm
uint32_t last_millis;
uint64_t uptime_millis;
unsigned long next_archive_neighbours_flush_ms;
unsigned long next_archive_recover_ms;
unsigned long next_battery_sample_ms;
unsigned long next_history_sample_ms;
unsigned long next_local_advert, next_flood_advert;
@@ -192,6 +193,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks, public WebPanelComm
void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr);
size_t getNeighbourCount() const;
void updateStatsHistory(unsigned long now_ms);
void maybeRecoverArchive(unsigned long now_ms);
bool restoreArchiveNeighbours();
void flushArchiveNeighbours();
void maybeFlushArchiveNeighbours(unsigned long now_ms);