|
- // 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;
- }
|