-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTMessage.cpp
More file actions
380 lines (321 loc) · 11.5 KB
/
Copy pathTMessage.cpp
File metadata and controls
380 lines (321 loc) · 11.5 KB
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
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
#include <unistd.h>
#include <cstdlib>
#include <stdexcept>
#include <limits>
#include <thread>
#include <ctime>
#include "logging.h"
#include "TError.h"
#include "TMessage.h"
#include "DataItems/TDataItem.h"
#include "eNET-AIO16-16F.h"
#include "adc.h"
const std::vector<TMessageId> ValidMessageIDs{
// to server
'Q', // query/read
'C', // config/write
'M', // generic Message; bundle of Actions
// to client
'R', // Response, no errors
'X', // response, error, syntaX
'E', // response, Error, semantic (e.g., "out of range" in an argument ), or operational (e.g., hardware timeout)
'H', // Hello
};
#pragma region TMessage implementation
TCheckSum TMessage::calculateChecksum(TBytes Message)
{
LOG_IT;
TCheckSum checksum = 0;
for (__u8 aByte : Message)
checksum = static_cast<TCheckSum>(checksum + aByte);
return checksum;
}
bool TMessage::isValidMessageID(TMessageId MessageId)
{
bool result = false;
for (uint i = 0; i < sizeof(ValidMessageIDs) / sizeof(TMessageId); i++)
{
if (ValidMessageIDs[i] == MessageId)
{
result = true;
break;
}
}
return result;
}
// Checks the Payload for well-formedness
// returns 0 if the Payload is well-formed
// this means that all the Data Items are well formed and the total size matches the expectation
// "A Message has an optional Payload, which is a sequence of zero or more Data Items"
TError TMessage::validatePayload(TBytes Payload)
{
LOG_IT;
// WARN: Does not seem to work as expected when parsing multiple DataItems
TError result = ERR_SUCCESS;
if (Payload.size() == 1) // one-byte Payload is "the checksum byte".
return result;
if (Payload.size() == 0) // zero-length Payload size is a valid payload
return ERR_MSG_DATAITEM_TOO_SHORT;
if (Payload.size() < sizeof(DataItemIds)+sizeof(TDataItemLength))
return ERR_MSG_DATAITEM_TOO_SHORT;
TDataItemHeader *head = (TDataItemHeader *)Payload.data();
__u32 DataItemSize = static_cast<__u32>(sizeof(TDataItemHeader) + head->dataLength);
if (DataItemSize > Payload.size())
{
Error("--ERR: data item thinks it is longer than payload, disize: " + std::to_string(DataItemSize) + ", psize: " + std::to_string(Payload.size()));
result = ERR_MSG_PAYLOAD_DATAITEM_LEN_MISMATCH;
}
else
{
TBytes DataItem = slicing(Payload, 0, DataItemSize);
result = TDataItemBase::validateDataItem(DataItem);
if (result == 0) // if the Data Item is well-formed, check the next one
{
Payload.erase(Payload.cbegin(), Payload.cbegin() + DataItemSize);
if (Payload.size() > 0)
result = validatePayload(Payload);
}
}
return result;
}
// Checks the Message for well-formedness
// returns 0 if Message is well-formed
TError TMessage::validateMessage(TBytes buf) // "NAK()" is shorthand for return error condition etc
{
LOG_IT;
Trace("ENTER: RAW Message: ", buf);
if (buf.size() < minimumMessageLength)
return ERR_MSG_TOO_SHORT; // NAK(received insufficient data, yet) until more data (the rest of the Message/header) received?
TMessageHeader *head = (TMessageHeader *)buf.data();
if (head->payload_size > maxPayloadLength)
return ERR_MSG_LEN_MISMATCH;
if (!isValidMessageID(head->type))
return ERR_MSG_ID_UNKNOWN; // NAK(invalid MessageId Category byte)
__u32 statedMessageLength = minimumMessageLength + head->payload_size;
if (buf.size() < statedMessageLength)
return ERR_MSG_LEN_MISMATCH; // NAK(received insufficient data, yet) until more data
TCheckSum checksum = TMessage::calculateChecksum(buf);
if (__valid_checksum__ != checksum)
{
Error("calculated csum: " + std::to_string(checksum) + " ERROR should be zero\n");
return ERR_MSG_CHECKSUM; // NAK(invalid checksum)
}
TBytes payload = buf;
payload.erase(payload.cbegin(), payload.cbegin() + sizeof(TMessageHeader));
TError validPayload = validatePayload(payload);
if (validPayload != 0)
return validPayload;
return 0; // valid message
}
/* A Payload consists of zero or more DataItems
* This function parses an array of bytes that is supposed to be a Payload
* ...then returns a vector of those TDataItems and sets result to indicate error/success
*/
TPayload TMessage::parsePayload(TBytes Payload, __u32 payload_length, TError &result)
{
LOG_IT;
TPayload dataItems; // an empty vector<>
result = ERR_SUCCESS;
if (payload_length == 0)
{ // zero-length payload size is a valid payload
return dataItems;
}
TBytes DataItemBytes = Payload; // pointer to start of byte[] Payload
Trace("Parsing Each Data Item in Payload generated by TMessage::FromBytes");
while (payload_length >= sizeof(TDataItemHeader))
{
if (dataItems.size() >= 16U)
{
result = ERR_MSG_PAYLOAD_DATAITEM_LEN_MISMATCH;
Error("TMessage::parsePayload: more than 16 DataItems");
break;
}
TDataItemHeader *head = (TDataItemHeader *)DataItemBytes.data();
// DataItemLength is the size of the Data Item, including the size of the Data Item Length
// + Data Item ID, and the Data Item's payload's bytelength
__u32 DataItemLength = static_cast<__u32>(sizeof(TDataItemHeader) + head->dataLength); // DataItem[3] is payload length
if (DataItemLength > payload_length)
{
result = ERR_MSG_PAYLOAD_DATAITEM_LEN_MISMATCH;
Error("TMessage::parsePayload: DataItemLength > payload_length returned error " + std::to_string(result) + ", " + err_msg[-result]);
break;
}
PTDataItemBase item = TDataItemBase::fromBytes(DataItemBytes, result);
if (result != ERR_SUCCESS)
{
Error("TMessage::parsePayload: DIAG::fromBytes returned error " + std::to_string(result) + ", " + err_msg[-result]);
break;
}
dataItems.push_back(item);
// remove the bytes from the Payload that were parsed into 'item'
DataItemBytes.erase(DataItemBytes.cbegin(), DataItemBytes.cbegin() + DataItemLength);
payload_length -= DataItemLength;
}
if (result == ERR_SUCCESS && payload_length != 0)
{
result = ERR_MSG_PAYLOAD_DATAITEM_LEN_MISMATCH;
Error("TMessage::parsePayload: trailing bytes are shorter than a DataItem header");
}
return dataItems;
}
TMessage TMessage::FromBytes(TBytes buf, TError &result)
{
LOG_IT;
result = ERR_SUCCESS;
//Debug("Received: ", buf);
auto siz = buf.size();
if (siz < minimumMessageLength)
{
result = ERR_MSG_TOO_SHORT;
Error("Message Size < minimumMessageLength (" + std::to_string(siz) + " < " + std::to_string(minimumMessageLength));
return TMessage(_INVALID_MESSAGEID_); // NAK(received insufficient data, yet) until more data (the rest of the Message/header) received?
}
TMessageHeader *head = (TMessageHeader *)buf.data();
if (head->payload_size > maxPayloadLength)
{
result = ERR_MSG_LEN_MISMATCH;
Error("TMessage::FromBytes: payload exceeds maxPayloadLength: " + std::to_string(head->payload_size));
return TMessage();
}
if (!isValidMessageID(head->type))
{
result = ERR_MSG_ID_UNKNOWN; // NAK(invalid MessageID Category byte)
Error("TMessage::FromBytes: detected invalid MId: " + std::to_string(result) + ", " + err_msg[-result]);
return TMessage();
}
__u32 statedMessageLength = minimumMessageLength + head->payload_size;
if (siz < statedMessageLength)
{
result = ERR_MSG_LEN_MISMATCH; // NAK(received insufficient data, yet) until more data
return TMessage();
}
TPayload dataItems;
if (head->payload_size > 0)
{
Trace("TMessage::FromBytes: Payload is " + std::to_string(head->payload_size) + " bytes");
TBytes payload = buf;
payload.erase(payload.cbegin(), payload.cbegin() + sizeof(TMessageHeader));
Trace("TMessage::FromBytes generated payload: ", payload);
dataItems = parsePayload(payload, head->payload_size, result);
Trace("parsePayload returned " + std::to_string(dataItems.size()) + " with resultCode " + std::to_string(result));
}
TCheckSum checksum = calculateChecksum(buf);
if (__valid_checksum__ != checksum)
{
result = ERR_MSG_CHECKSUM; // NAK(invalid checksum)
Error("TMessage::FromBytes: invalid checksum " + std::to_string(checksum) + " ERROR should be zero\n");
return TMessage();
}
TMessage message = TMessage(head->type, dataItems);
Trace("TMessage::FromBytes: TMessage constructed...Payload DataItem Count: " + std::to_string(message.DataItems.size()));
return message;
}
TMessage::TMessage(TMessageId MId)
{
LOG_IT;
this->setMId(MId);
}
TMessage::TMessage(TMessageId MId, TPayload Payload)
{
LOG_IT;
this->setMId(MId);
for (auto one : Payload)
{
DataItems.push_back(one);
}
// DataItems = Payload;
}
TMessage::TMessage(TBytes Msg)
{
LOG_IT;
TError result = ERR_SUCCESS;
*this = TMessage::FromBytes(Msg, result); // CODE SMELL: this technique makes me question my existence
if (result != ERR_SUCCESS)
throw std::logic_error(err_msg[-result]);
}
TMessageId TMessage::getMId()
{
return this->Id;
}
TCheckSum TMessage::getChecksum(bool bAsReply)
{
return TMessage::calculateChecksum(this->AsBytes(bAsReply));
}
TMessage &TMessage::setMId(TMessageId ID)
{
if (!isValidMessageID(ID))
throw std::logic_error("ERR_MSG_ID_UNKNOWN"); // TODO: FIX using TError
this->Id = ID;
return *this;
}
TMessage &TMessage::addDataItem(PTDataItemBase item)
{
LOG_IT;
this->DataItems.push_back(item);
return *this;
}
void TMessage::appendLengthBytes(TBytes& bytes, TMessagePayloadSize length)
{
for (uint i = 0; i < sizeof(TMessagePayloadSize); i++)
{
__u8 lsb = length & 0xFF;
bytes.push_back(lsb);
length >>= 8;
}
}
void TMessage::appendPayloadLengthAndItems(TBytes& bytes, bool bAsReply)
{
if (this->DataItems.size() > 16U)
throw std::length_error("Message contains more than 16 DataItems");
TBytes payloadBytes;
payloadBytes.reserve(std::min<std::size_t>(maxPayloadLength, 4096U));
for (const auto &item : this->DataItems)
{
if (!item)
throw std::invalid_argument("Message contains a null DataItem");
TBytes itemBytes = item->TDataItemBase::AsBytes(bAsReply);
if (itemBytes.size() < sizeof(TDataItemHeader) ||
itemBytes.size() > sizeof(TDataItemHeader) + MaxDataItemPayload)
throw std::length_error("Serialized DataItem has an invalid length");
if (itemBytes.size() > maxPayloadLength - payloadBytes.size())
throw std::length_error("Message payload exceeds maxPayloadLength");
payloadBytes.insert(payloadBytes.end(), itemBytes.begin(), itemBytes.end());
}
if (payloadBytes.size() > std::numeric_limits<TMessagePayloadSize>::max())
throw std::length_error("Message payload length cannot be represented on the wire");
appendLengthBytes(bytes, static_cast<TMessagePayloadSize>(payloadBytes.size()));
bytes.insert(bytes.end(), payloadBytes.begin(), payloadBytes.end());
}
TBytes TMessage::AsBytes(bool bAsReply)
{
LOG_IT;
Trace("AsBytes"+ bAsReply?", as Reply":", NOT reply");
TBytes bytes;
bytes.push_back(this->Id);
appendPayloadLengthAndItems(bytes, bAsReply);
TCheckSum csum = static_cast<TCheckSum>(-calculateChecksum(bytes));
bytes.push_back(csum); // WARN: only works because TCheckSum == __u8
Trace("Built: ", bytes);
return bytes;
}
std::string TMessage::AsString(bool bAsReply)
{
LOG_IT;
Trace("AsString, "+ bAsReply?"as Reply":"");
std::stringstream dest;
// TBytes raw = this->AsBytes(bAsReply);
// Trace("TMessage, Raw Bytes: ", raw);
//dest << "Message = MId:" << to_hex<__u8>(this->getMId()) << ", # DataItems: " << DataItems.size();
dest << "Message = MId: '" << this->getMId() << "', # DataItems: " << DataItems.size();
if (DataItems.size() != 0)
{
for (uint itemNumber = 0; itemNumber < DataItems.size(); itemNumber++)
{
PTDataItemBase item = this->DataItems[itemNumber];
dest << '\n'
<< " " << std::setw(2) << itemNumber + 1 << ": " << item->AsString(bAsReply);
}
}
return dest.str();
}
#pragma endregion