Skip to content

Commit 61033be

Browse files
committed
Use FSM to encode CBOR messages
1 parent f6af6a7 commit 61033be

File tree

2 files changed

+180
-53
lines changed

2 files changed

+180
-53
lines changed

src/cbor/CBOREncoder.cpp

Lines changed: 140 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -34,85 +34,179 @@
3434

3535
CborError CBOREncoder::encode(PropertyContainer & property_container, uint8_t * data, size_t const size, int & bytes_encoded, unsigned int & current_property_index, bool lightPayload)
3636
{
37-
CborEncoder encoder, arrayEncoder;
37+
EncoderState current_state = EncoderState::InitPropertyEncoder,
38+
next_state = EncoderState::InitPropertyEncoder;
39+
40+
PropertyContainerEncoder propertyEncoder(property_container, current_property_index);
41+
42+
while (current_state != EncoderState::SendMessage) {
43+
44+
switch (current_state) {
45+
case EncoderState::InitPropertyEncoder : next_state = handle_InitPropertyEncoder(propertyEncoder); break;
46+
case EncoderState::OpenCBORContainer : next_state = handle_OpenCBORContainer(propertyEncoder, data, size); break;
47+
case EncoderState::TryAppend : next_state = handle_TryAppend(propertyEncoder, lightPayload); break;
48+
case EncoderState::OutOfMemory : next_state = handle_OutOfMemory(propertyEncoder); break;
49+
case EncoderState::SkipProperty : next_state = handle_SkipProperty(propertyEncoder); break;
50+
case EncoderState::TrimAppend : next_state = handle_TrimAppend(propertyEncoder); break;
51+
case EncoderState::CloseCBORContainer : next_state = handle_CloseCBORContainer(propertyEncoder); break;
52+
case EncoderState::TrimClose : next_state = handle_TrimClose(propertyEncoder); break;
53+
case EncoderState::FinishAppend : next_state = handle_FinishAppend(propertyEncoder); break;
54+
case EncoderState::AdvancePropertyContainer : next_state = handle_AdvancePropertyContainer(propertyEncoder); break;
55+
case EncoderState::SendMessage : /* Nothing to do */ break;
56+
case EncoderState::Error : return CborErrorInternalError; break;
57+
}
58+
59+
current_state = next_state;
60+
}
3861

39-
cbor_encoder_init(&encoder, data, size, 0);
62+
if (propertyEncoder.encoded_property_count > 0)
63+
bytes_encoded = cbor_encoder_get_buffer_size(&propertyEncoder.encoder, data);
64+
else
65+
bytes_encoded = 0;
4066

41-
CHECK_CBOR(cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength));
67+
return CborNoError;
68+
}
4269

43-
/* Check if backing storage and cloud has diverged
44-
* time interval may be elapsed or property may be changed
70+
/******************************************************************************
71+
PRIVATE MEMBER FUNCTIONS
72+
******************************************************************************/
73+
74+
CBOREncoder::EncoderState CBOREncoder::handle_InitPropertyEncoder(PropertyContainerEncoder & propertyEncoder)
75+
{
76+
propertyEncoder.encoded_property_count = 0;
77+
propertyEncoder.checked_property_count = 0;
78+
propertyEncoder.encoded_property_limit = 0;
79+
propertyEncoder.property_limit_active = false;
80+
return EncoderState::OpenCBORContainer;
81+
}
82+
83+
CBOREncoder::EncoderState CBOREncoder::handle_OpenCBORContainer(PropertyContainerEncoder & propertyEncoder, uint8_t * data, size_t const size)
84+
{
85+
propertyEncoder.encoded_property_count = 0;
86+
propertyEncoder.checked_property_count = 0;
87+
cbor_encoder_init(&propertyEncoder.encoder, data, size, 0);
88+
cbor_encoder_create_array(&propertyEncoder.encoder, &propertyEncoder.arrayEncoder, CborIndefiniteLength);
89+
return EncoderState::TryAppend;
90+
}
91+
92+
CBOREncoder::EncoderState CBOREncoder::handle_TryAppend(PropertyContainerEncoder & propertyEncoder, bool & lightPayload)
93+
{
94+
/* Check if backing storage and cloud has diverged. Time interval may be elapsed or property may be changed
4595
* and if that's the case encode the property into the CBOR.
4696
*/
4797
CborError error = CborNoError;
48-
int num_encoded_properties = 0;
49-
int num_checked_properties = 0;
50-
static int encoded_properties_message_limit = CBOR_ENCODER_NO_PROPERTIES_LIMIT;
51-
52-
if(current_property_index >= property_container.size())
53-
current_property_index = 0;
54-
55-
PropertyContainer::iterator iter = property_container.begin();
56-
std::advance(iter, current_property_index);
98+
PropertyContainer::iterator iter = propertyEncoder.property_container.begin();
99+
std::advance(iter, propertyEncoder.current_property_index);
57100

58-
for(; iter != property_container.end(); iter++)
101+
for(; iter != propertyEncoder.property_container.end(); iter++)
59102
{
60103
Property * p = * iter;
61-
bool maximum_number_of_properties_reached = (num_encoded_properties >= encoded_properties_message_limit) && (encoded_properties_message_limit != -1);
62-
bool cbor_encoder_error = (error != CborNoError);
63-
64-
if (maximum_number_of_properties_reached || cbor_encoder_error)
65-
break;
66104

67105
if (p->shouldBeUpdated() && p->isReadableByCloud())
68106
{
69-
error = p->append(&arrayEncoder, lightPayload);
107+
error = p->append(&propertyEncoder.arrayEncoder, lightPayload);
70108
if(error == CborNoError)
71-
num_encoded_properties++;
109+
propertyEncoder.encoded_property_count++;
72110
}
73-
num_checked_properties++;
74-
}
111+
if(error == CborNoError)
112+
propertyEncoder.checked_property_count++;
75113

76-
if ((CborNoError != error) && (CborErrorOutOfMemory != error))
77-
{
78-
/* Trim the number of properties to be included in the next message to avoid multivalue property split */
79-
encoded_properties_message_limit = num_encoded_properties;
80-
return error;
114+
bool const maximum_number_of_properties_reached = (propertyEncoder.encoded_property_count >= propertyEncoder.encoded_property_limit) && (propertyEncoder.property_limit_active == true);
115+
bool const cbor_encoder_error = (error != CborNoError);
116+
117+
if (maximum_number_of_properties_reached || cbor_encoder_error)
118+
break;
81119
}
82120

83-
error = cbor_encoder_close_container(&encoder, &arrayEncoder);
121+
if (CborErrorOutOfMemory == error)
122+
return EncoderState::OutOfMemory;
123+
else if (CborNoError == error)
124+
return EncoderState::CloseCBORContainer;
125+
else if (CborErrorSplitItems == error)
126+
return EncoderState::TrimAppend;
127+
else
128+
return EncoderState::Error;
129+
}
130+
131+
CBOREncoder::EncoderState CBOREncoder::handle_OutOfMemory(PropertyContainerEncoder & propertyEncoder)
132+
{
133+
if(propertyEncoder.encoded_property_count > 0)
134+
return EncoderState::CloseCBORContainer;
135+
else
136+
return EncoderState::SkipProperty;
137+
}
138+
139+
CBOREncoder::EncoderState CBOREncoder::handle_SkipProperty(PropertyContainerEncoder & propertyEncoder)
140+
{
141+
/* Better to skip this property otherwise we will stay blocked here. This happens only with a message property
142+
* that not fits into the CBOR buffer
143+
*/
144+
propertyEncoder.current_property_index++;
145+
if(propertyEncoder.current_property_index >= propertyEncoder.property_container.size())
146+
propertyEncoder.current_property_index = 0;
147+
return EncoderState::Error;
148+
}
149+
150+
CBOREncoder::EncoderState CBOREncoder::handle_TrimAppend(PropertyContainerEncoder & propertyEncoder)
151+
{
152+
/* Trim the number of properties to be included in the next message to avoid multivalue property split */
153+
propertyEncoder.encoded_property_limit = propertyEncoder.encoded_property_count;
154+
propertyEncoder.property_limit_active = true;
155+
if(propertyEncoder.encoded_property_limit > 0)
156+
return EncoderState::OpenCBORContainer;
157+
else
158+
return EncoderState::Error;
159+
}
160+
161+
CBOREncoder::EncoderState CBOREncoder::handle_CloseCBORContainer(PropertyContainerEncoder & propertyEncoder)
162+
{
163+
CborError error = cbor_encoder_close_container(&propertyEncoder.encoder, &propertyEncoder.arrayEncoder);
84164
if (CborNoError != error)
85-
{
86-
/* Trim the number of properties to be included in the next message to avoid error closing container */
87-
encoded_properties_message_limit = num_encoded_properties - 1;
88-
return error;
89-
}
165+
return EncoderState::TrimClose;
166+
else
167+
return EncoderState::FinishAppend;
168+
}
169+
170+
CBOREncoder::EncoderState CBOREncoder::handle_TrimClose(PropertyContainerEncoder & propertyEncoder)
171+
{
172+
/* Trim the number of properties to be included in the next message to avoid error closing container */
173+
propertyEncoder.encoded_property_limit = propertyEncoder.encoded_property_count - 1;
174+
propertyEncoder.property_limit_active = true;
175+
if(propertyEncoder.encoded_property_limit > 0)
176+
return EncoderState::OpenCBORContainer;
177+
else
178+
return EncoderState::Error;
179+
}
90180

181+
CBOREncoder::EncoderState CBOREncoder::handle_FinishAppend(PropertyContainerEncoder & propertyEncoder)
182+
{
91183
/* Restore property message limit to CBOR_ENCODER_NO_PROPERTIES_LIMIT */
92-
encoded_properties_message_limit = CBOR_ENCODER_NO_PROPERTIES_LIMIT;
184+
propertyEncoder.property_limit_active = false;
93185

94186
/* The append process has been successful, so we don't need to terty to send this properties set. Cleanup _has_been_appended_but_not_sended flag */
95-
iter = property_container.begin();
96-
std::advance(iter, current_property_index);
187+
PropertyContainer::iterator iter = propertyEncoder.property_container.begin();
188+
std::advance(iter, propertyEncoder.current_property_index);
97189
int num_appended_properties = 0;
98190

99-
for(; iter != property_container.end(); iter++)
191+
for(; iter != propertyEncoder.property_container.end(); iter++)
100192
{
101193
Property * p = * iter;
102-
if (num_appended_properties >= num_checked_properties)
194+
if (num_appended_properties >= propertyEncoder.checked_property_count)
103195
break;
104196

105197
p->appendCompleted();
106198
num_appended_properties++;
107199
}
200+
return EncoderState::AdvancePropertyContainer;
201+
}
108202

203+
CBOREncoder::EncoderState CBOREncoder::handle_AdvancePropertyContainer(PropertyContainerEncoder & propertyEncoder)
204+
{
109205
/* Advance property index for the nex message */
110-
current_property_index += num_checked_properties;
206+
propertyEncoder.current_property_index += propertyEncoder.checked_property_count;
111207

112-
if (num_encoded_properties > 0)
113-
bytes_encoded = cbor_encoder_get_buffer_size(&encoder, data);
114-
else
115-
bytes_encoded = 0;
208+
if(propertyEncoder.current_property_index >= propertyEncoder.property_container.size())
209+
propertyEncoder.current_property_index = 0;
116210

117-
return CborNoError;
211+
return EncoderState::SendMessage;
118212
}

src/cbor/CBOREncoder.h

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,6 @@
3434

3535
#include "../property/PropertyContainer.h"
3636

37-
/******************************************************************************
38-
* CONSTANTS
39-
******************************************************************************/
40-
41-
static int const CBOR_ENCODER_NO_PROPERTIES_LIMIT = -1;
42-
4337
/******************************************************************************
4438
* CLASS DECLARATION
4539
******************************************************************************/
@@ -48,7 +42,6 @@ class CBOREncoder
4842
{
4943

5044
public:
51-
5245
/* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */
5346
/* if lightPayload is true the integer identifier of the property will be encoded in the message instead of the property name in order to reduce the size of the message payload*/
5447
static CborError encode(PropertyContainer & property_container, uint8_t * data, size_t const size, int & bytes_encoded, unsigned int & current_property_index, bool lightPayload = false);
@@ -58,6 +51,46 @@ class CBOREncoder
5851
CBOREncoder() { }
5952
CBOREncoder(CborEncoder const &) { }
6053

54+
enum class EncoderState
55+
{
56+
InitPropertyEncoder,
57+
OpenCBORContainer,
58+
TryAppend,
59+
OutOfMemory,
60+
SkipProperty,
61+
TrimAppend,
62+
CloseCBORContainer,
63+
TrimClose,
64+
FinishAppend,
65+
AdvancePropertyContainer,
66+
SendMessage,
67+
Error
68+
};
69+
70+
struct PropertyContainerEncoder
71+
{
72+
PropertyContainerEncoder(PropertyContainer & _property_container, unsigned int & _current_property_index): property_container(_property_container), current_property_index(_current_property_index) { }
73+
PropertyContainer & property_container;
74+
unsigned int & current_property_index;
75+
int encoded_property_count;
76+
int checked_property_count;
77+
int encoded_property_limit;
78+
bool property_limit_active;
79+
CborEncoder encoder;
80+
CborEncoder arrayEncoder;
81+
};
82+
83+
static EncoderState handle_InitPropertyEncoder(PropertyContainerEncoder & propertyEncoder);
84+
static EncoderState handle_OpenCBORContainer(PropertyContainerEncoder & propertyEncoder, uint8_t * data, size_t const size);
85+
static EncoderState handle_TryAppend(PropertyContainerEncoder & propertyEncoder, bool & lightPayload);
86+
static EncoderState handle_OutOfMemory(PropertyContainerEncoder & propertyEncoder);
87+
static EncoderState handle_SkipProperty(PropertyContainerEncoder & propertyEncoder);
88+
static EncoderState handle_TrimAppend(PropertyContainerEncoder & propertyEncoder);
89+
static EncoderState handle_CloseCBORContainer(PropertyContainerEncoder & propertyEncoder);
90+
static EncoderState handle_TrimClose(PropertyContainerEncoder & propertyEncoder);
91+
static EncoderState handle_FinishAppend(PropertyContainerEncoder & propertyEncoder);
92+
static EncoderState handle_AdvancePropertyContainer(PropertyContainerEncoder & propertyEncoder);
93+
6194
};
6295

6396
#endif /* ARDUINO_CBOR_CBOR_ENCODER_H_ */

0 commit comments

Comments
 (0)