Merge pull request #7137 from meshtastic/sort-nodes

Make NodeDB sort its internal vector when lastheard is updated. Don't…
This commit is contained in:
Jason P 2025-06-25 20:24:57 -05:00 committed by GitHub
commit c8bfb61c8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 37 additions and 43 deletions

View File

@ -113,35 +113,6 @@ int calculateMaxScroll(int totalEntries, int visibleRows)
return std::max(0, (totalEntries - 1) / (visibleRows * 2)); return std::max(0, (totalEntries - 1) / (visibleRows * 2));
} }
void retrieveAndSortNodes(std::vector<NodeEntry> &nodeList)
{
size_t numNodes = nodeDB->getNumMeshNodes();
for (size_t i = 0; i < numNodes; i++) {
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
if (!node || node->num == nodeDB->getNodeNum())
continue;
NodeEntry entry;
entry.node = node;
entry.sortValue = sinceLastSeen(node);
nodeList.push_back(entry);
}
// Sort nodes: favorites first, then by last heard (most recent first)
std::sort(nodeList.begin(), nodeList.end(), [](const NodeEntry &a, const NodeEntry &b) {
bool aFav = a.node->is_favorite;
bool bFav = b.node->is_favorite;
if (aFav != bFav)
return aFav;
if (a.sortValue == 0 || a.sortValue == UINT32_MAX)
return false;
if (b.sortValue == 0 || b.sortValue == UINT32_MAX)
return true;
return a.sortValue < b.sortValue;
});
}
void drawColumnSeparator(OLEDDisplay *display, int16_t x, int16_t yStart, int16_t yEnd) void drawColumnSeparator(OLEDDisplay *display, int16_t x, int16_t yStart, int16_t yEnd)
{ {
int columnWidth = display->getWidth() / 2; int columnWidth = display->getWidth() / 2;
@ -440,17 +411,16 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
// Space below header // Space below header
y += COMMON_HEADER_HEIGHT; y += COMMON_HEADER_HEIGHT;
// Fetch and display sorted node list int totalEntries = nodeDB->getNumMeshNodes();
std::vector<NodeEntry> nodeList;
retrieveAndSortNodes(nodeList);
int totalEntries = nodeList.size();
int totalRowsAvailable = (display->getHeight() - y) / rowYOffset; int totalRowsAvailable = (display->getHeight() - y) / rowYOffset;
int visibleNodeRows = totalRowsAvailable; int visibleNodeRows = totalRowsAvailable;
int totalColumns = 2; int totalColumns = 2;
int startIndex = scrollIndex * visibleNodeRows * totalColumns; int startIndex = scrollIndex * visibleNodeRows * totalColumns;
if (nodeDB->getMeshNodeByIndex(startIndex)->num == nodeDB->getNodeNum()) {
startIndex++; // skip own node
}
int endIndex = std::min(startIndex + visibleNodeRows * totalColumns, totalEntries); int endIndex = std::min(startIndex + visibleNodeRows * totalColumns, totalEntries);
int yOffset = 0; int yOffset = 0;
@ -462,10 +432,10 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
for (int i = startIndex; i < endIndex; ++i) { for (int i = startIndex; i < endIndex; ++i) {
int xPos = x + (col * columnWidth); int xPos = x + (col * columnWidth);
int yPos = y + yOffset; int yPos = y + yOffset;
renderer(display, nodeList[i].node, xPos, yPos, columnWidth); renderer(display, nodeDB->getMeshNodeByIndex(i), xPos, yPos, columnWidth);
if (extras) { if (extras) {
extras(display, nodeList[i].node, xPos, yPos, columnWidth, heading, lat, lon); extras(display, nodeDB->getMeshNodeByIndex(i), xPos, yPos, columnWidth, heading, lat, lon);
} }
lastNodeY = std::max(lastNodeY, yPos + FONT_HEIGHT_SMALL); lastNodeY = std::max(lastNodeY, yPos + FONT_HEIGHT_SMALL);

View File

@ -23,12 +23,6 @@ namespace NodeListRenderer
typedef void (*EntryRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int); typedef void (*EntryRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int);
typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int, float, double, double); typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int, float, double, double);
// Node entry structure
struct NodeEntry {
meshtastic_NodeInfoLite *node;
uint32_t sortValue;
};
// Node list mode enumeration // Node list mode enumeration
enum NodeListMode { MODE_LAST_HEARD = 0, MODE_HOP_SIGNAL = 1, MODE_DISTANCE = 2, MODE_COUNT = 3 }; enum NodeListMode { MODE_LAST_HEARD = 0, MODE_HOP_SIGNAL = 1, MODE_DISTANCE = 2, MODE_COUNT = 3 };
@ -57,7 +51,6 @@ void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state,
// Utility functions // Utility functions
const char *getCurrentModeTitle(int screenWidth); const char *getCurrentModeTitle(int screenWidth);
void retrieveAndSortNodes(std::vector<NodeEntry> &nodeList);
const char *getSafeNodeName(meshtastic_NodeInfoLite *node); const char *getSafeNodeName(meshtastic_NodeInfoLite *node);
void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields); void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields);

View File

@ -1337,6 +1337,30 @@ bool NodeDB::saveNodeDatabaseToDisk()
return saveProto(nodeDatabaseFileName, nodeDatabaseSize, &meshtastic_NodeDatabase_msg, &nodeDatabase, false); return saveProto(nodeDatabaseFileName, nodeDatabaseSize, &meshtastic_NodeDatabase_msg, &nodeDatabase, false);
} }
void NodeDB::sortMeshDB()
{
if (!Throttle::isWithinTimespanMs(lastSort, 1000 * 5)) {
lastSort = millis();
std::sort(meshNodes->begin(), meshNodes->end(), [](const meshtastic_NodeInfoLite &a, const meshtastic_NodeInfoLite &b) {
if (a.num == myNodeInfo.my_node_num) {
return true;
}
if (b.num == myNodeInfo.my_node_num) {
return false;
}
bool aFav = a.is_favorite;
bool bFav = b.is_favorite;
if (aFav != bFav)
return aFav;
if (a.last_heard == 0 || a.last_heard == UINT32_MAX)
return false;
if (b.last_heard == 0 || b.last_heard == UINT32_MAX)
return true;
return a.last_heard > b.last_heard;
});
}
}
bool NodeDB::saveToDiskNoRetry(int saveWhat) bool NodeDB::saveToDiskNoRetry(int saveWhat)
{ {
bool success = true; bool success = true;
@ -1558,6 +1582,7 @@ void NodeDB::addFromContact(meshtastic_SharedContact contact)
// Mark the node's key as manually verified to indicate trustworthiness. // Mark the node's key as manually verified to indicate trustworthiness.
updateGUIforNode = info; updateGUIforNode = info;
// powerFSM.trigger(EVENT_NODEDB_UPDATED); This event has been retired // powerFSM.trigger(EVENT_NODEDB_UPDATED); This event has been retired
sortMeshDB();
notifyObservers(true); // Force an update whether or not our node counts have changed notifyObservers(true); // Force an update whether or not our node counts have changed
} }
saveNodeDatabaseToDisk(); saveNodeDatabaseToDisk();
@ -1661,6 +1686,7 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
info->has_hops_away = true; info->has_hops_away = true;
info->hops_away = mp.hop_start - mp.hop_limit; info->hops_away = mp.hop_start - mp.hop_limit;
} }
sortMeshDB();
} }
} }

View File

@ -282,6 +282,7 @@ class NodeDB
bool duplicateWarned = false; bool duplicateWarned = false;
uint32_t lastNodeDbSave = 0; // when we last saved our db to flash uint32_t lastNodeDbSave = 0; // when we last saved our db to flash
uint32_t lastBackupAttempt = 0; // when we last tried a backup automatically or manually uint32_t lastBackupAttempt = 0; // when we last tried a backup automatically or manually
uint32_t lastSort = 0; // When last sorted the nodeDB
/// Find a node in our DB, create an empty NodeInfoLite if missing /// Find a node in our DB, create an empty NodeInfoLite if missing
meshtastic_NodeInfoLite *getOrCreateMeshNode(NodeNum n); meshtastic_NodeInfoLite *getOrCreateMeshNode(NodeNum n);
@ -310,6 +311,7 @@ class NodeDB
bool saveChannelsToDisk(); bool saveChannelsToDisk();
bool saveDeviceStateToDisk(); bool saveDeviceStateToDisk();
bool saveNodeDatabaseToDisk(); bool saveNodeDatabaseToDisk();
void sortMeshDB();
}; };
extern NodeDB *nodeDB; extern NodeDB *nodeDB;

View File

@ -245,12 +245,15 @@ void CannedMessageModule::updateDestinationSelectionList()
} }
} }
/* As the nodeDB is sorted, can skip this step
// Sort by favorite, then last heard // Sort by favorite, then last heard
std::sort(this->filteredNodes.begin(), this->filteredNodes.end(), [](const NodeEntry &a, const NodeEntry &b) { std::sort(this->filteredNodes.begin(), this->filteredNodes.end(), [](const NodeEntry &a, const NodeEntry &b) {
if (a.node->is_favorite != b.node->is_favorite) if (a.node->is_favorite != b.node->is_favorite)
return a.node->is_favorite > b.node->is_favorite; return a.node->is_favorite > b.node->is_favorite;
return a.lastHeard < b.lastHeard; return a.lastHeard < b.lastHeard;
}); });
*/
scrollIndex = 0; // Show first result at the top scrollIndex = 0; // Show first result at the top
destIndex = 0; // Highlight the first entry destIndex = 0; // Highlight the first entry
if (nodesChanged && runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION) { if (nodesChanged && runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION) {