diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 5f5cb9e5..8e861c85 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -525,7 +525,11 @@ bool MyMesh::filterRecvFloodPacket(mesh::Packet* packet) { // thresholds (max times this node's hash may already appear in the path, by hash size). // A companion moves around, so it re-enters its own flood's path more easily than a // fixed repeater — hardcoded rather than configurable since there's no CLI here. -static const uint8_t REPEAT_LOOP_MAX[] = { 0, /*1-byte*/ 2, /*2-byte*/ 1, /*3-byte*/ 1 }; +// Indexed by getPathHashSize() = (path_len>>6)+1, so 1..4. Index 0 is unused +// (hash size is never 0); index 4 covers path_mode 3, which tryParsePacket +// currently rejects — kept in-bounds so this can't OOB-read if that guard is +// ever relaxed. +static const uint8_t REPEAT_LOOP_MAX[] = { 0, /*1-byte*/ 2, /*2-byte*/ 1, /*3-byte*/ 1, /*4-byte*/ 1 }; // Caps how many hops an ADVERT flood gets repeated, matching simple_repeater's default // flood_max_advert — adverts are the most frequent flood traffic, so this is the one // depth limit worth keeping even without the rest of simple_repeater's flood_max knobs. @@ -533,6 +537,7 @@ static const uint8_t REPEAT_MAX_ADVERT_HOPS = 8; bool MyMesh::isRepeatLooped(const mesh::Packet* packet) const { uint8_t hash_size = packet->getPathHashSize(); + if (hash_size >= sizeof(REPEAT_LOOP_MAX)) return true; // unknown hash size: treat as looped, don't forward uint8_t hash_count = packet->getPathHashCount(); uint8_t n = 0; const uint8_t* path = packet->path; diff --git a/examples/companion_radio/ui-new/DiagnosticsScreen.h b/examples/companion_radio/ui-new/DiagnosticsScreen.h index 226999e6..6cb95ba0 100644 --- a/examples/companion_radio/ui-new/DiagnosticsScreen.h +++ b/examples/companion_radio/ui-new/DiagnosticsScreen.h @@ -4,6 +4,7 @@ #include #include #include "icons.h" +#include "PopupMenu.h" #include "../MyMesh.h" extern MyMesh the_mesh; @@ -18,6 +19,7 @@ extern MyMesh the_mesh; class DiagnosticsScreen : public UIScreen { UITask* _task; int _scroll = 0; + PopupMenu _reset_menu; // Enter → confirm before zeroing the cumulative counters struct Row { const char* label; char value[20]; }; static const int MAX_ROWS = 13; @@ -128,13 +130,31 @@ public: } drawScrollIndicator(display, start_y, visible * item_h, _row_count, visible, _scroll); display.setColor(DisplayDriver::LIGHT); - return 1000; // stats refresh once a second — no need for a faster redraw + if (_reset_menu.active) _reset_menu.render(display); + return _reset_menu.active ? 50 : 1000; // popup wants snappier redraw; stats otherwise refresh once a second } bool handleInput(char c) override { + if (_reset_menu.active) { + auto res = _reset_menu.handleInput(c); + // Index 0 = "Reset", 1 = "Cancel" (destructive option first, but never the + // default landing row — see setSelected below). + if (res == PopupMenu::SELECTED && _reset_menu.selectedIndex() == 0) { + the_mesh.resetStats(); // zeroes Dispatcher per-type counters + Mesh forward count + _task->showAlert("Counters reset", 800); + } + return true; + } if (c == KEY_UP) { if (_scroll > 0) _scroll--; return true; } if (c == KEY_DOWN) { _scroll++; return true; } // clamped to max_scroll in render() - if (c == KEY_CANCEL || c == KEY_CONTEXT_MENU || c == KEY_ENTER) { _task->gotoToolsScreen(); return true; } + if (c == KEY_ENTER) { + _reset_menu.begin("Reset counters?", 2); + _reset_menu.addItem("Reset"); + _reset_menu.addItem("Cancel"); + _reset_menu.setSelected(1); // default to Cancel so a stray Enter doesn't wipe stats + return true; + } + if (c == KEY_CANCEL || c == KEY_CONTEXT_MENU) { _task->gotoToolsScreen(); return true; } return false; } }; diff --git a/src/Dispatcher.h b/src/Dispatcher.h index 0128c57c..de26ee91 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -189,7 +189,7 @@ public: uint32_t getNumRecvByType(uint8_t payload_type) const { return n_recv_by_type[payload_type & 0x0F]; } int getPoolFreeCount() const { return _mgr->getFreeCount(); } int getOutboundQueueLen() const { return _mgr->getOutboundTotal(); } - void resetStats() { + virtual void resetStats() { n_sent_flood = n_sent_direct = n_recv_flood = n_recv_direct = 0; memset(n_sent_by_type, 0, sizeof(n_sent_by_type)); memset(n_recv_by_type, 0, sizeof(n_recv_by_type)); diff --git a/src/Mesh.h b/src/Mesh.h index 9de0ccbf..a81d7a7c 100644 --- a/src/Mesh.h +++ b/src/Mesh.h @@ -183,6 +183,9 @@ public: // confirms a repeater/transport role is really forwarding, not just configured to. uint32_t getNumForwarded() const { return n_forwarded; } + // also clear the forward counter alongside the Dispatcher packet counters. + void resetStats() override { Dispatcher::resetStats(); n_forwarded = 0; } + RNG* getRNG() const { return _rng; } RTCClock* getRTCClock() const { return _rtc; }