Skip to content

Commit 19806dc

Browse files
implementing CBOR encoder and decoder for Command protocol model
1 parent 45c4f07 commit 19806dc

File tree

6 files changed

+680
-0
lines changed

6 files changed

+680
-0
lines changed

src/cbor/CBOR.cpp

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#include "CBOR.h"
2+
3+
CommandID toCommandId(CBORCommandTag tag) {
4+
switch(tag) {
5+
case CBORCommandTag::CBOROtaBeginUp:
6+
return CommandID::OtaBeginUpId;
7+
case CBORCommandTag::CBORThingGetIdCmdUp:
8+
return CommandID::ThingGetIdCmdUpId;
9+
case CBORCommandTag::CBORThingGetLastValueCmdUp:
10+
return CommandID::ThingGetLastValueCmdUpId;
11+
case CBORCommandTag::CBORDeviceBeginCmdUp:
12+
return CommandID::DeviceBeginCmdUpId;
13+
case CBORCommandTag::CBOROtaProgressCmdUp:
14+
return CommandID::OtaProgressCmdUpId;
15+
case CBORCommandTag::CBORTimezoneCommandUp:
16+
return CommandID::TimezoneCommandUpId;
17+
case CBORCommandTag::CBOROtaUpdateCmdDown:
18+
return CommandID::OtaUpdateCmdDownId;
19+
case CBORCommandTag::CBORThingGetIdCmdDown:
20+
return CommandID::ThingGetIdCmdDownId;
21+
case CBORCommandTag::CBORThingGetLastValueCmdDown:
22+
return CommandID::ThingGetLastValueCmdDownId;
23+
case CBORCommandTag::CBORTimezoneCommandDown:
24+
return CommandID::TimezoneCommandDownId;
25+
default:
26+
return CommandID::UnknownCmdId;
27+
}
28+
}
29+
30+
31+
CBORCommandTag toCBORCommandTag(CommandID id) {
32+
switch(id) {
33+
case CommandID::OtaBeginUpId:
34+
return CBORCommandTag::CBOROtaBeginUp;
35+
case CommandID::ThingGetIdCmdUpId:
36+
return CBORCommandTag::CBORThingGetIdCmdUp;
37+
case CommandID::ThingGetLastValueCmdUpId:
38+
return CBORCommandTag::CBORThingGetLastValueCmdUp;
39+
case CommandID::DeviceBeginCmdUpId:
40+
return CBORCommandTag::CBORDeviceBeginCmdUp;
41+
case CommandID::OtaProgressCmdUpId:
42+
return CBORCommandTag::CBOROtaProgressCmdUp;
43+
case CommandID::TimezoneCommandUpId:
44+
return CBORCommandTag::CBORTimezoneCommandUp;
45+
case CommandID::OtaUpdateCmdDownId:
46+
return CBORCommandTag::CBOROtaUpdateCmdDown;
47+
case CommandID::ThingGetIdCmdDownId:
48+
return CBORCommandTag::CBORThingGetIdCmdDown;
49+
case CommandID::ThingGetLastValueCmdDownId:
50+
return CBORCommandTag::CBORThingGetLastValueCmdDown;
51+
case CommandID::TimezoneCommandDownId:
52+
return CBORCommandTag::CBORTimezoneCommandDown;
53+
default:
54+
return CBORCommandTag::CBORUnknownCmdTag;
55+
}
56+
}

src/cbor/CBOR.h

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
#include <message/Models.h>
3+
4+
enum CBORCommandTag: uint64_t {
5+
// Commands UP
6+
CBOROtaBeginUp = 0x10000,
7+
CBORThingGetIdCmdUp = 0x10300,
8+
CBORThingGetLastValueCmdUp = 0x10500,
9+
CBORDeviceBeginCmdUp = 0x10700,
10+
CBOROtaProgressCmdUp = 0x10200,
11+
CBORTimezoneCommandUp = 0x10800,
12+
13+
// Commands DOWN
14+
CBOROtaUpdateCmdDown = 0x10100,
15+
CBORThingGetIdCmdDown = 0x10400,
16+
CBORThingGetLastValueCmdDown = 0x10600,
17+
CBORTimezoneCommandDown = 0x10900,
18+
19+
// Unknown Command Tag https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
20+
CBORUnknownCmdTag16b = 0xffff, // invalid tag
21+
CBORUnknownCmdTag32b = 0xffffffff, // invalid tag
22+
CBORUnknownCmdTag64b = 0xffffffffffffffff, // invalid tag
23+
CBORUnknownCmdTag = CBORUnknownCmdTag32b
24+
};
25+
26+
CommandID toCommandId(CBORCommandTag tag);
27+
CBORCommandTag toCBORCommandTag(CommandID id);

src/cbor/MessageDecoder.cpp

+247
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
//
2+
// This file is part of CBORDecoder
3+
//
4+
// Copyright 2019 ARDUINO SA (http://www.arduino.cc/)
5+
//
6+
// This software is released under the GNU General Public License version 3,
7+
// which covers the main part of CBORDecoder.
8+
// The terms of this license can be found at:
9+
// https://www.gnu.org/licenses/gpl-3.0.en.html
10+
//
11+
// You can be released from the requirements of the above licenses by purchasing
12+
// a commercial license. Buying such a license is mandatory if you want to modify or
13+
// otherwise use the software for commercial activities involving the Arduino
14+
// software without disclosing the source code of your own applications. To purchase
15+
// a commercial license, send an email to [email protected].
16+
//
17+
18+
/******************************************************************************
19+
INCLUDE
20+
******************************************************************************/
21+
22+
#include <Arduino.h>
23+
24+
#undef max
25+
#undef min
26+
#include <algorithm>
27+
28+
#include "MessageDecoder.h"
29+
#include <AIoTC_Config.h>
30+
31+
/******************************************************************************
32+
PUBLIC MEMBER FUNCTIONS
33+
******************************************************************************/
34+
35+
Decoder::Status CBORMessageDecoder::decode(Message * message, uint8_t const * const payload, size_t& length)
36+
{
37+
CborValue main_iter, array_iter;
38+
CborTag tag;
39+
CborParser parser;
40+
41+
if (cbor_parser_init(payload, length, 0, &parser, &main_iter) != CborNoError)
42+
return Decoder::Status::Error;
43+
44+
if (main_iter.type != CborTagType)
45+
return Decoder::Status::Error;
46+
47+
if (cbor_value_get_tag(&main_iter, &tag) == CborNoError) {
48+
message->id = toCommandId(CBORCommandTag(tag));
49+
}
50+
51+
if (cbor_value_advance(&main_iter) != CborNoError) {
52+
return Decoder::Status::Error;
53+
}
54+
55+
ArrayParserState current_state = ArrayParserState::EnterArray,
56+
next_state = ArrayParserState::Error;
57+
58+
while (current_state != ArrayParserState::Complete) {
59+
switch (current_state) {
60+
case ArrayParserState::EnterArray : next_state = handle_EnterArray(&main_iter, &array_iter); break;
61+
case ArrayParserState::ParseParam : next_state = handle_Param(&array_iter, message); break;
62+
case ArrayParserState::LeaveArray : next_state = handle_LeaveArray(&main_iter, &array_iter); break;
63+
case ArrayParserState::Complete : return Decoder::Status::Complete;
64+
case ArrayParserState::MessageNotSupported : return Decoder::Status::Error;
65+
case ArrayParserState::Error : return Decoder::Status::Error;
66+
}
67+
68+
current_state = next_state;
69+
}
70+
71+
return Decoder::Status::Complete;
72+
}
73+
74+
/******************************************************************************
75+
PRIVATE MEMBER FUNCTIONS
76+
******************************************************************************/
77+
78+
bool copyCBORStringToArray(CborValue * param, char * dest, size_t dest_size) {
79+
if (!cbor_value_is_text_string(param)) {
80+
return false;
81+
}
82+
83+
// NOTE: keep in mind that _cbor_value_copy_string tries to put a \0 at the end of the string
84+
if(_cbor_value_copy_string(param, dest, &dest_size, NULL) != CborNoError) {
85+
return false;
86+
}
87+
88+
return true;
89+
}
90+
91+
// FIXME dest_size should be also returned, the copied byte array can have a different size from the starting one
92+
// for the time being we need this on SHA256 only
93+
bool copyCBORByteToArray(CborValue * param, uint8_t * dest, size_t dest_size) {
94+
if (!cbor_value_is_byte_string(param)) {
95+
return false;
96+
}
97+
98+
// NOTE: keep in mind that _cbor_value_copy_string tries to put a \0 at the end of the string
99+
if(_cbor_value_copy_string(param, dest, &dest_size, NULL) != CborNoError) {
100+
101+
return false;
102+
}
103+
104+
return true;
105+
}
106+
107+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::handle_EnterArray(CborValue * main_iter, CborValue * array_iter) {
108+
ArrayParserState next_state = ArrayParserState::Error;
109+
if (cbor_value_get_type(main_iter) == CborArrayType) {
110+
if (cbor_value_enter_container(main_iter, array_iter) == CborNoError) {
111+
next_state = ArrayParserState::ParseParam;
112+
}
113+
}
114+
115+
return next_state;
116+
}
117+
118+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::handle_LeaveArray(CborValue * main_iter, CborValue * array_iter) {
119+
// Advance to the next parameter (the last one in the array)
120+
if (cbor_value_advance(array_iter) != CborNoError) {
121+
return ArrayParserState::Error;
122+
}
123+
// Leave the array
124+
if (cbor_value_leave_container(main_iter, array_iter) != CborNoError) {
125+
return ArrayParserState::Error;
126+
}
127+
return ArrayParserState::Complete;
128+
}
129+
130+
/******************************************************************************
131+
MESSAGE DECODE FUNCTIONS
132+
******************************************************************************/
133+
134+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeThingGetIdCmdDown(CborValue * param, Message * message) {
135+
ThingGetIdCmdDown * thingCommand = (ThingGetIdCmdDown *) message;
136+
137+
// Message is composed of a single parameter, a string (thing_id)
138+
if (!copyCBORStringToArray(param, thingCommand->params.thing_id, sizeof(thingCommand->params.thing_id))) {
139+
return ArrayParserState::Error;
140+
}
141+
142+
return ArrayParserState::LeaveArray;
143+
}
144+
145+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeTimezoneCommandDown(CborValue * param, Message * message) {
146+
TimezoneCommandDown * setTz = (TimezoneCommandDown *) message;
147+
148+
// Message is composed of 2 parameters, offset 32-bit signed integer and until 32-bit unsigned integer
149+
// Get offset
150+
if (cbor_value_is_integer(param)) {
151+
int64_t val = 0;
152+
if (cbor_value_get_int64(param, &val) == CborNoError) {
153+
setTz->params.offset = static_cast<int32_t>(val);
154+
}
155+
}
156+
157+
// Next
158+
if (cbor_value_advance(param) != CborNoError) {
159+
return ArrayParserState::Error;
160+
}
161+
162+
// Get until
163+
if (cbor_value_is_integer(param)) {
164+
uint64_t val = 0;
165+
if (cbor_value_get_uint64(param, &val) == CborNoError) {
166+
setTz->params.until = static_cast<uint32_t>(val);
167+
}
168+
}
169+
170+
return ArrayParserState::LeaveArray;
171+
}
172+
173+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeThingGetLastValueCmdDown(CborValue * param, Message * message) {
174+
ThingGetLastValueCmdDown * setLv = (ThingGetLastValueCmdDown *) message;
175+
176+
// Message is composed by a single parameter, a variable length byte array.
177+
if (cbor_value_is_byte_string(param)) {
178+
// Cortex M0 is not able to assign a value to pointed memory that is not 32bit aligned
179+
// we use a support variable to cope with that
180+
size_t s;
181+
if (cbor_value_dup_byte_string(param, &setLv->params.last_values, &s, NULL) != CborNoError) {
182+
return ArrayParserState::Error;
183+
}
184+
185+
setLv->params.length = s;
186+
}
187+
188+
return ArrayParserState::LeaveArray;
189+
}
190+
191+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::decodeOtaUpdateCmdDown(CborValue * param, Message * message) {
192+
OtaUpdateCmdDown * ota = (OtaUpdateCmdDown *) message;
193+
194+
// Message is composed 4 parameters: id, url, initialSha, finalSha
195+
if (!copyCBORByteToArray(param, ota->params.id, sizeof(ota->params.id))) {
196+
return ArrayParserState::Error;
197+
}
198+
199+
if (cbor_value_advance(param) != CborNoError) {
200+
return ArrayParserState::Error;
201+
}
202+
203+
if (!copyCBORStringToArray(param, ota->params.url, sizeof(ota->params.url))) {
204+
return ArrayParserState::Error;
205+
}
206+
207+
if (cbor_value_advance(param) != CborNoError) {
208+
return ArrayParserState::Error;
209+
}
210+
211+
if (!copyCBORByteToArray(param, ota->params.initialSha256, sizeof(ota->params.initialSha256))) {
212+
return ArrayParserState::Error;
213+
}
214+
215+
if (cbor_value_advance(param) != CborNoError) {
216+
return ArrayParserState::Error;
217+
}
218+
219+
if (!copyCBORByteToArray(param, ota->params.finalSha256, sizeof(ota->params.finalSha256))) {
220+
return ArrayParserState::Error;
221+
}
222+
223+
return ArrayParserState::LeaveArray;
224+
}
225+
226+
CBORMessageDecoder::ArrayParserState CBORMessageDecoder::handle_Param(CborValue * param, Message * message) {
227+
228+
switch (message->id)
229+
{
230+
case CommandID::ThingGetIdCmdDownId:
231+
return CBORMessageDecoder::decodeThingGetIdCmdDown(param, message);
232+
233+
case CommandID::TimezoneCommandDownId:
234+
return CBORMessageDecoder::decodeTimezoneCommandDown(param, message);
235+
236+
case CommandID::ThingGetLastValueCmdDownId:
237+
return CBORMessageDecoder::decodeThingGetLastValueCmdDown(param, message);
238+
239+
case CommandID::OtaUpdateCmdDownId:
240+
return CBORMessageDecoder::decodeOtaUpdateCmdDown(param, message);
241+
242+
default:
243+
return ArrayParserState::MessageNotSupported;
244+
}
245+
246+
return ArrayParserState::LeaveArray;
247+
}

0 commit comments

Comments
 (0)