NeighborInfo: Only keep neighbors in RAM (#3660)

* NeighborInfo: Only keep neighbors in RAM
It fills up quickly when nodes are running >=2.3

* Defer first transmission as it's usually empty

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
GUVWAF
2024-04-18 23:16:50 +02:00
committed by GitHub
parent 4c0b7ea409
commit e4b5f2ce14
2 changed files with 50 additions and 121 deletions

View File

@@ -4,11 +4,8 @@
#include "NodeDB.h"
#include "RTC.h"
#define MAX_NUM_NEIGHBORS 10 // also defined in NeighborInfo protobuf options
NeighborInfoModule *neighborInfoModule;
static const char *neighborInfoConfigFile = "/prefs/neighbors.proto";
/*
Prints a single neighbor info packet and associated neighbors
Uses LOG_DEBUG, which equates to Console.log
@@ -30,26 +27,23 @@ NOTE: for debugging only
*/
void NeighborInfoModule::printNodeDBNeighbors()
{
int num_neighbors = getNumNeighbors();
LOG_DEBUG("Our NodeDB contains %d neighbors\n", num_neighbors);
for (int i = 0; i < num_neighbors; i++) {
const meshtastic_Neighbor *dbEntry = getNeighborByIndex(i);
LOG_DEBUG(" Node %d: node_id=0x%x, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
LOG_DEBUG("Our NodeDB contains %d neighbors\n", neighbors.size());
for (size_t i = 0; i < neighbors.size(); i++) {
LOG_DEBUG("Node %d: node_id=0x%x, snr=%.2f\n", i, neighbors[i].node_id, neighbors[i].snr);
}
}
/* Send our initial owner announcement 35 seconds after we start (to give network time to setup) */
NeighborInfoModule::NeighborInfoModule()
: ProtobufModule("neighborinfo", meshtastic_PortNum_NEIGHBORINFO_APP, &meshtastic_NeighborInfo_msg),
concurrency::OSThread("NeighborInfoModule"), neighbors(neighborState.neighbors),
numNeighbors(&neighborState.neighbors_count)
concurrency::OSThread("NeighborInfoModule")
{
ourPortNum = meshtastic_PortNum_NEIGHBORINFO_APP;
if (moduleConfig.neighbor_info.enabled) {
isPromiscuous = true; // Update neighbors from all packets
this->loadProtoForModule();
setIntervalFromNow(35 * 1000);
setIntervalFromNow(
Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_broadcast_interval_secs));
} else {
LOG_DEBUG("NeighborInfoModule is disabled\n");
disable();
@@ -63,18 +57,17 @@ Assumes that the neighborInfo packet has been allocated
*/
uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighborInfo)
{
uint my_node_id = nodeDB->getNodeNum();
NodeNum my_node_id = nodeDB->getNodeNum();
neighborInfo->node_id = my_node_id;
neighborInfo->last_sent_by_id = my_node_id;
neighborInfo->node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval;
int num_neighbors = cleanUpNeighbors();
cleanUpNeighbors();
for (int i = 0; i < num_neighbors; i++) {
const meshtastic_Neighbor *dbEntry = getNeighborByIndex(i);
if ((neighborInfo->neighbors_count < MAX_NUM_NEIGHBORS) && (dbEntry->node_id != my_node_id)) {
neighborInfo->neighbors[neighborInfo->neighbors_count].node_id = dbEntry->node_id;
neighborInfo->neighbors[neighborInfo->neighbors_count].snr = dbEntry->snr;
for (auto nbr : neighbors) {
if ((neighborInfo->neighbors_count < MAX_NUM_NEIGHBORS) && (nbr.node_id != my_node_id)) {
neighborInfo->neighbors[neighborInfo->neighbors_count].node_id = nbr.node_id;
neighborInfo->neighbors[neighborInfo->neighbors_count].snr = nbr.snr;
// Note: we don't set the last_rx_time and node_broadcast_intervals_secs here, because we don't want to send this over
// the mesh
neighborInfo->neighbors_count++;
@@ -85,41 +78,22 @@ uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighb
}
/*
Remove neighbors from the database that we haven't heard from in a while
@returns new number of neighbors
Remove neighbors from the database that we haven't heard from in a while
*/
size_t NeighborInfoModule::cleanUpNeighbors()
void NeighborInfoModule::cleanUpNeighbors()
{
uint32_t now = getTime();
int num_neighbors = getNumNeighbors();
NodeNum my_node_id = nodeDB->getNodeNum();
// Find neighbors to remove
std::vector<int> indices_to_remove;
for (int i = 0; i < num_neighbors; i++) {
const meshtastic_Neighbor *dbEntry = getNeighborByIndex(i);
for (auto it = neighbors.rbegin(); it != neighbors.rend();) {
// We will remove a neighbor if we haven't heard from them in twice the broadcast interval
if ((now - dbEntry->last_rx_time > dbEntry->node_broadcast_interval_secs * 2) && (dbEntry->node_id != my_node_id)) {
indices_to_remove.push_back(i);
if ((now - it->last_rx_time > it->node_broadcast_interval_secs * 2) && (it->node_id != my_node_id)) {
LOG_DEBUG("Removing neighbor with node ID 0x%x\n", it->node_id);
it = std::vector<meshtastic_Neighbor>::reverse_iterator(
neighbors.erase(std::next(it).base())); // Erase the element and update the iterator
} else {
++it;
}
}
// Update the neighbor list
for (uint i = 0; i < indices_to_remove.size(); i++) {
int index = indices_to_remove[i];
LOG_DEBUG("Removing neighbor with node ID 0x%x\n", neighbors[index].node_id);
for (int j = index; j < num_neighbors - 1; j++) {
neighbors[j] = neighbors[j + 1];
}
(*numNeighbors)--;
}
// Save the neighbor list if we removed any neighbors or neighbors were already updated upon receiving a packet
if (indices_to_remove.size() > 0 || shouldSave) {
saveProtoForModule();
}
return *numNeighbors;
}
/* Send neighbor info to the mesh */
@@ -143,7 +117,9 @@ Will be used for broadcast.
int32_t NeighborInfoModule::runOnce()
{
bool requestReplies = false;
sendNeighborInfo(NODENUM_BROADCAST, requestReplies);
if (airTime->isTxAllowedChannelUtil(true) && airTime->isTxAllowedAirUtil()) {
sendNeighborInfo(NODENUM_BROADCAST, requestReplies);
}
return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_broadcast_interval_secs);
}
@@ -178,10 +154,7 @@ void NeighborInfoModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtas
void NeighborInfoModule::resetNeighbors()
{
*numNeighbors = 0;
neighborState.neighbors_count = 0;
memset(neighborState.neighbors, 0, sizeof(neighborState.neighbors));
saveProtoForModule();
neighbors.clear();
}
void NeighborInfoModule::updateNeighbors(const meshtastic_MeshPacket &mp, const meshtastic_NeighborInfo *np)
@@ -201,61 +174,36 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen
n = nodeDB->getNodeNum();
}
// look for one in the existing list
for (int i = 0; i < (*numNeighbors); i++) {
meshtastic_Neighbor *nbr = &neighbors[i];
if (nbr->node_id == n) {
for (size_t i = 0; i < neighbors.size(); i++) {
if (neighbors[i].node_id == n) {
// if found, update it
nbr->snr = snr;
nbr->last_rx_time = getTime();
neighbors[i].snr = snr;
neighbors[i].last_rx_time = getTime();
// Only if this is the original sender, the broadcast interval corresponds to it
if (originalSender == n && node_broadcast_interval_secs != 0)
nbr->node_broadcast_interval_secs = node_broadcast_interval_secs;
return nbr;
neighbors[i].node_broadcast_interval_secs = node_broadcast_interval_secs;
return &neighbors[i];
}
}
// otherwise, allocate one and assign data to it
// TODO: max memory for the database should take neighbors into account, but currently doesn't
if (*numNeighbors < MAX_NUM_NEIGHBORS) {
(*numNeighbors)++;
}
meshtastic_Neighbor *new_nbr = &neighbors[((*numNeighbors) - 1)];
new_nbr->node_id = n;
new_nbr->snr = snr;
new_nbr->last_rx_time = getTime();
meshtastic_Neighbor new_nbr = meshtastic_Neighbor_init_zero;
new_nbr.node_id = n;
new_nbr.snr = snr;
new_nbr.last_rx_time = getTime();
// Only if this is the original sender, the broadcast interval corresponds to it
if (originalSender == n && node_broadcast_interval_secs != 0)
new_nbr->node_broadcast_interval_secs = node_broadcast_interval_secs;
new_nbr.node_broadcast_interval_secs = node_broadcast_interval_secs;
else // Assume the same broadcast interval as us for the neighbor if we don't know it
new_nbr->node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval;
shouldSave = true; // Save the new neighbor upon next cleanup
return new_nbr;
}
new_nbr.node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval;
void NeighborInfoModule::loadProtoForModule()
{
if (nodeDB->loadProto(neighborInfoConfigFile, meshtastic_NeighborInfo_size, sizeof(meshtastic_NeighborInfo),
&meshtastic_NeighborInfo_msg, &neighborState) != LoadFileResult::SUCCESS) {
neighborState = meshtastic_NeighborInfo_init_zero;
if (neighbors.size() < MAX_NUM_NEIGHBORS) {
neighbors.push_back(new_nbr);
} else {
// If we have too many neighbors, replace the oldest one
LOG_WARN("Neighbor DB is full, replacing oldest neighbor\n");
neighbors.erase(neighbors.begin());
neighbors.push_back(new_nbr);
}
}
/**
* @brief Save the module config to file.
*
* @return true On success.
* @return false On error.
*/
bool NeighborInfoModule::saveProtoForModule()
{
bool okay = true;
#ifdef FS
FS.mkdir("/prefs");
#endif
okay &= nodeDB->saveProto(neighborInfoConfigFile, meshtastic_NeighborInfo_size, &meshtastic_NeighborInfo_msg, &neighborState);
if (okay)
shouldSave = false;
return okay;
return &neighbors.back();
}