/
OLCB_Datagram_Handler.cpp
227 lines (210 loc) · 9.06 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
#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
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->getDestinationNID(&n);
if(NID != 0 && n == *NID) //Yay! datagram sent OK
{
//Serial.println("It's an ACK!");
datagramResult(true,0);
_txDatagramBufferFree = true;
return true;
}
}
else if(frame->isDatagramNak())
{
OLCB_NodeID n;
frame->getDestinationNID(&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->getDestinationNID(&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) ||
( (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())
{
//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()) //Last frame? Need to ACK or NAK!
{
//Serial.println("Is last fragment!");
if(processDatagram())
{
//TODO we should probably move this to the update loop! Don't want to block here, I don't think.
//Serial.println("ACKING!");
while(!_link->ackDatagram(NID,&(_rxDatagramBuffer->source)));
}
else
{
//Serial.println("NAKING!");
while(!_link->nakDatagram(NID,&(_rxDatagramBuffer->source), DATAGRAM_REJECTED_DATAGRAM_TYPE_NOT_ACCEPTED));
}
_rxDatagramBufferFree = true; //in either case, the buffer is now free
}
return true;
}
else if(frame->isLastDatagram()) //we can't currently accept this frame, either because the buffer is not free, or we didn't see the first datagram fragment
{
if(_rxDatagramBufferFree) //we missed the first frame somehow; perhaps it came while we were processing an earlier datagram
{
//Serial.println("NAKing datagram, missed first frame");
while(!_link->nakDatagram(NID, &n, DATAGRAM_REJECTED_OUT_OF_ORDER));
}
else
{
//Serial.println("NAKing datagram, buffer full");
while(!_link->nakDatagram(NID, &n, DATAGRAM_REJECTED_BUFFER_FULL));
}
return true;
}
return true; //if we reach here, we have a middle DG fragment addressed to us; just don't take any kind of action whatsoever, including sending OptionalInteractionRejected (which is what would happen if we returned false
}
void OLCB_Datagram_Handler::update(void)
{
if(isPermitted())
{
if(_txFlag) //We're in the middle of a 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
}
}