fix: retry SD archive recovery while stats are enabled
This commit is contained in:
1 parent
78bacc3dab
commit
9415dc4107
4 files changed
+29
-1
No files matched your search
@@ -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`
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user