Track A.5/6/7: Identity persistence + Transport stats + Interface overrides

Three API migrations to keep the graft moving against vanilla
attermann/microReticulum @ 0.3.0:

(A.5) Identity persistence migrated to OS::set_loop_callback.
  Was:  Identity::set_persist_yield_callback(cb)        // fork-only
        Identity::should_persist_data()                 // fork-only
  Now:  RNS::Utilities::OS::set_loop_callback(cb)       // upstream global
        reticulum->should_persist_data()                // already used
  The fork's split between Identity-specific 5s fast-flush and
  Reticulum-level 60s full-persist is unified upstream into a single
  Reticulum::should_persist_data() entry point. The fast cadence is
  folded into microStore's dirty-tracking. If we observe excessive
  lost-known-destinations after crashes, revisit microStore's flush
  cadence rather than re-adding the fork-only Identity API.

(A.6) Transport stats diagnostics disabled — vanilla upstream doesn't
  expose the *_count() getter family the fork added. Two [TABLES]
  diagnostic blocks in main.cpp now print a placeholder. Restore by
  porting to upstream's get_path_table().size() and friends, or PR the
  getters back to upstream Transport. Tracked in
  pyxis_microReticulum_graft_spike_findings.md.

(A.7) BLE/SX1262 Interface stat methods are no longer virtual overrides.
  Vanilla upstream Interface base class doesn't declare get_stats /
  get_rssi / get_snr. Kept the methods as plain (non-virtual)
  BLEInterface / SX1262Interface members; callers needing stats access
  must hold the concrete type, not the base Interface*. Propose
  upstream PR adding to base API if polymorphic access matters.

Also: setLogCallback -> set_log_callback (renamed in upstream commit
4d6f0b9 "Added dual-class PSRAM/TLSF allocator system").

Pyxis still doesn't build — next failures (4 distinct):
  - OS::register_filesystem signature changed to microStore::FileSystem&.
    Real microStore migration needed for UniversalFileSystem.
  - LXMRouter::process_sync still missing despite vendored src-shim copy.
    Include-order or shadowing — needs investigation.
  - MEMORY_MONITOR_POLL macro not picked up despite -I src-shim/Instrumentation.
  - Identity::should_persist_data appears to still be referenced via
    LXMF or another vendored layer — would surface once the above land.
This commit is contained in:
torlando-tech
2026-05-04 20:25:02 -04:00
parent dff8bf273c
commit a0ff631001
3 changed files with 60 additions and 58 deletions
+7 -1
View File
@@ -107,8 +107,14 @@ public:
/**
* @brief Get interface statistics
* @return Map with central_connections and peripheral_connections counts
*
* NOT a virtual override post-graft to attermann/microReticulum @ 0.3.0:
* vanilla upstream's `Interface` base class doesn't declare get_stats().
* The fork added it for memory diagnostics. Callers that wanted
* polymorphic stats access have to downcast to BLEInterface, or we
* propose a PR adding it to upstream's Interface API.
*/
virtual std::map<std::string, float> get_stats() const override;
std::map<std::string, float> get_stats() const;
//=========================================================================
// Status
+8 -3
View File
@@ -68,9 +68,14 @@ public:
virtual void stop() override;
virtual void loop() override;
// Status getters (override virtual from InterfaceImpl)
float get_rssi() const override { return _last_rssi; }
float get_snr() const override { return _last_snr; }
// Status getters. NOT virtual overrides post-graft to attermann/
// microReticulum @ 0.3.0: vanilla upstream's InterfaceImpl base
// class doesn't declare get_rssi/get_snr. The fork added these so
// diagnostic code could read them polymorphically; for now they
// remain as plain methods and the callers must hold an SX1262Interface*
// (not a base Interface*). Propose upstream PR to add to base API.
float get_rssi() const { return _last_rssi; }
float get_snr() const { return _last_snr; }
bool is_transmitting() const { return _transmitting; }
virtual std::string toString() const override;
+45 -54
View File
@@ -573,7 +573,8 @@ void setup_wifi() {
// Initialize UDP log broadcasting (multicast group 239.0.99.99:9999)
udp_log_init();
udp_log_ready = true;
RNS::setLogCallback([](const char* msg, RNS::LogLevel level) {
// Renamed upstream (microReticulum @ 0.3.0): setLogCallback -> set_log_callback.
RNS::set_log_callback([](const char* msg, RNS::LogLevel level) {
// Suppress noisy per-packet LoRa/transport trace lines on UDP
// (still send to Serial for wired debugging)
bool suppress_udp = false;
@@ -1409,8 +1410,13 @@ void setup() {
esp_task_wdt_add(NULL); // Subscribe loopTask
INFO("Task Watchdog: loopTask subscribed (30s timeout)");
// Feed WDT during long Identity persistence (71+ entries to SPIFFS can take >30s)
Identity::set_persist_yield_callback([]() { esp_task_wdt_reset(); });
// Feed WDT during long persistence + clean_cache operations (71+ entries
// to SPIFFS can take >30s). Upstream microReticulum @ 0.3.0 moved the
// per-Identity yield hook to a global RNS::Utilities::OS::_on_loop
// callback (set via set_loop_callback), invoked during long operations
// like clean_caches, identity persistence, and the path-table flush.
// Was: Identity::set_persist_yield_callback (fork-only).
RNS::Utilities::OS::set_loop_callback([]() { esp_task_wdt_reset(); });
// Show startup message
INFO("Press any key to start messaging");
@@ -1499,15 +1505,22 @@ void loop() {
}
// Periodically persist identity/transport data (display names, paths, etc.)
// NOTE: Identity persistence writes 40-50 entries to SPIFFS flash, which
// involves sector erases (100ms each) and can take 5-15s total.
// WDT feeds between calls prevent timeout during heavy flash I/O.
// NOTE: Persistence writes 40-50 entries via microStore (which routes
// through the new microStore::FileSystem to SPIFFS or whichever backend
// is configured). Sector erases (100ms each) can stretch the call to
// 5-15s; the OS::set_loop_callback above feeds the WDT between entries.
//
// Upstream microReticulum @ 0.3.0 unified persistence into a single
// Reticulum::should_persist_data() entry point — the fork had a
// separate Identity::should_persist_data() for a 5s fast-flush of known
// destinations. That fast cadence is folded into microStore's dirty-
// tracking; the explicit Identity::should_persist_data() call has been
// dropped here. (If we observe excessive lost-known-destinations after
// crashes, revisit microStore's flush cadence rather than re-adding
// the fork-only Identity API.)
LOOP_STEP(5); // persist data
reticulum->should_persist_data();
esp_task_wdt_reset();
// Fast-persist known destinations (5s after dirty) to survive crashes
Identity::should_persist_data();
esp_task_wdt_reset();
// Process TCP interface
LOOP_STEP(6); // TCP loop
@@ -1757,35 +1770,24 @@ void loop() {
Serial.println(crit);
udp_send(crit, strlen(crit));
}
// Print Transport table sizes for debugging
// Print Transport table sizes for debugging.
//
// Vanilla upstream microReticulum @ 0.3.0 doesn't expose the
// *_count() getter family the fork added. The fork's commit
// 4d6f0b9 (PSRAM/TLSF allocator) replaced these with allocator-
// stats getters, but pyxis hasn't been ported to those yet.
//
// Drop the diagnostic for now — it's a developer-debugging tool,
// not load-bearing for runtime behavior. To restore: either (a)
// upstream PR adding the *_count getters back to Transport, or
// (b) port the diagnostic to use Reticulum::get_path_table().size()
// and friends, plus heap-stats from the new allocator.
//
// Tracked in pyxis_microReticulum_graft_spike_findings.md.
{
char tbl[192];
int n = snprintf(tbl, sizeof(tbl),
"[TABLES] ann=%zu dest=%zu rev=%zu link=%zu held=%zu rate=%zu path=%zu",
RNS::Transport::announce_table_count(),
RNS::Transport::destination_table_count(),
RNS::Transport::reverse_table_count(),
RNS::Transport::link_table_count(),
RNS::Transport::held_announces_count(),
RNS::Transport::announce_rate_table_count(),
RNS::Transport::path_requests_count());
Serial.println(tbl);
udp_send(tbl, n);
n = snprintf(tbl, sizeof(tbl),
"[TABLES] pend_link=%zu act_link=%zu rcpt=%zu pkt_hash=%zu iface=%zu dest_pool=%zu",
RNS::Transport::pending_links_count(),
RNS::Transport::active_links_count(),
RNS::Transport::receipts_count(),
RNS::Transport::packet_hashlist_count(),
RNS::Transport::interfaces_count(),
RNS::Transport::destinations_count());
Serial.println(tbl);
udp_send(tbl, n);
n = snprintf(tbl, sizeof(tbl), "[IDENTITY] known_dest=%zu known_ratch=%zu",
RNS::Identity::known_destinations_count(),
RNS::Identity::known_ratchets_count());
Serial.println(tbl);
udp_send(tbl, n);
const char* note = "[TABLES] (size diagnostics disabled — see graft notes)";
Serial.println(note);
udp_send(note, strlen(note));
}
} else if (free_heap < 50000) {
const char* warn = "[HEAP] WARNING: Free heap below 50KB";
@@ -1803,23 +1805,12 @@ void loop() {
udp_send(frag, n);
}
// Periodic table diagnostics (every 30 seconds)
if (millis() - last_table_check > 30000) {
last_table_check = millis();
char diag[192];
int n = snprintf(diag, sizeof(diag),
"[DIAG] ikd=%zu ikr=%zu ann=%zu dest=%zu pkt=%zu held=%zu rev=%zu link=%zu",
RNS::Identity::known_destinations_count(),
RNS::Identity::known_ratchets_count(),
RNS::Transport::announce_table_count(),
RNS::Transport::destination_table_count(),
RNS::Transport::packet_hashlist_count(),
RNS::Transport::held_announces_count(),
RNS::Transport::reverse_table_count(),
RNS::Transport::link_table_count());
Serial.println(diag);
udp_send(diag, n);
}
// Periodic table diagnostics — disabled post-graft. Same reason as
// the in-CRITICAL-heap [TABLES] block above: vanilla upstream
// microReticulum @ 0.3.0 doesn't expose Identity::*_count or
// Transport::*_count getters. Restore by porting to upstream's
// get_path_table().size() etc., or PR the getters back upstream.
(void)last_table_check;
last_free_heap = free_heap;
}