feat: add SD-backed stats archive and dedicated /stats web console
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <MeshCore.h>
|
||||
|
||||
#include "ArchiveStorage.h"
|
||||
|
||||
struct HistorySample {
|
||||
uint32_t epoch_secs;
|
||||
uint32_t uptime_secs;
|
||||
uint32_t packets_sent;
|
||||
uint32_t packets_recv;
|
||||
uint32_t heap_free;
|
||||
uint32_t heap_min;
|
||||
uint32_t psram_free;
|
||||
uint32_t psram_min;
|
||||
uint16_t battery_mv;
|
||||
uint16_t queue_len;
|
||||
uint16_t error_flags;
|
||||
uint16_t recv_errors;
|
||||
uint16_t neighbour_count;
|
||||
uint16_t direct_dups;
|
||||
uint16_t flood_dups;
|
||||
int16_t last_rssi_x4;
|
||||
int16_t last_snr_x4;
|
||||
int16_t noise_floor;
|
||||
uint8_t flags;
|
||||
int8_t battery_pct;
|
||||
uint16_t reserved;
|
||||
};
|
||||
|
||||
struct HistoryEvent {
|
||||
uint32_t epoch_secs;
|
||||
uint32_t uptime_secs;
|
||||
uint8_t type;
|
||||
int16_t value;
|
||||
uint8_t reserved;
|
||||
};
|
||||
|
||||
enum HistorySampleFlags : uint8_t {
|
||||
HISTORY_FLAG_EXTERNAL_POWER = 1 << 0,
|
||||
HISTORY_FLAG_CHARGING = 1 << 1,
|
||||
HISTORY_FLAG_VBUS = 1 << 2,
|
||||
HISTORY_FLAG_WIFI_CONNECTED = 1 << 3,
|
||||
HISTORY_FLAG_MQTT_CONNECTED = 1 << 4,
|
||||
HISTORY_FLAG_WEB_ENABLED = 1 << 5,
|
||||
HISTORY_FLAG_WEB_PANEL_UP = 1 << 6,
|
||||
HISTORY_FLAG_ARCHIVE_MOUNTED = 1 << 7,
|
||||
};
|
||||
|
||||
enum HistoryEventType : uint8_t {
|
||||
HISTORY_EVENT_BOOT = 1,
|
||||
HISTORY_EVENT_WEB_STARTED = 2,
|
||||
HISTORY_EVENT_WEB_STOPPED = 3,
|
||||
HISTORY_EVENT_MQTT_CONNECTED = 4,
|
||||
HISTORY_EVENT_MQTT_DISCONNECTED = 5,
|
||||
HISTORY_EVENT_ARCHIVE_MOUNTED = 6,
|
||||
HISTORY_EVENT_ARCHIVE_UNAVAILABLE = 7,
|
||||
HISTORY_EVENT_LOW_MEMORY = 8,
|
||||
HISTORY_EVENT_STATS_ENABLED = 9,
|
||||
HISTORY_EVENT_STATS_DISABLED = 10,
|
||||
};
|
||||
|
||||
class StatsHistory {
|
||||
public:
|
||||
static constexpr uint32_t kSampleIntervalSecs = 60;
|
||||
static constexpr uint32_t kArchiveSummaryIntervalSecs = 300;
|
||||
|
||||
StatsHistory();
|
||||
~StatsHistory();
|
||||
|
||||
void begin(bool enabled, ArchiveStorage* archive);
|
||||
void setArchive(ArchiveStorage* archive);
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
bool isEnabled() const { return _enabled; }
|
||||
bool isRecentHistoryAvailable() const { return _samples != nullptr; }
|
||||
bool isPsramBacked() const { return _psram_backed; }
|
||||
bool isDegraded() const { return _degraded; }
|
||||
bool isArchiveAvailable() const;
|
||||
bool hasArchiveRestore() const { return _restored_sample_count > 0; }
|
||||
|
||||
size_t getSampleCapacity() const { return _sample_capacity; }
|
||||
size_t getSampleCount() const { return _sample_count; }
|
||||
size_t getEventCapacity() const { return _event_capacity; }
|
||||
size_t getEventCount() const { return _event_count; }
|
||||
size_t getRestoredSampleCount() const { return _restored_sample_count; }
|
||||
|
||||
void pushSample(const HistorySample& sample);
|
||||
void recordEvent(uint8_t type, uint32_t epoch_secs, uint32_t uptime_secs, int16_t value = 0);
|
||||
void maybeFlush(uint32_t now_ms);
|
||||
|
||||
bool buildSeriesJson(const char* series, char* buffer, size_t buffer_size, uint32_t now_epoch_secs, uint32_t now_uptime_secs) const;
|
||||
bool getRecentEvent(size_t reverse_index, HistoryEvent& event) const;
|
||||
|
||||
static const char* getEventTypeName(uint8_t type);
|
||||
static constexpr uint32_t getSampleIntervalSecs() { return kSampleIntervalSecs; }
|
||||
static constexpr uint32_t getArchiveSummaryIntervalSecs() { return kArchiveSummaryIntervalSecs; }
|
||||
|
||||
private:
|
||||
bool ensureBuffers();
|
||||
bool restoreSummaryLog();
|
||||
bool restoreEventsLog();
|
||||
bool parseSummaryLine(const char* line, HistorySample& sample) const;
|
||||
bool parseEventLine(const char* line, HistoryEvent& event) const;
|
||||
void storeSample(const HistorySample& sample, bool mark_dirty);
|
||||
void storeEvent(const HistoryEvent& event, bool queue_pending);
|
||||
void appendPendingEvent(const HistoryEvent& event);
|
||||
void flushSummaryLog();
|
||||
void flushEventsLog();
|
||||
void writeMetaFile() const;
|
||||
bool getSampleFromOldest(size_t index, HistorySample& sample) const;
|
||||
|
||||
bool _enabled;
|
||||
bool _psram_backed;
|
||||
bool _degraded;
|
||||
bool _summary_dirty;
|
||||
uint32_t _next_summary_flush_ms;
|
||||
uint32_t _next_event_flush_ms;
|
||||
size_t _sample_capacity;
|
||||
size_t _sample_head;
|
||||
size_t _sample_count;
|
||||
size_t _event_capacity;
|
||||
size_t _event_head;
|
||||
size_t _event_count;
|
||||
HistorySample* _samples;
|
||||
HistoryEvent* _events;
|
||||
ArchiveStorage* _archive;
|
||||
size_t _restored_sample_count;
|
||||
HistoryEvent _pending_events[16];
|
||||
size_t _pending_event_count;
|
||||
};
|
||||
Reference in New Issue
Block a user