V148

Fork of RadioHead-148 by David Rimer

Revision:
0:ab4e012489ef
diff -r 000000000000 -r ab4e012489ef RHRouter.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/RHRouter.cpp	Thu Oct 15 01:27:00 2015 +0000
@@ -0,0 +1,306 @@
+// RHRouter.cpp
+//
+// Define addressed datagram
+// 
+// Part of the Arduino RH library for operating with HopeRF RH compatible transceivers 
+// (see http://www.hoperf.com)
+// RHDatagram will be received only by the addressed node or all nodes within range if the 
+// to address is RH_BROADCAST_ADDRESS
+//
+// Author: Mike McCauley (mikem@airspayce.com)
+// Copyright (C) 2011 Mike McCauley
+// $Id: RHRouter.cpp,v 1.7 2015/08/13 02:45:47 mikem Exp $
+
+#include <RHRouter.h>
+
+RHRouter::RoutedMessage RHRouter::_tmpMessage;
+
+////////////////////////////////////////////////////////////////////
+// Constructors
+RHRouter::RHRouter(RHGenericDriver& driver, uint8_t thisAddress) 
+    : RHReliableDatagram(driver, thisAddress)
+{
+    _max_hops = RH_DEFAULT_MAX_HOPS;
+    clearRoutingTable();
+}
+
+////////////////////////////////////////////////////////////////////
+// Public methods
+bool RHRouter::init()
+{
+    bool ret = RHReliableDatagram::init();
+    if (ret)
+	_max_hops = RH_DEFAULT_MAX_HOPS;
+    return ret;
+}
+
+////////////////////////////////////////////////////////////////////
+void RHRouter::setMaxHops(uint8_t max_hops)
+{
+    _max_hops = max_hops;
+}
+
+////////////////////////////////////////////////////////////////////
+void RHRouter::addRouteTo(uint8_t dest, uint8_t next_hop, uint8_t state)
+{
+    uint8_t i;
+
+    // First look for an existing entry we can update
+    for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+    {
+	if (_routes[i].dest == dest)
+	{
+	    _routes[i].dest = dest;
+	    _routes[i].next_hop = next_hop;
+	    _routes[i].state = state;
+	    return;
+	}
+    }
+
+    // Look for an invalid entry we can use
+    for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+    {
+	if (_routes[i].state == Invalid)
+	{
+	    _routes[i].dest = dest;
+	    _routes[i].next_hop = next_hop;
+	    _routes[i].state = state;
+	    return;
+	}
+    }
+
+    // Need to make room for a new one
+    retireOldestRoute();
+    // Should be an invalid slot now
+    for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+    {
+	if (_routes[i].state == Invalid)
+	{
+	    _routes[i].dest = dest;
+	    _routes[i].next_hop = next_hop;
+	    _routes[i].state = state;
+	}
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+RHRouter::RoutingTableEntry* RHRouter::getRouteTo(uint8_t dest)
+{
+    uint8_t i;
+    for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+	if (_routes[i].dest == dest && _routes[i].state != Invalid)
+	    return &_routes[i];
+    return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+void RHRouter::deleteRoute(uint8_t index)
+{
+    // Delete a route by copying following routes on top of it
+    memcpy(&_routes[index], &_routes[index+1], 
+	   sizeof(RoutingTableEntry) * (RH_ROUTING_TABLE_SIZE - index - 1));
+    _routes[RH_ROUTING_TABLE_SIZE - 1].state = Invalid;
+}
+
+////////////////////////////////////////////////////////////////////
+void RHRouter::printRoutingTable()
+{
+#ifdef RH_HAVE_SERIAL
+    uint8_t i;
+    for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+    {
+	Serial.print(i, DEC);
+	Serial.print(" Dest: ");
+	Serial.print(_routes[i].dest, DEC);
+	Serial.print(" Next Hop: ");
+	Serial.print(_routes[i].next_hop, DEC);
+	Serial.print(" State: ");
+	Serial.println(_routes[i].state, DEC);
+    }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHRouter::deleteRouteTo(uint8_t dest)
+{
+    uint8_t i;
+    for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+    {
+	if (_routes[i].dest == dest)
+	{
+	    deleteRoute(i);
+	    return true;
+	}
+    }
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////
+void RHRouter::retireOldestRoute()
+{
+    // We just obliterate the first in the table and clear the last
+    deleteRoute(0);
+}
+
+////////////////////////////////////////////////////////////////////
+void RHRouter::clearRoutingTable()
+{
+    uint8_t i;
+    for (i = 0; i < RH_ROUTING_TABLE_SIZE; i++)
+	_routes[i].state = Invalid;
+}
+
+
+uint8_t RHRouter::sendtoWait(uint8_t* buf, uint8_t len, uint8_t dest, uint8_t flags)
+{
+    return sendtoFromSourceWait(buf, len, dest, _thisAddress, flags);
+}
+
+////////////////////////////////////////////////////////////////////
+// Waits for delivery to the next hop (but not for delivery to the final destination)
+uint8_t RHRouter::sendtoFromSourceWait(uint8_t* buf, uint8_t len, uint8_t dest, uint8_t source, uint8_t flags)
+{
+    if (((uint16_t)len + sizeof(RoutedMessageHeader)) > _driver.maxMessageLength())
+	return RH_ROUTER_ERROR_INVALID_LENGTH;
+
+    // Construct a RH RouterMessage message
+    _tmpMessage.header.source = source;
+    _tmpMessage.header.dest = dest;
+    _tmpMessage.header.hops = 0;
+    _tmpMessage.header.id = _lastE2ESequenceNumber++;
+    _tmpMessage.header.flags = flags;
+    memcpy(_tmpMessage.data, buf, len);
+
+    return route(&_tmpMessage, sizeof(RoutedMessageHeader)+len);
+}
+
+////////////////////////////////////////////////////////////////////
+uint8_t RHRouter::route(RoutedMessage* message, uint8_t messageLen)
+{
+    // Reliably deliver it if possible. See if we have a route:
+    uint8_t next_hop = RH_BROADCAST_ADDRESS;
+    if (message->header.dest != RH_BROADCAST_ADDRESS)
+    {
+	RoutingTableEntry* route = getRouteTo(message->header.dest);
+	if (!route)
+	    return RH_ROUTER_ERROR_NO_ROUTE;
+	next_hop = route->next_hop;
+    }
+
+    if (!RHReliableDatagram::sendtoWait((uint8_t*)message, messageLen, next_hop))
+	return RH_ROUTER_ERROR_UNABLE_TO_DELIVER;
+
+    return RH_ROUTER_ERROR_NONE;
+}
+
+////////////////////////////////////////////////////////////////////
+// Subclasses may want to override this to peek at messages going past
+void RHRouter::peekAtMessage(RoutedMessage* message, uint8_t messageLen)
+{
+    // Default does nothing
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHRouter::recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* source, uint8_t* dest, uint8_t* id, uint8_t* flags)
+{  
+    uint8_t tmpMessageLen = sizeof(_tmpMessage);
+    uint8_t _from;
+    uint8_t _to;
+    uint8_t _id;
+    uint8_t _flags;
+    if (RHReliableDatagram::recvfromAck((uint8_t*)&_tmpMessage, &tmpMessageLen, &_from, &_to, &_id, &_flags))
+    {
+	// Here we simulate networks with limited visibility between nodes
+	// so we can test routing
+#ifdef RH_TEST_NETWORK
+	if (
+#if RH_TEST_NETWORK==1
+	    // This network looks like 1-2-3-4
+	       (_thisAddress == 1 && _from == 2)
+	    || (_thisAddress == 2 && (_from == 1 || _from == 3))
+	    || (_thisAddress == 3 && (_from == 2 || _from == 4))
+	    || (_thisAddress == 4 && _from == 3)
+	    
+#elif RH_TEST_NETWORK==2
+	       // This network looks like 1-2-4
+	       //                         | | |
+	       //                         --3--
+	       (_thisAddress == 1 && (_from == 2 || _from == 3))
+	    ||  _thisAddress == 2
+	    ||  _thisAddress == 3
+	    || (_thisAddress == 4 && (_from == 2 || _from == 3))
+
+#elif RH_TEST_NETWORK==3
+	       // This network looks like 1-2-4
+	       //                         |   |
+	       //                         --3--
+	       (_thisAddress == 1 && (_from == 2 || _from == 3))
+	    || (_thisAddress == 2 && (_from == 1 || _from == 4))
+	    || (_thisAddress == 3 && (_from == 1 || _from == 4))
+	    || (_thisAddress == 4 && (_from == 2 || _from == 3))
+
+#elif RH_TEST_NETWORK==4
+	       // This network looks like 1-2-3
+	       //                           |
+	       //                           4
+	       (_thisAddress == 1 && _from == 2)
+	    ||  _thisAddress == 2
+	    || (_thisAddress == 3 && _from == 2)
+	    || (_thisAddress == 4 && _from == 2)
+
+#endif
+)
+	{
+	    // OK
+	}
+	else
+	{
+	    return false; // Pretend we got nothing
+	}
+#endif
+
+	peekAtMessage(&_tmpMessage, tmpMessageLen);
+	// See if its for us or has to be routed
+	if (_tmpMessage.header.dest == _thisAddress || _tmpMessage.header.dest == RH_BROADCAST_ADDRESS)
+	{
+	    // Deliver it here
+	    if (source) *source  = _tmpMessage.header.source;
+	    if (dest)   *dest    = _tmpMessage.header.dest;
+	    if (id)     *id      = _tmpMessage.header.id;
+	    if (flags)  *flags   = _tmpMessage.header.flags;
+	    uint8_t msgLen = tmpMessageLen - sizeof(RoutedMessageHeader);
+	    if (*len > msgLen)
+		*len = msgLen;
+	    memcpy(buf, _tmpMessage.data, *len);
+	    return true; // Its for you!
+	}
+	else if (   _tmpMessage.header.dest != RH_BROADCAST_ADDRESS
+		 && _tmpMessage.header.hops++ < _max_hops)
+	{
+	    // Maybe it has to be routed to the next hop
+	    // REVISIT: if it fails due to no route or unable to deliver to the next hop, 
+	    // tell the originator. BUT HOW?
+	    route(&_tmpMessage, tmpMessageLen);
+	}
+	// Discard it and maybe wait for another
+    }
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////
+bool RHRouter::recvfromAckTimeout(uint8_t* buf, uint8_t* len, uint16_t timeout, uint8_t* source, uint8_t* dest, uint8_t* id, uint8_t* flags)
+{  
+    unsigned long starttime = millis();
+    int32_t timeLeft;
+    while ((timeLeft = timeout - (millis() - starttime)) > 0)
+    {
+	if (waitAvailableTimeout(timeLeft))
+	{
+	    if (recvfromAck(buf, len, source, dest, id, flags))
+		return true;
+	}
+	YIELD;
+    }
+    return false;
+}
+