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

@@ -24,8 +24,7 @@ separated by 2.16 MHz with respect to the adjacent channels. Channel zero starts
/// Sometimes while debugging it is useful to set this false, to disable rf95 accesses
bool useHardware = true;
MeshRadio::MeshRadio(MemoryPool<MeshPacket> &_pool, PointerQueue<MeshPacket> &_rxDest)
: radioIf(_pool, _rxDest), sendPacketObserver(this, &MeshRadio::send) // , manager(radioIf)
MeshRadio::MeshRadio() : sendPacketObserver(this, &MeshRadio::send) // , manager(radioIf)
{
myNodeInfo.num_channels = NUM_CHANNELS;
@@ -150,7 +149,7 @@ void MeshRadio::loop()
DEBUG_MSG("ERROR! Bug! Tx packet took too long to send, forcing radio into rx mode\n");
radioIf.setModeRx();
if (radioIf.sendingPacket) { // There was probably a packet we were trying to send, free it
radioIf.pool.release(radioIf.sendingPacket);
packetPool.release(radioIf.sendingPacket);
radioIf.sendingPacket = NULL;
}
recordCriticalError(ErrTxWatchdog);

View File

@@ -76,7 +76,7 @@ class MeshRadio
/** 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
*/
MeshRadio(MemoryPool<MeshPacket> &pool, PointerQueue<MeshPacket> &rxDest);
MeshRadio();
bool init();

View File

@@ -44,15 +44,9 @@ FIXME in the initial proof of concept we just skip the entire want/deny flow and
MeshService service;
// 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)
#include "Router.h"
#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
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE), packetPool(MAX_PACKETS), fromRadioQueue(MAX_RX_FROMRADIO)
MeshService::MeshService() : toPhoneQueue(MAX_RX_TOPHONE)
{
// assert(MAX_RX_TOPHONE == 32); // FIXME, delete this, just checking my clever macro
}
@@ -62,6 +56,7 @@ void MeshService::init()
nodeDB.init();
gpsObserver.observe(&gps);
packetReceivedObserver.observe(&router.notifyPacketReceived);
// No need to call this here, our periodic task will fire quite soon
// sendOwnerPeriod();
@@ -81,7 +76,7 @@ void MeshService::sendOurOwner(NodeNum dest, bool wantReplies)
}
/// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all
MeshPacket *MeshService::handleFromRadioUser(MeshPacket *mp)
const MeshPacket *MeshService::handleFromRadioUser(const MeshPacket *mp)
{
bool wasBroadcast = mp->to == NODENUM_BROADCAST;
bool isCollision = mp->from == myNodeInfo.my_node_num;
@@ -93,7 +88,6 @@ MeshPacket *MeshService::handleFromRadioUser(MeshPacket *mp)
if (weWin) {
DEBUG_MSG("NOTE! Received a nodenum collision and we are vetoing\n");
releaseToPool(mp); // discard it
mp = NULL;
sendOurOwner(); // send our owner as a _broadcast_ because that other guy is mistakenly using our nodenum
@@ -121,7 +115,7 @@ MeshPacket *MeshService::handleFromRadioUser(MeshPacket *mp)
return mp;
}
void MeshService::handleIncomingPosition(MeshPacket *mp)
void MeshService::handleIncomingPosition(const MeshPacket *mp)
{
if (mp->has_payload && mp->payload.has_position) {
DEBUG_MSG("handled incoming position time=%u\n", mp->payload.position.time);
@@ -140,12 +134,10 @@ void MeshService::handleIncomingPosition(MeshPacket *mp)
}
}
void MeshService::handleFromRadio(MeshPacket *mp)
int MeshService::handleFromRadio(const MeshPacket *mp)
{
powerFSM.trigger(EVENT_RECEIVED_PACKET); // Possibly keep the node from sleeping
mp->rx_time = gps.getValidTime(); // store the arrival timestamp for the phone
// 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)
if (!gps.isConnected)
handleIncomingPosition(mp);
@@ -170,24 +162,17 @@ void MeshService::handleFromRadio(MeshPacket *mp)
if (d)
releaseToPool(d);
}
assert(toPhoneQueue.enqueue(mp, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages
MeshPacket *copied = packetPool.allocCopy(*mp);
assert(toPhoneQueue.enqueue(copied, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages
if (mp->payload.want_response)
sendNetworkPing(mp->from);
} else {
DEBUG_MSG("Not delivering vetoed User message\n");
}
}
void MeshService::handleFromRadio()
{
MeshPacket *mp;
uint32_t oldFromNum = fromNum;
while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) {
handleFromRadio(mp);
}
if (oldFromNum != fromNum) // We don't want to generate extra notifies for multiple new packets
fromNumChanged.notifyObservers(fromNum);
return 0;
}
uint32_t sendOwnerCb()
@@ -202,7 +187,10 @@ Periodic sendOwnerPeriod(sendOwnerCb);
/// Do idle processing (mostly processing messages which have been queued from the radio)
void MeshService::loop()
{
handleFromRadio();
if (oldFromNum != fromNum) { // We don't want to generate extra notifies for multiple new packets
fromNumChanged.notifyObservers(fromNum);
oldFromNum = true;
}
// occasionally send our owner info
sendOwnerPeriod.loop();
@@ -236,7 +224,9 @@ void MeshService::handleToRadio(std::string s)
bool loopback = false; // if true send any packet the phone sends back itself (for testing)
if (loopback) {
MeshPacket *mp = packetPool.allocCopy(r.variant.packet);
const MeshPacket *mp = &r.variant.packet;
// no need to copy anymore because handle from radio assumes it should _not_ delete
// packetPool.allocCopy(r.variant.packet);
handleFromRadio(mp);
// handleFromRadio will tell the phone a new packet arrived
}

View File

@@ -6,6 +6,7 @@
#include "MemoryPool.h"
#include "MeshRadio.h"
#include "MeshTypes.h"
#include "Observer.h"
#include "PointerQueue.h"
#include "mesh.pb.h"
@@ -17,6 +18,8 @@
class MeshService
{
CallbackObserver<MeshService, void *> gpsObserver = CallbackObserver<MeshService, void *>(this, &MeshService::onGPSChanged);
CallbackObserver<MeshService, const MeshPacket *> packetReceivedObserver =
CallbackObserver<MeshService, const MeshPacket *>(this, &MeshService::handleFromRadio);
/// received packets waiting for the phone to process them
/// FIXME, change to a DropOldestQueue and keep a count of the number of dropped packets to ensure
@@ -27,13 +30,10 @@ class MeshService
/// The current nonce for the newest packet which has been queued for the phone
uint32_t fromNum = 0;
/// Updated in loop() to detect when fromNum changes
uint32_t oldFromNum = 0;
public:
MemoryPool<MeshPacket> packetPool;
/// Packets which have just arrived from the radio, ready to be processed by this service and possibly
/// forwarded to the phone.
PointerQueue<MeshPacket> fromRadioQueue;
/// Called when some new packets have arrived from one of the radios
Observable<uint32_t> fromNumChanged;
@@ -90,18 +90,15 @@ class MeshService
/// returns 0 to allow futher processing
int onGPSChanged(void *arg);
/// handle all the packets that just arrived from the mesh radio
void handleFromRadio();
/// Handle a packet that just arrived from the radio. We will either eventually enqueue the message to the phone or return it
/// to the free pool
void handleFromRadio(MeshPacket *p);
/// Handle a packet that just arrived from the radio. This method does _not_ free the provided packet. If it needs
/// to keep the packet around it makes a copy
int handleFromRadio(const MeshPacket *p);
/// handle a user packet that just arrived on the radio, return NULL if we should not process this packet at all
MeshPacket *handleFromRadioUser(MeshPacket *mp);
const MeshPacket *handleFromRadioUser(const MeshPacket *mp);
/// look at inbound packets and if they contain a position with time, possibly set our clock
void handleIncomingPosition(MeshPacket *mp);
void handleIncomingPosition(const MeshPacket *mp);
};
extern MeshService service;

View File

@@ -2,6 +2,8 @@
// low level types
#include "MemoryPool.h"
#include "mesh.pb.h"
#include <Arduino.h>
typedef uint8_t NodeNum;
@@ -10,4 +12,7 @@ typedef uint8_t NodeNum;
#define ERRNO_OK 0
#define ERRNO_UNKNOWN 32 // pick something that doesn't conflict with RH_ROUTER_ERROR_UNABLE_TO_DELIVER
typedef int ErrorCode;
typedef int ErrorCode;
/// Alloc and free packets to our global, ISR safe pool
extern MemoryPool<MeshPacket> packetPool;

View File

@@ -193,7 +193,8 @@ void axp192Init()
#endif
}
void getMacAddr(uint8_t *dmac) {
void getMacAddr(uint8_t *dmac)
{
#ifndef NO_ESP32
assert(esp_efuse_mac_get_default(dmac) == ESP_OK);
#else
@@ -203,7 +204,7 @@ void getMacAddr(uint8_t *dmac) {
dmac[3] = 0xef;
dmac[4] = 0x01;
dmac[5] = 0x02; // FIXME, macaddr stuff needed for NRF52
#endif
#endif
}
const char *getDeviceName()
@@ -220,6 +221,8 @@ const char *getDeviceName()
static MeshRadio *radio = NULL;
#include "Router.h"
void setup()
{
// Debug
@@ -261,7 +264,7 @@ void setup()
// Don't init display if we don't have one or we are waking headless due to a timer event
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER)
ssd1306_found = false; // forget we even have the hardware
#endif
#endif
// Initialize the screen first so we can show the logo while we start up everything else.
if (ssd1306_found)
@@ -278,7 +281,8 @@ void setup()
#ifndef NO_ESP32
// MUST BE AFTER service.init, so we have our radio config settings (from nodedb init)
radio = new MeshRadio(service.packetPool, service.fromRadioQueue);
radio = new MeshRadio();
router.addInterface(&radio->radioIf);
#endif
if (radio && !radio->init())

View File

@@ -15,6 +15,9 @@ PB_BIND(Data, Data, 2)
PB_BIND(User, User, AUTO)
PB_BIND(RouteDiscovery, RouteDiscovery, AUTO)
PB_BIND(SubPacket, SubPacket, 2)

View File

@@ -32,6 +32,10 @@ typedef enum _ChannelSettings_ModemConfig {
} ChannelSettings_ModemConfig;
/* Struct definitions */
typedef struct _RouteDiscovery {
pb_callback_t route;
} RouteDiscovery;
typedef struct _ChannelSettings {
int32_t tx_power;
ChannelSettings_ModemConfig modem_config;
@@ -175,6 +179,7 @@ typedef struct _ToRadio {
#define Position_init_default {0, 0, 0, 0, 0}
#define Data_init_default {_Data_Type_MIN, {0, {0}}}
#define User_init_default {"", "", "", {0}}
#define RouteDiscovery_init_default {{{NULL}, NULL}}
#define SubPacket_init_default {false, Position_init_default, false, Data_init_default, false, User_init_default, 0}
#define MeshPacket_init_default {0, 0, false, SubPacket_init_default, 0, 0, 0}
#define ChannelSettings_init_default {0, _ChannelSettings_ModemConfig_MIN, {0}, ""}
@@ -188,6 +193,7 @@ typedef struct _ToRadio {
#define Position_init_zero {0, 0, 0, 0, 0}
#define Data_init_zero {_Data_Type_MIN, {0, {0}}}
#define User_init_zero {"", "", "", {0}}
#define RouteDiscovery_init_zero {{{NULL}, NULL}}
#define SubPacket_init_zero {false, Position_init_zero, false, Data_init_zero, false, User_init_zero, 0}
#define MeshPacket_init_zero {0, 0, false, SubPacket_init_zero, 0, 0, 0}
#define ChannelSettings_init_zero {0, _ChannelSettings_ModemConfig_MIN, {0}, ""}
@@ -200,6 +206,7 @@ typedef struct _ToRadio {
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
/* Field tags (for use in manual encoding/decoding) */
#define RouteDiscovery_route_tag 2
#define ChannelSettings_tx_power_tag 1
#define ChannelSettings_modem_config_tag 3
#define ChannelSettings_psk_tag 4
@@ -289,6 +296,11 @@ X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 4)
#define User_CALLBACK NULL
#define User_DEFAULT NULL
#define RouteDiscovery_FIELDLIST(X, a) \
X(a, CALLBACK, REPEATED, INT32, route, 2)
#define RouteDiscovery_CALLBACK pb_default_field_callback
#define RouteDiscovery_DEFAULT NULL
#define SubPacket_FIELDLIST(X, a) \
X(a, STATIC, OPTIONAL, MESSAGE, position, 1) \
X(a, STATIC, OPTIONAL, MESSAGE, data, 3) \
@@ -401,6 +413,7 @@ X(a, STATIC, ONEOF, MESSAGE, (variant,packet,variant.packet), 1)
extern const pb_msgdesc_t Position_msg;
extern const pb_msgdesc_t Data_msg;
extern const pb_msgdesc_t User_msg;
extern const pb_msgdesc_t RouteDiscovery_msg;
extern const pb_msgdesc_t SubPacket_msg;
extern const pb_msgdesc_t MeshPacket_msg;
extern const pb_msgdesc_t ChannelSettings_msg;
@@ -416,6 +429,7 @@ extern const pb_msgdesc_t ToRadio_msg;
#define Position_fields &Position_msg
#define Data_fields &Data_msg
#define User_fields &User_msg
#define RouteDiscovery_fields &RouteDiscovery_msg
#define SubPacket_fields &SubPacket_msg
#define MeshPacket_fields &MeshPacket_msg
#define ChannelSettings_fields &ChannelSettings_msg
@@ -431,6 +445,7 @@ extern const pb_msgdesc_t ToRadio_msg;
#define Position_size 46
#define Data_size 256
#define User_size 72
/* RouteDiscovery_size depends on runtime parameters */
#define SubPacket_size 383
#define MeshPacket_size 426
#define ChannelSettings_size 44

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;