2020-02-02 20:45:32 +00:00
|
|
|
|
|
|
|
#include <Arduino.h>
|
|
|
|
#include <assert.h>
|
2020-04-15 03:22:27 +00:00
|
|
|
#include <string>
|
2020-02-02 20:45:32 +00:00
|
|
|
|
2020-03-19 02:15:51 +00:00
|
|
|
#include "GPS.h"
|
2020-04-10 19:18:48 +00:00
|
|
|
//#include "MeshBluetoothService.h"
|
2020-03-19 02:15:51 +00:00
|
|
|
#include "MeshService.h"
|
2020-02-03 17:13:19 +00:00
|
|
|
#include "NodeDB.h"
|
2020-02-21 16:41:36 +00:00
|
|
|
#include "Periodic.h"
|
2020-03-03 21:31:44 +00:00
|
|
|
#include "PowerFSM.h"
|
2020-03-19 02:15:51 +00:00
|
|
|
#include "main.h"
|
|
|
|
#include "mesh-pb-constants.h"
|
2020-02-02 20:45:32 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
receivedPacketQueue - this is a queue of messages we've received from the mesh, which we are keeping to deliver to the phone.
|
2020-03-19 02:15:51 +00:00
|
|
|
It is implemented with a FreeRTos queue (wrapped with a little RTQueue class) of pointers to MeshPacket protobufs (which were
|
|
|
|
alloced with new). After a packet ptr is removed from the queue and processed it should be deleted. (eventually we should move
|
|
|
|
sent packets into a 'sentToPhone' queue of packets we can delete just as soon as we are sure the phone has acked those packets -
|
|
|
|
when the phone writes to FromNum)
|
2020-02-02 20:45:32 +00:00
|
|
|
|
2020-03-19 02:15:51 +00:00
|
|
|
mesh - an instance of Mesh class. Which manages the interface to the mesh radio library, reception of packets from other nodes,
|
|
|
|
arbitrating to select a node number and keeping the current nodedb.
|
2020-02-02 20:45:32 +00:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
2020-02-08 20:42:54 +00:00
|
|
|
/* Broadcast when a newly powered mesh node wants to find a node num it can use
|
|
|
|
|
|
|
|
The algoritm is as follows:
|
2020-03-19 02:15:51 +00:00
|
|
|
* when a node starts up, it broadcasts their user and the normal flow is for all other nodes to reply with their User as well (so
|
|
|
|
the new node can build its node db)
|
|
|
|
* If a node ever receives a User (not just the first broadcast) message where the sender node number equals our node number, that
|
|
|
|
indicates a collision has occurred and the following steps should happen:
|
2020-02-08 20:42:54 +00:00
|
|
|
|
2020-03-19 02:15:51 +00:00
|
|
|
If the receiving node (that was already in the mesh)'s macaddr is LOWER than the new User who just tried to sign in: it gets to
|
|
|
|
keep its nodenum. We send a broadcast message of OUR User (we use a broadcast so that the other node can receive our message,
|
|
|
|
considering we have the same id - it also serves to let observers correct their nodedb) - this case is rare so it should be okay.
|
2020-02-08 20:42:54 +00:00
|
|
|
|
2020-03-19 02:15:51 +00:00
|
|
|
If any node receives a User where the macaddr is GTE than their local macaddr, they have been vetoed and should pick a new random
|
|
|
|
nodenum (filtering against whatever it knows about the nodedb) and rebroadcast their User.
|
2020-02-08 20:42:54 +00:00
|
|
|
|
|
|
|
FIXME in the initial proof of concept we just skip the entire want/deny flow and just hand pick node numbers at first.
|
|
|
|
*/
|
2020-02-02 20:45:32 +00:00
|
|
|
|
2020-02-08 20:42:54 +00:00
|
|
|
MeshService service;
|
2020-02-08 18:00:15 +00:00
|
|
|
|
2020-04-17 16:48:54 +00:00
|
|
|
#include "Router.h"
|
2020-02-03 17:13:19 +00:00
|
|
|
|
2020-04-25 17:59:40 +00:00
|
|
|
static uint32_t sendOwnerCb()
|
|
|
|
{
|
|
|
|
service.sendOurOwner();
|
|
|
|
|
|
|
|
return radioConfig.preferences.send_owner_interval * radioConfig.preferences.position_broadcast_secs * 1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Periodic sendOwnerPeriod(sendOwnerCb);
|
|
|
|
|
2020-04-17 16:48:54 +00:00
|
|
|
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
|
2020-02-02 20:45:32 +00:00
|
|
|
{
|
2020-02-03 17:13:19 +00:00
|
|
|
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
|
2020-02-02 20:45:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MeshService::init()
|
|
|
|
{
|
2020-04-25 17:59:40 +00:00
|
|
|
sendOwnerPeriod.setup();
|
2020-02-04 17:00:17 +00:00
|
|
|
nodeDB.init();
|
2020-02-06 16:49:33 +00:00
|
|
|
|
2020-05-04 18:15:05 +00:00
|
|
|
gpsObserver.observe(gps);
|
2020-04-17 16:48:54 +00:00
|
|
|
packetReceivedObserver.observe(&router.notifyPacketReceived);
|
2020-02-06 18:58:19 +00:00
|
|
|
}
|
|
|
|
|
2020-03-25 20:09:12 +00:00
|
|
|
void MeshService::sendOurOwner(NodeNum dest, bool wantReplies)
|
2020-02-06 18:58:19 +00:00
|
|
|
{
|
2020-05-19 18:56:17 +00:00
|
|
|
MeshPacket *p = router.allocForSending();
|
2020-02-06 18:58:19 +00:00
|
|
|
p->to = dest;
|
2020-05-10 00:51:20 +00:00
|
|
|
p->decoded.want_response = wantReplies;
|
2020-05-12 20:35:22 +00:00
|
|
|
p->decoded.which_payload = SubPacket_user_tag;
|
2020-05-10 00:51:20 +00:00
|
|
|
User &u = p->decoded.user;
|
2020-02-06 18:58:19 +00:00
|
|
|
u = owner;
|
2020-02-07 17:36:15 +00:00
|
|
|
DEBUG_MSG("sending owner %s/%s/%s\n", u.id, u.long_name, u.short_name);
|
2020-02-06 18:58:19 +00:00
|
|
|
|
|
|
|
sendToMesh(p);
|
2020-02-02 20:45:32 +00:00
|
|
|
}
|
|
|
|
|
2020-02-13 03:58:44 +00:00
|
|
|
/// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all
|
2020-04-17 16:48:54 +00:00
|
|
|
const MeshPacket *MeshService::handleFromRadioUser(const MeshPacket *mp)
|
2020-02-13 03:58:44 +00:00
|
|
|
{
|
|
|
|
bool wasBroadcast = mp->to == NODENUM_BROADCAST;
|
|
|
|
bool isCollision = mp->from == myNodeInfo.my_node_num;
|
|
|
|
|
|
|
|
// we win if we have a lower macaddr
|
2020-05-10 00:51:20 +00:00
|
|
|
bool weWin = memcmp(&owner.macaddr, &mp->decoded.user.macaddr, sizeof(owner.macaddr)) < 0;
|
2020-02-13 03:58:44 +00:00
|
|
|
|
2020-03-19 02:15:51 +00:00
|
|
|
if (isCollision) {
|
|
|
|
if (weWin) {
|
2020-02-13 03:58:44 +00:00
|
|
|
DEBUG_MSG("NOTE! Received a nodenum collision and we are vetoing\n");
|
|
|
|
|
|
|
|
mp = NULL;
|
|
|
|
|
|
|
|
sendOurOwner(); // send our owner as a _broadcast_ because that other guy is mistakenly using our nodenum
|
2020-03-19 02:15:51 +00:00
|
|
|
} else {
|
2020-02-13 03:58:44 +00:00
|
|
|
// we lost, we need to try for a new nodenum!
|
|
|
|
DEBUG_MSG("NOTE! Received a nodenum collision we lost, so picking a new nodenum\n");
|
2020-03-19 02:15:51 +00:00
|
|
|
nodeDB.updateFrom(
|
|
|
|
*mp); // update the DB early - before trying to repick (so we don't select the same node number again)
|
2020-02-13 03:58:44 +00:00
|
|
|
nodeDB.pickNewNodeNum();
|
|
|
|
sendOurOwner(); // broadcast our new attempt at a node number
|
|
|
|
}
|
2020-03-19 02:15:51 +00:00
|
|
|
} else if (wasBroadcast) {
|
|
|
|
// If we haven't yet abandoned the packet and it was a broadcast, reply (just to them) with our User record so they can
|
|
|
|
// build their DB
|
2020-02-13 03:58:44 +00:00
|
|
|
|
|
|
|
// Someone just sent us a User, reply with our Owner
|
|
|
|
DEBUG_MSG("Received broadcast Owner from 0x%x, replying with our owner\n", mp->from);
|
|
|
|
|
|
|
|
sendOurOwner(mp->from);
|
|
|
|
|
2020-05-10 00:51:20 +00:00
|
|
|
String lcd = String("Joined: ") + mp->decoded.user.long_name + "\n";
|
2020-03-15 23:47:38 +00:00
|
|
|
screen.print(lcd.c_str());
|
2020-02-13 03:58:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return mp;
|
|
|
|
}
|
|
|
|
|
2020-04-17 16:48:54 +00:00
|
|
|
void MeshService::handleIncomingPosition(const MeshPacket *mp)
|
2020-02-26 17:00:53 +00:00
|
|
|
{
|
2020-05-12 20:35:22 +00:00
|
|
|
if (mp->which_payload == MeshPacket_decoded_tag && mp->decoded.which_payload == SubPacket_position_tag) {
|
2020-05-10 00:51:20 +00:00
|
|
|
DEBUG_MSG("handled incoming position time=%u\n", mp->decoded.position.time);
|
2020-02-26 17:00:53 +00:00
|
|
|
|
2020-05-10 00:51:20 +00:00
|
|
|
if (mp->decoded.position.time) {
|
2020-02-26 17:00:53 +00:00
|
|
|
struct timeval tv;
|
2020-05-10 00:51:20 +00:00
|
|
|
uint32_t secs = mp->decoded.position.time;
|
2020-02-26 17:00:53 +00:00
|
|
|
|
|
|
|
tv.tv_sec = secs;
|
|
|
|
tv.tv_usec = 0;
|
|
|
|
|
2020-05-04 18:15:05 +00:00
|
|
|
perhapsSetRTC(&tv);
|
2020-02-26 17:00:53 +00:00
|
|
|
}
|
2020-03-25 20:35:49 +00:00
|
|
|
} else {
|
|
|
|
DEBUG_MSG("Ignoring incoming packet - not a position\n");
|
2020-02-26 17:00:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-17 16:48:54 +00:00
|
|
|
int MeshService::handleFromRadio(const MeshPacket *mp)
|
2020-02-18 01:47:01 +00:00
|
|
|
{
|
2020-03-03 21:31:44 +00:00
|
|
|
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
|
|
|
|
|
2020-02-26 17:00:53 +00:00
|
|
|
// If it is a position packet, perhaps set our clock (if we don't have a GPS of our own, otherwise wait for that to work)
|
2020-05-04 18:15:05 +00:00
|
|
|
if (!gps->isConnected)
|
2020-03-03 21:31:44 +00:00
|
|
|
handleIncomingPosition(mp);
|
2020-03-25 20:35:49 +00:00
|
|
|
else {
|
|
|
|
DEBUG_MSG("Ignoring incoming time, because we have a GPS\n");
|
|
|
|
}
|
2020-02-26 17:00:53 +00:00
|
|
|
|
2020-05-12 20:35:22 +00:00
|
|
|
if (mp->which_payload == MeshPacket_decoded_tag && mp->decoded.which_payload == SubPacket_user_tag) {
|
2020-02-18 01:47:01 +00:00
|
|
|
mp = handleFromRadioUser(mp);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we veto a received User packet, we don't put it into the DB or forward it to the phone (to prevent confusing it)
|
2020-03-19 02:15:51 +00:00
|
|
|
if (mp) {
|
2020-02-19 18:53:09 +00:00
|
|
|
DEBUG_MSG("Forwarding to phone, from=0x%x, rx_time=%u\n", mp->from, mp->rx_time);
|
2020-02-18 01:47:01 +00:00
|
|
|
nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio
|
|
|
|
|
|
|
|
fromNum++;
|
|
|
|
|
2020-03-19 02:15:51 +00:00
|
|
|
if (toPhoneQueue.numFree() == 0) {
|
2020-02-18 01:47:01 +00:00
|
|
|
DEBUG_MSG("NOTE: tophone queue is full, discarding oldest\n");
|
|
|
|
MeshPacket *d = toPhoneQueue.dequeuePtr(0);
|
|
|
|
if (d)
|
|
|
|
releaseToPool(d);
|
|
|
|
}
|
2020-04-17 16:48:54 +00:00
|
|
|
|
|
|
|
MeshPacket *copied = packetPool.allocCopy(*mp);
|
|
|
|
assert(toPhoneQueue.enqueue(copied, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages
|
2020-02-20 02:51:17 +00:00
|
|
|
|
2020-05-10 00:51:20 +00:00
|
|
|
if (mp->decoded.want_response)
|
2020-02-20 02:51:17 +00:00
|
|
|
sendNetworkPing(mp->from);
|
2020-04-05 20:58:38 +00:00
|
|
|
} else {
|
|
|
|
DEBUG_MSG("Not delivering vetoed User message\n");
|
|
|
|
}
|
2020-02-13 03:58:44 +00:00
|
|
|
|
2020-04-17 16:48:54 +00:00
|
|
|
return 0;
|
2020-02-02 20:45:32 +00:00
|
|
|
}
|
|
|
|
|
2020-02-08 17:39:26 +00:00
|
|
|
/// Do idle processing (mostly processing messages which have been queued from the radio)
|
|
|
|
void MeshService::loop()
|
|
|
|
{
|
2020-04-17 16:48:54 +00:00
|
|
|
if (oldFromNum != fromNum) { // We don't want to generate extra notifies for multiple new packets
|
|
|
|
fromNumChanged.notifyObservers(fromNum);
|
2020-04-17 20:05:16 +00:00
|
|
|
oldFromNum = fromNum;
|
2020-04-17 16:48:54 +00:00
|
|
|
}
|
2020-02-08 17:39:26 +00:00
|
|
|
}
|
|
|
|
|
2020-02-11 19:56:48 +00:00
|
|
|
/// The radioConfig object just changed, call this to force the hw to change to the new settings
|
|
|
|
void MeshService::reloadConfig()
|
|
|
|
{
|
|
|
|
// If we can successfully set this radio to these settings, save them to disk
|
2020-03-30 23:05:28 +00:00
|
|
|
nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
|
2020-04-14 18:40:49 +00:00
|
|
|
configChanged.notifyObservers(NULL);
|
2020-02-11 19:56:48 +00:00
|
|
|
nodeDB.saveToDisk();
|
|
|
|
}
|
|
|
|
|
2020-04-22 21:55:36 +00:00
|
|
|
/**
|
|
|
|
* Given a ToRadio buffer parse it and properly handle it (setup radio, owner or send packet into the mesh)
|
2020-04-25 17:59:40 +00:00
|
|
|
* Called by PhoneAPI.handleToRadio. Note: p is a scratch buffer, this function is allowed to write to it but it can not keep a
|
|
|
|
* reference
|
2020-04-22 21:55:36 +00:00
|
|
|
*/
|
|
|
|
void MeshService::handleToRadio(MeshPacket &p)
|
2020-02-02 20:45:32 +00:00
|
|
|
{
|
2020-04-22 21:55:36 +00:00
|
|
|
handleIncomingPosition(&p); // If it is a position packet, perhaps set our clock
|
2020-02-02 20:45:32 +00:00
|
|
|
|
2020-04-22 21:55:36 +00:00
|
|
|
if (p.from == 0) // If the phone didn't set a sending node ID, use ours
|
|
|
|
p.from = nodeDB.getNodeNum();
|
2020-02-19 18:53:09 +00:00
|
|
|
|
2020-04-22 21:55:36 +00:00
|
|
|
if (p.id == 0)
|
|
|
|
p.id = generatePacketId(); // If the phone didn't supply one, then pick one
|
2020-04-17 18:52:20 +00:00
|
|
|
|
2020-05-04 18:15:05 +00:00
|
|
|
p.rx_time = getValidTime(); // Record the time the packet arrived from the phone
|
|
|
|
// (so we update our nodedb for the local node)
|
2020-04-17 19:41:01 +00:00
|
|
|
|
2020-04-22 21:55:36 +00:00
|
|
|
// Send the packet into the mesh
|
2020-02-19 18:53:09 +00:00
|
|
|
|
2020-04-22 21:55:36 +00:00
|
|
|
sendToMesh(packetPool.allocCopy(p));
|
2020-04-17 18:52:20 +00:00
|
|
|
|
2020-04-22 21:55:36 +00:00
|
|
|
bool loopback = false; // if true send any packet the phone sends back itself (for testing)
|
|
|
|
if (loopback) {
|
|
|
|
// no need to copy anymore because handle from radio assumes it should _not_ delete
|
|
|
|
// packetPool.allocCopy(r.variant.packet);
|
|
|
|
handleFromRadio(&p);
|
|
|
|
// handleFromRadio will tell the phone a new packet arrived
|
2020-03-15 23:27:15 +00:00
|
|
|
}
|
2020-02-02 20:45:32 +00:00
|
|
|
}
|
|
|
|
|
2020-02-06 16:49:33 +00:00
|
|
|
void MeshService::sendToMesh(MeshPacket *p)
|
2020-02-02 20:45:32 +00:00
|
|
|
{
|
2020-02-17 00:03:16 +00:00
|
|
|
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
|
2020-02-19 04:17:11 +00:00
|
|
|
|
2020-03-19 02:15:51 +00:00
|
|
|
// Strip out any time information before sending packets to other nodes - to keep the wire size small (and because other
|
|
|
|
// nodes shouldn't trust it anyways) Note: for now, we allow a device with a local GPS to include the time, so that gpsless
|
|
|
|
// devices can get time.
|
2020-05-12 20:35:22 +00:00
|
|
|
if (p->which_payload == MeshPacket_decoded_tag && p->decoded.which_payload == SubPacket_position_tag) {
|
2020-05-04 18:15:05 +00:00
|
|
|
if (!gps->isConnected) {
|
2020-05-10 00:51:20 +00:00
|
|
|
DEBUG_MSG("Stripping time %u from position send\n", p->decoded.position.time);
|
|
|
|
p->decoded.position.time = 0;
|
2020-03-25 20:09:12 +00:00
|
|
|
} else
|
2020-05-10 00:51:20 +00:00
|
|
|
DEBUG_MSG("Providing time to mesh %u\n", p->decoded.position.time);
|
2020-02-26 17:00:53 +00:00
|
|
|
}
|
2020-02-19 23:29:18 +00:00
|
|
|
|
2020-05-19 18:56:17 +00:00
|
|
|
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
|
2020-05-25 15:19:14 +00:00
|
|
|
router.sendLocal(p);
|
2020-02-06 16:49:33 +00:00
|
|
|
}
|
2020-02-02 20:45:32 +00:00
|
|
|
|
2020-03-25 20:09:12 +00:00
|
|
|
void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
|
2020-02-12 22:07:06 +00:00
|
|
|
{
|
|
|
|
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
|
|
|
assert(node);
|
|
|
|
|
2020-03-25 20:09:12 +00:00
|
|
|
DEBUG_MSG("Sending network ping to 0x%x, with position=%d, wantReplies=%d\n", dest, node->has_position, wantReplies);
|
2020-02-12 22:07:06 +00:00
|
|
|
if (node->has_position)
|
2020-03-25 20:09:12 +00:00
|
|
|
sendOurPosition(dest, wantReplies);
|
2020-02-12 22:07:06 +00:00
|
|
|
else
|
2020-03-25 20:09:12 +00:00
|
|
|
sendOurOwner(dest, wantReplies);
|
2020-02-12 22:07:06 +00:00
|
|
|
}
|
|
|
|
|
2020-03-25 20:09:12 +00:00
|
|
|
void MeshService::sendOurPosition(NodeNum dest, bool wantReplies)
|
2020-02-12 22:07:06 +00:00
|
|
|
{
|
|
|
|
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
|
|
|
|
assert(node);
|
|
|
|
assert(node->has_position);
|
|
|
|
|
|
|
|
// Update our local node info with our position (even if we don't decide to update anyone else)
|
2020-05-19 18:56:17 +00:00
|
|
|
MeshPacket *p = router.allocForSending();
|
2020-02-20 02:51:17 +00:00
|
|
|
p->to = dest;
|
2020-05-12 20:35:22 +00:00
|
|
|
p->decoded.which_payload = SubPacket_position_tag;
|
2020-05-10 00:51:20 +00:00
|
|
|
p->decoded.position = node->position;
|
|
|
|
p->decoded.want_response = wantReplies;
|
|
|
|
p->decoded.position.time = getValidTime(); // This nodedb timestamp might be stale, so update it if our clock is valid.
|
2020-02-12 22:07:06 +00:00
|
|
|
sendToMesh(p);
|
|
|
|
}
|
|
|
|
|
2020-04-10 19:40:44 +00:00
|
|
|
int MeshService::onGPSChanged(void *unused)
|
2020-02-06 18:58:19 +00:00
|
|
|
{
|
2020-05-01 15:31:52 +00:00
|
|
|
// DEBUG_MSG("got gps notify\n");
|
2020-04-10 19:18:48 +00:00
|
|
|
|
2020-02-12 22:07:06 +00:00
|
|
|
// Update our local node info with our position (even if we don't decide to update anyone else)
|
2020-05-19 18:56:17 +00:00
|
|
|
MeshPacket *p = router.allocForSending();
|
2020-05-12 20:35:22 +00:00
|
|
|
p->decoded.which_payload = SubPacket_position_tag;
|
2020-03-18 21:51:54 +00:00
|
|
|
|
2020-05-10 00:51:20 +00:00
|
|
|
Position &pos = p->decoded.position;
|
2020-03-19 01:34:22 +00:00
|
|
|
// !zero or !zero lat/long means valid
|
2020-05-04 18:15:05 +00:00
|
|
|
if (gps->latitude != 0 || gps->longitude != 0) {
|
|
|
|
if (gps->altitude != 0)
|
|
|
|
pos.altitude = gps->altitude;
|
|
|
|
pos.latitude_i = gps->latitude;
|
|
|
|
pos.longitude_i = gps->longitude;
|
|
|
|
pos.time = getValidTime();
|
2020-03-19 01:34:22 +00:00
|
|
|
}
|
2020-02-06 16:49:33 +00:00
|
|
|
|
2020-02-12 22:07:06 +00:00
|
|
|
// We limit our GPS broadcasts to a max rate
|
|
|
|
static uint32_t lastGpsSend;
|
|
|
|
uint32_t now = millis();
|
2020-03-19 02:15:51 +00:00
|
|
|
if (lastGpsSend == 0 || now - lastGpsSend > radioConfig.preferences.position_broadcast_secs * 1000) {
|
2020-02-12 22:07:06 +00:00
|
|
|
lastGpsSend = now;
|
|
|
|
DEBUG_MSG("Sending position to mesh\n");
|
|
|
|
|
|
|
|
sendToMesh(p);
|
2020-03-19 02:15:51 +00:00
|
|
|
} else {
|
2020-02-12 22:07:06 +00:00
|
|
|
// We don't need to send this packet to anyone else, but it still serves as a nice uniform way to update our local state
|
|
|
|
nodeDB.updateFrom(*p);
|
|
|
|
|
|
|
|
releaseToPool(p);
|
|
|
|
}
|
2020-04-10 19:40:44 +00:00
|
|
|
|
|
|
|
return 0;
|
2020-02-02 20:45:32 +00:00
|
|
|
}
|