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>
pull/3646/head^2
GUVWAF 2024-04-18 23:16:50 +02:00 zatwierdzone przez GitHub
rodzic 4c0b7ea409
commit e4b5f2ce14
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
2 zmienionych plików z 50 dodań i 121 usunięć

Wyświetl plik

@ -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();
}

Wyświetl plik

@ -1,13 +1,13 @@
#pragma once
#include "ProtobufModule.h"
#define MAX_NUM_NEIGHBORS 10 // also defined in NeighborInfo protobuf options
/*
* Neighborinfo module for sending info on each node's 0-hop neighbors to the mesh
*/
class NeighborInfoModule : public ProtobufModule<meshtastic_NeighborInfo>, private concurrency::OSThread
{
meshtastic_Neighbor *neighbors;
pb_size_t *numNeighbors;
std::vector<meshtastic_Neighbor> neighbors;
public:
/*
@ -18,15 +18,7 @@ class NeighborInfoModule : public ProtobufModule<meshtastic_NeighborInfo>, priva
/* Reset neighbor info after clearing nodeDB*/
void resetNeighbors();
bool saveProtoForModule();
private:
bool shouldSave = false; // Whether we should save the neighbor info to flash
protected:
// Note: this holds our local info.
meshtastic_NeighborInfo neighborState;
/*
* Called to handle a particular incoming message
* @return true if you've guaranteed you've handled this message and no other handlers should be considered for it
@ -40,10 +32,9 @@ class NeighborInfoModule : public ProtobufModule<meshtastic_NeighborInfo>, priva
uint32_t collectNeighborInfo(meshtastic_NeighborInfo *neighborInfo);
/*
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 cleanUpNeighbors();
void cleanUpNeighbors();
/* Allocate a new NeighborInfo packet */
meshtastic_NeighborInfo *allocateNeighborInfoPacket();
@ -56,22 +47,12 @@ class NeighborInfoModule : public ProtobufModule<meshtastic_NeighborInfo>, priva
*/
void sendNeighborInfo(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
size_t getNumNeighbors() { return *numNeighbors; }
meshtastic_Neighbor *getNeighborByIndex(size_t x)
{
assert(x < *numNeighbors);
return &neighbors[x];
}
/* update neighbors with subpacket sniffed from network */
void updateNeighbors(const meshtastic_MeshPacket &mp, const meshtastic_NeighborInfo *np);
/* update a NeighborInfo packet with our NodeNum as last_sent_by_id */
void alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtastic_NeighborInfo *n) override;
void loadProtoForModule();
/* Does our periodic broadcast */
int32_t runOnce() override;