-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathOLCB_Datagram_Handler.cpp
265 lines (245 loc) · 10.2 KB
/
OLCB_Datagram_Handler.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
#include "OLCB_Datagram_Handler.h"
bool OLCB_Datagram_Handler::sendDatagram(OLCB_Datagram *datagram)
{
//someone wants to send a datagram! Check to see if our buffer is free, or two seconds have passed
if(!_txDatagramBufferFree)
{
//Serial.println("No free datagram tx buffer!");
return false;
}
//Serial.println("settting dg buffer to occupied");
_txDatagramBufferFree = false;
//Copy the datagram to free the original up for other uses.
memcpy(_txDatagramBuffer,datagram,sizeof(OLCB_Datagram));
_txDatagramBuffer->source.copy(NID); //set the source to be me!
_txFlag = true;
_loc = 0;
_sentTime = millis(); //log the final transmission time for response timeout checking
//Serial.println(_txDatagramBuffer->length, DEC);
//for(uint8_t i = 0; i < _txDatagramBuffer->length; ++i)
//Serial.println(_txDatagramBuffer->data[i], HEX);
return true;
}
bool OLCB_Datagram_Handler::isDatagramSent(void)
{
return(_txDatagramBufferFree);
}
bool OLCB_Datagram_Handler::handleMessage(OLCB_Buffer *frame)
{
if(!isPermitted())
{
return false;
}
//Serial.println("datagram handle message");
//First, make sure we are dealing with a datagram.
if(!frame->isDatagram())
{
//Serial.println("not a datagram");
//see if it is an addressed non-datagram to us, and if it is an expected ACK or NAK
if(frame->isDatagramAck())
{
OLCB_NodeID n;
frame->getDestNID(&n);
if(NID != 0 && n == *NID) //Yay! datagram sent OK
{
//Serial.println("Got an ACK, freeing TX buffer");
datagramResult(true,0);
_txDatagramBufferFree = true;
return true;
}
}
else if(frame->isDatagramNak())
{
OLCB_NodeID n;
frame->getDestNID(&n);
if(NID != 0 && n == *NID) //Yay! datagram sent OK, but NAK'd
{
//Serial.println("It's a NAK!");
uint16_t errorCode = frame->getDatagramNakErrorCode();
if(errorCode & DATAGRAM_REJECTED_RESEND_MASK)
{
//Serial.println("resending datagram");
//send again!
_txFlag = true;
_loc = 0;
}
else
{
//Serial.println("give up");
_txFlag = false;
_loc = 0;
_txDatagramBufferFree = true;
}
datagramResult(false,errorCode);
return true;
}
}
//Serial.println("Wasn't anything we care about, apparently");
return false; //whatever it was, it wasn't for us!
}
//now, check to see if this datagram is addressed to us. If not, ignore it. (Also, reject if we don't have a NID set!)
//Note: Sometimes we want to process a datagram regardless of the address. This is the case for what I call "default handlers". If we are marked as such, skip this check.
OLCB_NodeID n;
frame->getDestNID(&n);
//Serial.print("got datagram fragment for ");
//n.print();
//NID->print();
if((NID == 0) || (n != *NID))
{
//Serial.println("Someone else");
return false;
}
//Serial.println("us");
//we've established it's a datagram, and that it is addressed to us, now what? One of several possibilities.
// - we are ready to receive a new datagram (and this is the first frame, otherwise error). prep buffer, start recording.
// - We are currently receiving a datagram (and this is from the same source). continue filling buffer.
// - We are currently receiving a datagram (and this is not from the same source): send "busy, send again" response.
// - This is the last frame, and we like it. Send ACK.
// - This is the last frame, and it's not something we can handle. send NAK.
// check the source for this datagram against our receive buffer
frame->getSourceNID(&n);
uint8_t *frame_data = frame->getData();;
uint8_t frame_length = frame->getLength();
if( (frame->isFirstDatagram() && _rxDatagramBufferFree) || (frame->isOnlyDatagram() && _rxDatagramBufferFree) ||
( (frame->isMiddleDatagram() || frame->isLastDatagram()) && (n == _rxDatagramBuffer->source) && !_rxDatagramBufferFree) ) //first frame and free buffer, or continuing frame and not free buffer
{
//begin filling!
//is this the first frame? Need to initialize the datagram buffer
if(frame->isFirstDatagram() || frame->isOnlyDatagram())
{
//Serial.println("Is first fragment!");
_rxDatagramBufferFree = false;
//TODO FOR SOME REASON THE FOLLOWING LINE (or variants on it) CORRUPT THE FIRST TWO BYTES OF
//_rxDatagramBuffer->source to be 0xc6. WHY GOD WHY!?
// memcpy(_rxDatagramBuffer->source, &n, sizeof(OLCB_NodeID));
// _rxDatagramBuffer->source->alias = n.alias;
// _rxDatagramBuffer->source->val[0] = n.val[0];
// _rxDatagramBuffer->source->val[0] = n.val[1];
// _rxDatagramBuffer->source->val[0] = n.val[2];
// _rxDatagramBuffer->source->val[0] = n.val[3];
// _rxDatagramBuffer->source->val[0] = n.val[4];
// _rxDatagramBuffer->source->val[0] = n.val[5];
// n.print();
// _rxDatagramBuffer->source->print();
_rxDatagramBuffer->length = 0;
frame->getSourceNID(&(_rxDatagramBuffer->source)); //TODO NOTICE that this only gets us the alias, and not the entire NID!!!
//_rxDatagramBuffer->source.print();
//_rxDatagramBuffer->destination.print();
// n.print(); //TODO this one and the one above are DIFFERENT but should not be!!
}
//copy data from frame into _rxDatagramBuffer->data
for (uint8_t i=0; i< frame_length; i++)
{
_rxDatagramBuffer->data[i+_rxDatagramBuffer->length] = *(frame_data+i);
}
_rxDatagramBuffer->length += frame_length;
if(frame->isLastDatagram() || frame->isOnlyDatagram() ) //Last frame? Need to ACK or NAK!
{
//Serial.println("Is last fragment!");
uint16_t errorcode = processDatagram();
//Serial.print("from processDatagram, received result code ");
//Serial.println(errorcode, HEX);
if(errorcode == DATAGRAM_ERROR_OK)
{
//TODO we should probably move this to the update loop! Don't want to block here, I don't think.
//Serial.println("ACKING!");
if(!_link->ackDatagram(NID,&(_rxDatagramBuffer->source))) //try to ack, and if fail, queue an ack for later
sendAck(&(_rxDatagramBuffer->source));
}
else
{
//Serial.print("NAKING! Reason:");
//Serial.println(errorcode, HEX);
if(!_link->nakDatagram(NID,&(_rxDatagramBuffer->source), errorcode)) //try to send, if if fail, queue for later
sendNak(&(_rxDatagramBuffer->source), errorcode);
}
//Serial.println("freeing rx buffer");
_rxDatagramBufferFree = true; //in either case, the buffer is now free
}
return true;
}
else if( (frame->isLastDatagram() || frame->isOnlyDatagram()) && _rxDatagramBufferFree)
//we can't currently accept this frame because we didn't see the first datagram fragment
{
//Serial.println("NAKing datagram, missed first frame");
if(!_link->nakDatagram(NID, &n, DATAGRAM_REJECTED_OUT_OF_ORDER))
sendNak(&n, DATAGRAM_REJECTED_OUT_OF_ORDER);
}
else //got here because our buffer was full.
{
//Serial.println("NAKing datagram, buffer full");
if(!_link->nakDatagram(NID, &n, DATAGRAM_REJECTED_BUFFER_FULL))
sendNak(&n, DATAGRAM_REJECTED_BUFFER_FULL);
}
return true; //returning ture here prevents emitting spurious OIR
}
void OLCB_Datagram_Handler::sendNak(OLCB_NodeID *dest, uint16_t reason)
{
memcpy(&_ackDest, dest, sizeof(OLCB_NodeID));
_ackReason = reason;
}
void OLCB_Datagram_Handler::sendAck(OLCB_NodeID *dest)
{
memcpy(&_ackDest, dest, sizeof(OLCB_NodeID));
_ackReason = DATAGRAM_ERROR_OK;
}
void OLCB_Datagram_Handler::update(void)
{
if(!isPermitted())
{
return;
}
else if(_ackReason != 0xFFFF) //we have an ack or nak to send
{
if(_ackReason == DATAGRAM_ERROR_OK)
{
while(!_link->ackDatagram(NID, &_ackDest));
}
else
{
while(!_link->nakDatagram(NID, &_ackDest, _ackReason));
}
_ackReason = 0xFFFF;
}
else if(_txFlag) //We're in the middle of a transmission
{
//Serial.println("in dghandler update");
//Serial.println("In middle of transmission");
uint8_t sent = _link->sendDatagramFragment(_txDatagramBuffer, _loc);
//TODO We should be checking for a 0 result here, as that might indicate trouble
if(!sent && (millis()-_sentTime) > DATAGRAM_ACK_TIMEOUT) //if nothing got transmitted, let's not get hung up. might not happen. Let it go for a couple of seconds, then give up
{
//give up
//Serial.println("DG transmission timeout reached, freeing buffer");
_txDatagramBufferFree = true;
_txFlag = false;
datagramResult(false,DATAGRAM_ERROR_ABORTED);
}
else if(_loc+sent == _txDatagramBuffer->length) //we're done with this datagram
{
_txFlag = false; //done transmitting
_loc = 0; //reset the location
_sentTime = millis(); //log the final transmission time for response timeout checking
}
else
{
_loc += sent;
}
}
//If we're not transmitting, but awaiting a response, make sure that the response hasn't timed out!
else if(!_txDatagramBufferFree && ((millis()-_sentTime) > DATAGRAM_ACK_TIMEOUT) )
{
//Serial.println("ACK timeout reached, freeing buffer");
_txDatagramBufferFree = true;
_txFlag = false;
datagramResult(false,DATAGRAM_ERROR_ACK_TIMEOUT);
}
}
void OLCB_Datagram_Handler::clearBuffer(OLCB_NodeID *nodeid)
{
if((!_rxDatagramBufferFree) && (*nodeid == _rxDatagramBuffer->source))
{
//Serial.println("DG clearing buffer");
_rxDatagramBufferFree = true; //kill transmission
}
}