massive WIP updates to create a clean Router abstraction for mesh

This commit is contained in:
geeksville
2020-04-17 09:48:54 -07:00
parent 6eb74415ab
commit f108c576a7
18 changed files with 303 additions and 146 deletions

View File

@@ -11,10 +11,7 @@
#define MAX_RHPACKETLEN 251
static uint8_t radiobuf[MAX_RHPACKETLEN];
CustomRF95::CustomRF95(MemoryPool<MeshPacket> &_pool, PointerQueue<MeshPacket> &_rxDest)
: RH_RF95(NSS_GPIO, RF95_IRQ_GPIO), RadioInterface(_pool, _rxDest), txQueue(MAX_TX_QUEUE)
{
}
CustomRF95::CustomRF95() : RH_RF95(NSS_GPIO, RF95_IRQ_GPIO), txQueue(MAX_TX_QUEUE) {}
bool CustomRF95::canSleep()
{
@@ -58,7 +55,7 @@ ErrorCode CustomRF95::send(MeshPacket *p)
ErrorCode res = txQueue.enqueue(p, 0) ? ERRNO_OK : ERRNO_UNKNOWN;
if (res != ERRNO_OK) // we weren't able to queue it, so we must drop it to prevent leaks
pool.release(p);
packetPool.release(p);
return res;
}
@@ -76,7 +73,7 @@ void CustomRF95::handleInterrupt()
if (sendingPacket) // Were we sending?
{
// We are done sending that packet, release it
pool.releaseFromISR(sendingPacket, &higherPriWoken);
packetPool.releaseFromISR(sendingPacket, &higherPriWoken);
sendingPacket = NULL;
// DEBUG_MSG("Done with send\n");
}
@@ -94,7 +91,7 @@ void CustomRF95::handleInterrupt()
// DEBUG_MSG("Received packet from mesh src=0x%x,dest=0x%x,id=%d,len=%d rxGood=%d,rxBad=%d,freqErr=%d,snr=%d\n",
// srcaddr, destaddr, id, rxlen, rf95.rxGood(), rf95.rxBad(), freqerr, snr);
MeshPacket *mp = pool.allocZeroed();
MeshPacket *mp = packetPool.allocZeroed();
SubPacket *p = &mp->payload;
@@ -113,12 +110,12 @@ void CustomRF95::handleInterrupt()
}
if (!pb_decode_from_bytes(payload, payloadLen, SubPacket_fields, p)) {
pool.releaseFromISR(mp, &higherPriWoken);
packetPool.releaseFromISR(mp, &higherPriWoken);
} else {
// parsing was successful, queue for our recipient
mp->has_payload = true;
assert(rxDest.enqueueFromISR(mp, &higherPriWoken)); // NOWAIT - fixme, if queue is full, delete older messages
deliverToReceiverISR(mp, &higherPriWoken);
}
clearRxBuf(); // This message accepted and cleared

View File

@@ -19,7 +19,7 @@ class CustomRF95 : public RH_RF95, public RadioInterface
/** pool is the pool we will alloc our rx packets from
* rxDest is where we will send any rx packets, it becomes receivers responsibility to return packet to the pool
*/
CustomRF95(MemoryPool<MeshPacket> &pool, PointerQueue<MeshPacket> &rxDest);
CustomRF95();
/**
* Return true if we think the board can go to sleep (i.e. our tx queue is empty, we are not sending or receiving)

View File

@@ -31,6 +31,7 @@ template <class T> class MemoryPool
~MemoryPool() { delete[] buf; }
/// Return a queable object which has been prefilled with zeros. Panic if no buffer is available
/// Note: this method is safe to call from regular OR ISR code
T *allocZeroed()
{
T *p = allocZeroed(0);
@@ -40,7 +41,7 @@ template <class T> class MemoryPool
}
/// Return a queable object which has been prefilled with zeros - allow timeout to wait for available buffers (you probably
/// don't want this version)
/// don't want this version).
T *allocZeroed(TickType_t maxWait)
{
T *p = dead.dequeuePtr(maxWait);
@@ -65,7 +66,7 @@ template <class T> class MemoryPool
{
assert(dead.enqueue(p, 0));
assert(p >= buf &&
(size_t) (p - buf) <
(size_t)(p - buf) <
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
}
@@ -74,7 +75,7 @@ template <class T> class MemoryPool
{
assert(dead.enqueueFromISR(p, higherPriWoken));
assert(p >= buf &&
(size_t) (p - buf) <
(size_t)(p - buf) <
maxElements); // sanity check to make sure a programmer didn't free something that didn't come from this pool
}
};

View File

@@ -2,14 +2,21 @@
#include "NodeDB.h"
#include "assert.h"
#include "configuration.h"
#include <assert.h>
#include <pb_decode.h>
#include <pb_encode.h>
RadioInterface::RadioInterface(MemoryPool<MeshPacket> &_pool, PointerQueue<MeshPacket> &_rxDest) : pool(_pool), rxDest(_rxDest) {}
RadioInterface::RadioInterface() {}
ErrorCode SimRadio::send(MeshPacket *p)
{
DEBUG_MSG("SimRadio.send\n");
pool.release(p);
packetPool.release(p);
return ERRNO_OK;
}
void RadioInterface::deliverToReceiverISR(MeshPacket *p, BaseType_t *higherPriWoken)
{
assert(rxDest);
assert(rxDest->enqueueFromISR(p, higherPriWoken)); // NOWAIT - fixme, if queue is full, delete older messages
}

View File

@@ -16,43 +16,38 @@
class RadioInterface
{
friend class MeshRadio; // for debugging we let that class touch pool
PointerQueue<MeshPacket> *rxDest = NULL;
protected:
MemoryPool<MeshPacket> &pool;
PointerQueue<MeshPacket> &rxDest;
MeshPacket *sendingPacket = NULL; // The packet we are currently sending
/**
* Enqueue a received packet for the registered receiver
*/
void deliverToReceiverISR(MeshPacket *p, BaseType_t *higherPriWoken);
public:
/** pool is the pool we will alloc our rx packets from
* rxDest is where we will send any rx packets, it becomes receivers responsibility to return packet to the pool
*/
RadioInterface(MemoryPool<MeshPacket> &pool, PointerQueue<MeshPacket> &rxDest);
RadioInterface();
/**
* Return true if we think the board can go to sleep (i.e. our tx queue is empty, we are not sending or receiving)
*
* This method must be used before putting the CPU into deep or light sleep.
* Set where to deliver received packets. This method should only be used by the Router class
*/
virtual bool canSleep() { return true; }
void setReceiver(PointerQueue<MeshPacket> *_rxDest) { rxDest = _rxDest; }
/// Prepare hardware for sleep. Call this _only_ for deep sleep, not needed for light sleep.
/// return true for success
virtual bool sleep() { return true; }
/// Send a packet (possibly by enquing in a private fifo). This routine will
/// later free() the packet to pool. This routine is not allowed to stall because it is called from
/// bluetooth comms code. If the txmit queue is empty it might return an error
/**
* Send a packet (possibly by enquing in a private fifo). This routine will
* later free() the packet to pool. This routine is not allowed to stall.
* If the txmit queue is full it might return an error
*/
virtual ErrorCode send(MeshPacket *p) = 0;
};
class SimRadio : public RadioInterface
{
public:
/** pool is the pool we will alloc our rx packets from
* rxDest is where we will send any rx packets, it becomes receivers responsibility to return packet to the pool
*/
SimRadio(MemoryPool<MeshPacket> &_pool, PointerQueue<MeshPacket> &_rxDest) : RadioInterface(_pool, _rxDest) {}
virtual ErrorCode send(MeshPacket *p);
// methods from radiohead

72
src/rf95/Router.cpp Normal file
View File

@@ -0,0 +1,72 @@
#include "Router.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
/**
* Router todo
*
* Implement basic interface and use it elsewhere in app
* Add naive flooding mixin (& drop duplicate rx broadcasts), add tools for sending broadcasts with incrementing sequence #s
* Add an optional adjacent node only 'send with ack' mixin. If we timeout waiting for the ack, call handleAckTimeout(packet)
* Add DSR mixin
*
**/
#define MAX_RX_FROMRADIO \
4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big
// I think this is right, one packet for each of the three fifos + one packet being currently assembled for TX or RX
#define MAX_PACKETS \
(MAX_RX_TOPHONE + MAX_RX_FROMRADIO + MAX_TX_QUEUE + \
2) // max number of packets which can be in flight (either queued from reception or queued for sending)
MemoryPool<MeshPacket> packetPool(MAX_PACKETS);
Router router;
/**
* Constructor
*
* Currently we only allow one interface, that may change in the future
*/
Router::Router() : fromRadioQueue(MAX_RX_FROMRADIO) {}
/**
* do idle processing
* Mostly looking in our incoming rxPacket queue and calling handleReceived.
*/
void Router::loop()
{
MeshPacket *mp;
while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) {
handleReceived(mp);
}
}
/**
* Send a packet on a suitable interface. This routine will
* later free() the packet to pool. This routine is not allowed to stall.
* If the txmit queue is full it might return an error
*/
ErrorCode Router::send(MeshPacket *p)
{
assert(iface);
return iface->send(p);
}
#include "GPS.h"
/**
* Handle any packet that is received by an interface on this node.
* Note: some packets may merely being passed through this node and will be forwarded elsewhere.
*/
void Router::handleReceived(MeshPacket *p)
{
// FIXME, this class shouldn't EVER need to know about the GPS, move getValidTime() into a non gps dependent function
// Also, we should set the time from the ISR and it should have msec level resolution
p->rx_time = gps.getValidTime(); // store the arrival timestamp for the phone
DEBUG_MSG("Notifying observers of received packet\n");
notifyPacketReceived.notifyObservers(p);
packetPool.release(p);
}

70
src/rf95/Router.h Normal file
View File

@@ -0,0 +1,70 @@
#pragma once
#include "MemoryPool.h"
#include "MeshTypes.h"
#include "Observer.h"
#include "PointerQueue.h"
#include "RadioInterface.h"
#include "mesh.pb.h"
#include <RH_RF95.h>
/**
* A mesh aware router that supports multiple interfaces.
*/
class Router
{
private:
RadioInterface *iface;
/// Packets which have just arrived from the radio, ready to be processed by this service and possibly
/// forwarded to the phone.
PointerQueue<MeshPacket> fromRadioQueue;
public:
/// Local services that want to see _every_ packet this node receives can observe this.
/// Observers should always return 0 and _copy_ any packets they want to keep for use later (this packet will be getting
/// freed)
Observable<const MeshPacket *> notifyPacketReceived;
/**
* Constructor
*
*/
Router();
/**
* Currently we only allow one interface, that may change in the future
*/
void addInterface(RadioInterface *_iface)
{
iface = _iface;
iface->setReceiver(&fromRadioQueue);
}
/**
* do idle processing
* Mostly looking in our incoming rxPacket queue and calling handleReceived.
*/
void loop();
/**
* Send a packet on a suitable interface. This routine will
* later free() the packet to pool. This routine is not allowed to stall.
* If the txmit queue is full it might return an error
*/
virtual ErrorCode send(MeshPacket *p);
private:
/**
* Called from loop()
* Handle any packet that is received by an interface on this node.
* Note: some packets may merely being passed through this node and will be forwarded elsewhere.
*
* Note: this method will free the provided packet
*/
void handleReceived(MeshPacket *p);
};
extern Router router;