-
Notifications
You must be signed in to change notification settings - Fork 1
/
RHRouter.cpp
318 lines (285 loc) · 9.28 KB
/
RHRouter.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// 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.10 2020/08/04 09:02:14 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;
_isa_router = true;
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::setIsaRouter(bool isa_router)
{
_isa_router = isa_router;
}
////////////////////////////////////////////////////////////////////
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
(void)message; // Not used
(void)messageLen; // Not used
}
////////////////////////////////////////////////////////////////////
bool RHRouter::recvfromAck(uint8_t* buf, uint8_t* len, uint8_t* source, uint8_t* dest, uint8_t* id, uint8_t* flags, uint8_t* hops)
{
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;
if (hops) *hops = _tmpMessage.header.hops;
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?
// If we are forwarding packets, do so. Otherwise, drop.
if (_isa_router)
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, uint8_t* hops)
{
unsigned long starttime = millis();
int32_t timeLeft;
while ((timeLeft = timeout - (millis() - starttime)) > 0)
{
if (waitAvailableTimeout(timeLeft))
{
if (recvfromAck(buf, len, source, dest, id, flags, hops))
return true;
}
YIELD;
}
return false;
}