From f5b729a207d096d84e091fe638795876414aabc1 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Mon, 6 Jul 2020 10:20:11 +0200 Subject: [PATCH 1/7] Allow propagation of CBOR library function return value to determine if a out-of-memory error occurs. If that's the case the CBOR encoding is completed until that property and the CBOR message is still sent with the next properties encoded on the next run of CBOREncoder::encode --- src/cbor/CBOREncoder.cpp | 3 +- src/property/Property.cpp | 72 +++++++++++-------- src/property/Property.h | 26 +++++-- src/property/PropertyContainer.cpp | 13 ++-- src/property/PropertyContainer.h | 2 +- src/property/types/CloudBool.h | 4 +- src/property/types/CloudColor.h | 9 +-- src/property/types/CloudFloat.h | 4 +- src/property/types/CloudInt.h | 4 +- src/property/types/CloudLocation.h | 7 +- src/property/types/CloudString.h | 4 +- src/property/types/CloudWrapperBool.h | 4 +- src/property/types/CloudWrapperFloat.h | 4 +- src/property/types/CloudWrapperInt.h | 4 +- src/property/types/CloudWrapperString.h | 4 +- .../types/automation/CloudColoredLight.h | 2 +- .../types/automation/CloudDimmedLight.h | 2 +- .../types/automation/CloudTelevision.h | 2 +- 18 files changed, 100 insertions(+), 70 deletions(-) diff --git a/src/cbor/CBOREncoder.cpp b/src/cbor/CBOREncoder.cpp index 6de4d487d..5fa9b51dd 100644 --- a/src/cbor/CBOREncoder.cpp +++ b/src/cbor/CBOREncoder.cpp @@ -40,7 +40,8 @@ int CBOREncoder::encode(PropertyContainer & property_container, uint8_t * data, * time interval may be elapsed or property may be changed * and if that's the case encode the property into the CBOR. */ - if (appendChangedProperties(property_container, &arrayEncoder, lightPayload) < 1) + CborError err = appendChangedProperties(property_container, &arrayEncoder, lightPayload); + if ((err != CborNoError) && (err != CborErrorOutOfMemory)) return -1; if (cbor_encoder_close_container(&encoder, &arrayEncoder) != CborNoError) diff --git a/src/property/Property.cpp b/src/property/Property.cpp index 9c4203c4d..bccbae1b8 100644 --- a/src/property/Property.cpp +++ b/src/property/Property.cpp @@ -142,78 +142,92 @@ void Property::execCallbackOnSync() { } } -void Property::append(CborEncoder *encoder, bool lightPayload) { +CborError Property::append(CborEncoder *encoder, bool lightPayload) { _lightPayload = lightPayload; _attributeIdentifier = 0; - appendAttributesToCloudReal(encoder); + CHECK_CBOR(appendAttributesToCloudReal(encoder)); fromLocalToCloud(); _has_been_updated_once = true; _update_requested = false; _last_updated_millis = millis(); + return CborNoError; } -void Property::appendAttributeReal(bool value, String attributeName, CborEncoder *encoder) { - appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) { - cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); - cbor_encode_boolean(&mapEncoder, value); +CborError Property::appendAttributeReal(bool value, String attributeName, CborEncoder *encoder) { + return appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) + { + CHECK_CBOR(cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::BooleanValue))); + CHECK_CBOR(cbor_encode_boolean(&mapEncoder, value)); + return CborNoError; }, encoder); } -void Property::appendAttributeReal(int value, String attributeName, CborEncoder *encoder) { - appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) { - cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Value)); - cbor_encode_int(&mapEncoder, value); +CborError Property::appendAttributeReal(int value, String attributeName, CborEncoder *encoder) { + return appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) + { + CHECK_CBOR(cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Value))); + CHECK_CBOR(cbor_encode_int(&mapEncoder, value)); + return CborNoError; }, encoder); } -void Property::appendAttributeReal(float value, String attributeName, CborEncoder *encoder) { - appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) { - cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Value)); - cbor_encode_float(&mapEncoder, value); +CborError Property::appendAttributeReal(float value, String attributeName, CborEncoder *encoder) { + return appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) + { + CHECK_CBOR(cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Value))); + CHECK_CBOR(cbor_encode_float(&mapEncoder, value)); + return CborNoError; }, encoder); } -void Property::appendAttributeReal(String value, String attributeName, CborEncoder *encoder) { - appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) { - cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::StringValue)); - cbor_encode_text_stringz(&mapEncoder, value.c_str()); +CborError Property::appendAttributeReal(String value, String attributeName, CborEncoder *encoder) { + return appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) + { + CHECK_CBOR(cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::StringValue))); + CHECK_CBOR(cbor_encode_text_stringz(&mapEncoder, value.c_str())); + return CborNoError; }, encoder); } -void Property::appendAttributeName(String attributeName, std::functionappendValue, CborEncoder *encoder) { +CborError Property::appendAttributeName(String attributeName, std::functionappendValue, CborEncoder *encoder) { if (attributeName != "") { // when the attribute name string is not empty, the attribute identifier is incremented in order to be encoded in the message if the _lightPayload flag is set _attributeIdentifier++; } CborEncoder mapEncoder; unsigned int num_map_properties = _encode_timestamp ? 3 : 2; - cbor_encoder_create_map(encoder, &mapEncoder, num_map_properties); - cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Name)); + CHECK_CBOR(cbor_encoder_create_map(encoder, &mapEncoder, num_map_properties)); + CHECK_CBOR(cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Name))); // if _lightPayload is true, the property and attribute identifiers will be encoded instead of the property name - if (_lightPayload) { + if (_lightPayload) + { // the most significant byte of the identifier to be encoded represent the property identifier int completeIdentifier = _attributeIdentifier * 256; // the least significant byte of the identifier to be encoded represent the attribute identifier completeIdentifier += _identifier; - cbor_encode_int(&mapEncoder, completeIdentifier); - } else { + CHECK_CBOR(cbor_encode_int(&mapEncoder, completeIdentifier)); + } + else + { String completeName = _name; if (attributeName != "") { completeName += ":" + attributeName; } - cbor_encode_text_stringz(&mapEncoder, completeName.c_str()); + CHECK_CBOR(cbor_encode_text_stringz(&mapEncoder, completeName.c_str())); } /* Encode the value */ - appendValue(mapEncoder); + CHECK_CBOR(appendValue(mapEncoder)); + /* Encode the timestamp if that has been required. */ if(_encode_timestamp) { - cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::Time)); - cbor_encode_uint(&mapEncoder, _timestamp); + CHECK_CBOR(cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::Time))); + CHECK_CBOR(cbor_encode_uint(&mapEncoder, _timestamp)); } /* Close the container */ - cbor_encoder_close_container(encoder, &mapEncoder); + CHECK_CBOR(cbor_encoder_close_container(encoder, &mapEncoder)); + return CborNoError; } void Property::setAttributesFromCloud(std::list * map_data_list) { diff --git a/src/property/Property.h b/src/property/Property.h index 8eb643668..864d44224 100644 --- a/src/property/Property.h +++ b/src/property/Property.h @@ -36,6 +36,18 @@ #include "../cbor/lib/tinycbor/cbor-lib.h" +/****************************************************************************** + DEFINE + ******************************************************************************/ + +#define CHECK_CBOR(expr) \ + do { \ + CborError error = CborNoError; \ + error = (expr); \ + if (CborNoError != error) \ + return error; \ + } while(0); + #define appendAttributesToCloud() appendAttributesToCloudReal(CborEncoder *encoder) #define appendAttribute(x) appendAttributeReal(x, getAttributeName(#x, '.'), encoder) #define setAttribute(x) setAttributeReal(x, getAttributeName(#x, '.')) @@ -168,12 +180,12 @@ class Property void setIdentifier(int identifier); void updateLocalTimestamp(); - void append(CborEncoder * encoder, bool lightPayload); - void appendAttributeReal(bool value, String attributeName = "", CborEncoder *encoder = nullptr); - void appendAttributeReal(int value, String attributeName = "", CborEncoder *encoder = nullptr); - void appendAttributeReal(float value, String attributeName = "", CborEncoder *encoder = nullptr); - void appendAttributeReal(String value, String attributeName = "", CborEncoder *encoder = nullptr); - void appendAttributeName(String attributeName, std::functionf, CborEncoder *encoder); + CborError append(CborEncoder * encoder, bool lightPayload); + CborError appendAttributeReal(bool value, String attributeName = "", CborEncoder *encoder = nullptr); + CborError appendAttributeReal(int value, String attributeName = "", CborEncoder *encoder = nullptr); + CborError appendAttributeReal(float value, String attributeName = "", CborEncoder *encoder = nullptr); + CborError appendAttributeReal(String value, String attributeName = "", CborEncoder *encoder = nullptr); + CborError appendAttributeName(String attributeName, std::functionf, CborEncoder *encoder); void setAttributesFromCloud(std::list * map_data_list); void setAttributeReal(bool& value, String attributeName = ""); void setAttributeReal(int& value, String attributeName = ""); @@ -185,7 +197,7 @@ class Property virtual bool isDifferentFromCloud() = 0; virtual void fromCloudToLocal() = 0; virtual void fromLocalToCloud() = 0; - virtual void appendAttributesToCloudReal(CborEncoder *encoder) = 0; + virtual CborError appendAttributesToCloudReal(CborEncoder *encoder) = 0; virtual void setAttributesFromCloud() = 0; virtual bool isPrimitive() { return false; diff --git a/src/property/PropertyContainer.cpp b/src/property/PropertyContainer.cpp index b545378ad..8cc5f83e2 100644 --- a/src/property/PropertyContainer.cpp +++ b/src/property/PropertyContainer.cpp @@ -83,20 +83,21 @@ Property * getProperty(PropertyContainer & prop_cont, int const identifier) return (*iter); } -int appendChangedProperties(PropertyContainer & prop_cont, CborEncoder * arrayEncoder, bool lightPayload) +CborError appendChangedProperties(PropertyContainer & prop_cont, CborEncoder * arrayEncoder, bool lightPayload) { - int appendedProperties = 0; + CborError err = CborNoError; std::for_each(prop_cont.begin(), prop_cont.end(), - [arrayEncoder, lightPayload, &appendedProperties](Property * p) + [arrayEncoder, lightPayload, &err](Property * p) { if (p->shouldBeUpdated() && p->isReadableByCloud()) { - p->append(arrayEncoder, lightPayload); - appendedProperties++; + err = p->append(arrayEncoder, lightPayload); + if(err != CborNoError) + return; } }); - return appendedProperties; + return err; } void requestUpdateForAllProperties(PropertyContainer & prop_cont) diff --git a/src/property/PropertyContainer.h b/src/property/PropertyContainer.h index abc9d7a79..7212f3a60 100644 --- a/src/property/PropertyContainer.h +++ b/src/property/PropertyContainer.h @@ -78,7 +78,7 @@ Property * getProperty(PropertyContainer & prop_cont, String const & name); Property * getProperty(PropertyContainer & prop_cont, int const identifier); -int appendChangedProperties(PropertyContainer & prop_cont, CborEncoder * arrayEncoder, bool lightPayload); +CborError appendChangedProperties(PropertyContainer & prop_cont, CborEncoder * arrayEncoder, bool lightPayload); void updateTimestampOnLocallyChangedProperties(PropertyContainer & prop_cont); void requestUpdateForAllProperties(PropertyContainer & prop_cont); void updateProperty(PropertyContainer & prop_cont, String propertyName, unsigned long cloudChangeEventTime, bool const is_sync_message, std::list * map_data_list); diff --git a/src/property/types/CloudBool.h b/src/property/types/CloudBool.h index 2ac989629..e0b9f1f30 100644 --- a/src/property/types/CloudBool.h +++ b/src/property/types/CloudBool.h @@ -52,8 +52,8 @@ class CloudBool : public Property { virtual void fromLocalToCloud() { _cloud_value = _value; } - virtual void appendAttributesToCloud() { - appendAttribute(_value); + virtual CborError appendAttributesToCloud() { + return appendAttribute(_value); } virtual void setAttributesFromCloud() { setAttribute(_cloud_value); diff --git a/src/property/types/CloudColor.h b/src/property/types/CloudColor.h index ea126122a..205423165 100644 --- a/src/property/types/CloudColor.h +++ b/src/property/types/CloudColor.h @@ -188,10 +188,11 @@ class CloudColor : public Property { virtual void fromLocalToCloud() { _cloud_value = _value; } - virtual void appendAttributesToCloud() { - appendAttribute(_value.hue); - appendAttribute(_value.sat); - appendAttribute(_value.bri); + virtual CborError appendAttributesToCloud() { + CHECK_CBOR(appendAttribute(_value.hue)); + CHECK_CBOR(appendAttribute(_value.sat)); + CHECK_CBOR(appendAttribute(_value.bri)); + return CborNoError; } virtual void setAttributesFromCloud() { setAttribute(_cloud_value.hue); diff --git a/src/property/types/CloudFloat.h b/src/property/types/CloudFloat.h index 33860f9c3..b68fe70fe 100644 --- a/src/property/types/CloudFloat.h +++ b/src/property/types/CloudFloat.h @@ -54,8 +54,8 @@ class CloudFloat : public Property { virtual void fromLocalToCloud() { _cloud_value = _value; } - virtual void appendAttributesToCloud() { - appendAttribute(_value); + virtual CborError appendAttributesToCloud() { + return appendAttribute(_value); } virtual void setAttributesFromCloud() { setAttribute(_cloud_value); diff --git a/src/property/types/CloudInt.h b/src/property/types/CloudInt.h index 97a3c6a15..0ce65777a 100644 --- a/src/property/types/CloudInt.h +++ b/src/property/types/CloudInt.h @@ -52,8 +52,8 @@ class CloudInt : public Property { virtual void fromLocalToCloud() { _cloud_value = _value; } - virtual void appendAttributesToCloud() { - appendAttribute(_value); + virtual CborError appendAttributesToCloud() { + return appendAttribute(_value); } virtual void setAttributesFromCloud() { setAttribute(_cloud_value); diff --git a/src/property/types/CloudLocation.h b/src/property/types/CloudLocation.h index cbb74754a..0968fea3d 100644 --- a/src/property/types/CloudLocation.h +++ b/src/property/types/CloudLocation.h @@ -89,9 +89,10 @@ class CloudLocation : public Property { virtual void fromLocalToCloud() { _cloud_value = _value; } - virtual void appendAttributesToCloud() { - appendAttribute(_value.lat); - appendAttribute(_value.lon); + virtual CborError appendAttributesToCloud() { + CHECK_CBOR(appendAttribute(_value.lat)); + CHECK_CBOR(appendAttribute(_value.lon)); + return CborNoError; } virtual void setAttributesFromCloud() { setAttribute(_cloud_value.lat); diff --git a/src/property/types/CloudString.h b/src/property/types/CloudString.h index b2fd447ef..027179411 100644 --- a/src/property/types/CloudString.h +++ b/src/property/types/CloudString.h @@ -55,8 +55,8 @@ class CloudString : public Property { virtual void fromLocalToCloud() { _cloud_value = _value; } - virtual void appendAttributesToCloud() { - appendAttribute(_value); + virtual CborError appendAttributesToCloud() { + return appendAttribute(_value); } virtual void setAttributesFromCloud() { setAttribute(_cloud_value); diff --git a/src/property/types/CloudWrapperBool.h b/src/property/types/CloudWrapperBool.h index 483cefc99..701e7c87d 100644 --- a/src/property/types/CloudWrapperBool.h +++ b/src/property/types/CloudWrapperBool.h @@ -45,8 +45,8 @@ class CloudWrapperBool : public CloudWrapperBase { virtual void fromLocalToCloud() { _cloud_value = _primitive_value; } - virtual void appendAttributesToCloud() { - appendAttribute(_primitive_value); + virtual CborError appendAttributesToCloud() { + return appendAttribute(_primitive_value); } virtual void setAttributesFromCloud() { setAttribute(_cloud_value); diff --git a/src/property/types/CloudWrapperFloat.h b/src/property/types/CloudWrapperFloat.h index d30b1d86c..db0f640cf 100644 --- a/src/property/types/CloudWrapperFloat.h +++ b/src/property/types/CloudWrapperFloat.h @@ -47,8 +47,8 @@ class CloudWrapperFloat : public CloudWrapperBase { virtual void fromLocalToCloud() { _cloud_value = _primitive_value; } - virtual void appendAttributesToCloud() { - appendAttribute(_primitive_value); + virtual CborError appendAttributesToCloud() { + return appendAttribute(_primitive_value); } virtual void setAttributesFromCloud() { setAttribute(_cloud_value); diff --git a/src/property/types/CloudWrapperInt.h b/src/property/types/CloudWrapperInt.h index db83ab1db..261c72d46 100644 --- a/src/property/types/CloudWrapperInt.h +++ b/src/property/types/CloudWrapperInt.h @@ -45,8 +45,8 @@ class CloudWrapperInt : public CloudWrapperBase { virtual void fromLocalToCloud() { _cloud_value = _primitive_value; } - virtual void appendAttributesToCloud() { - appendAttribute(_primitive_value); + virtual CborError appendAttributesToCloud() { + return appendAttribute(_primitive_value); } virtual void setAttributesFromCloud() { setAttribute(_cloud_value); diff --git a/src/property/types/CloudWrapperString.h b/src/property/types/CloudWrapperString.h index e384365ad..3e1c0a3e3 100644 --- a/src/property/types/CloudWrapperString.h +++ b/src/property/types/CloudWrapperString.h @@ -49,8 +49,8 @@ class CloudWrapperString : public CloudWrapperBase { virtual void fromLocalToCloud() { _cloud_value = _primitive_value; } - virtual void appendAttributesToCloud() { - appendAttribute(_primitive_value); + virtual CborError appendAttributesToCloud() { + return appendAttribute(_primitive_value); } virtual void setAttributesFromCloud() { setAttribute(_cloud_value); diff --git a/src/property/types/automation/CloudColoredLight.h b/src/property/types/automation/CloudColoredLight.h index d5e380118..a19aef56c 100644 --- a/src/property/types/automation/CloudColoredLight.h +++ b/src/property/types/automation/CloudColoredLight.h @@ -118,7 +118,7 @@ class CloudColoredLight : public CloudColor { virtual void fromLocalToCloud() { _cloud_value = _value; } - virtual void appendAttributesToCloud() { + virtual CborError appendAttributesToCloud() { appendAttribute(_value.swi); appendAttribute(_value.hue); appendAttribute(_value.sat); diff --git a/src/property/types/automation/CloudDimmedLight.h b/src/property/types/automation/CloudDimmedLight.h index ecb9a6d92..5b7e5d2c8 100644 --- a/src/property/types/automation/CloudDimmedLight.h +++ b/src/property/types/automation/CloudDimmedLight.h @@ -98,7 +98,7 @@ class CloudDimmedLight : public Property { _cloud_value = _value; } - virtual void appendAttributesToCloud() { + virtual CborError appendAttributesToCloud() { appendAttribute(_value.swi); // To allow visualization through color widget // Start diff --git a/src/property/types/automation/CloudTelevision.h b/src/property/types/automation/CloudTelevision.h index c96a49e45..6fcb9fb4b 100644 --- a/src/property/types/automation/CloudTelevision.h +++ b/src/property/types/automation/CloudTelevision.h @@ -213,7 +213,7 @@ class CloudTelevision : public Property { virtual void fromLocalToCloud() { _cloud_value = _value; } - virtual void appendAttributesToCloud() { + virtual CborError appendAttributesToCloud() { appendAttribute(_value.swi); appendAttribute(_value.vol); appendAttribute(_value.mut); From 39a78e647819f713f00c3f2b837b58c9a5ed50b1 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Mon, 6 Jul 2020 10:29:08 +0200 Subject: [PATCH 2/7] Directly integrate function for property encoding into CBOREncoder::encode --- src/cbor/CBOREncoder.cpp | 18 +++++++++++++++++- src/property/PropertyContainer.cpp | 17 ----------------- src/property/PropertyContainer.h | 1 - 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/cbor/CBOREncoder.cpp b/src/cbor/CBOREncoder.cpp index 5fa9b51dd..9d9ff8071 100644 --- a/src/cbor/CBOREncoder.cpp +++ b/src/cbor/CBOREncoder.cpp @@ -21,6 +21,10 @@ #include "CBOREncoder.h" +#undef max +#undef min +#include + #include "lib/tinycbor/cbor-lib.h" /****************************************************************************** @@ -40,10 +44,22 @@ int CBOREncoder::encode(PropertyContainer & property_container, uint8_t * data, * time interval may be elapsed or property may be changed * and if that's the case encode the property into the CBOR. */ - CborError err = appendChangedProperties(property_container, &arrayEncoder, lightPayload); + CborError err = CborNoError; + std::for_each(property_container.begin(), + property_container.end(), + [lightPayload, &arrayEncoder, &err](Property * p) + { + if (p->shouldBeUpdated() && p->isReadableByCloud()) + { + err = p->append(&arrayEncoder, lightPayload); + if(err != CborNoError) + return; + } + }); if ((err != CborNoError) && (err != CborErrorOutOfMemory)) return -1; + if (cbor_encoder_close_container(&encoder, &arrayEncoder) != CborNoError) return -1; diff --git a/src/property/PropertyContainer.cpp b/src/property/PropertyContainer.cpp index 8cc5f83e2..7a699ee97 100644 --- a/src/property/PropertyContainer.cpp +++ b/src/property/PropertyContainer.cpp @@ -83,23 +83,6 @@ Property * getProperty(PropertyContainer & prop_cont, int const identifier) return (*iter); } -CborError appendChangedProperties(PropertyContainer & prop_cont, CborEncoder * arrayEncoder, bool lightPayload) -{ - CborError err = CborNoError; - std::for_each(prop_cont.begin(), - prop_cont.end(), - [arrayEncoder, lightPayload, &err](Property * p) - { - if (p->shouldBeUpdated() && p->isReadableByCloud()) - { - err = p->append(arrayEncoder, lightPayload); - if(err != CborNoError) - return; - } - }); - return err; -} - void requestUpdateForAllProperties(PropertyContainer & prop_cont) { std::for_each(prop_cont.begin(), diff --git a/src/property/PropertyContainer.h b/src/property/PropertyContainer.h index 7212f3a60..f18ff8019 100644 --- a/src/property/PropertyContainer.h +++ b/src/property/PropertyContainer.h @@ -78,7 +78,6 @@ Property * getProperty(PropertyContainer & prop_cont, String const & name); Property * getProperty(PropertyContainer & prop_cont, int const identifier); -CborError appendChangedProperties(PropertyContainer & prop_cont, CborEncoder * arrayEncoder, bool lightPayload); void updateTimestampOnLocallyChangedProperties(PropertyContainer & prop_cont); void requestUpdateForAllProperties(PropertyContainer & prop_cont); void updateProperty(PropertyContainer & prop_cont, String propertyName, unsigned long cloudChangeEventTime, bool const is_sync_message, std::list * map_data_list); From bc8d6e315ad020d3346e51690ea8f42e4323508d Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Mon, 6 Jul 2020 10:39:09 +0200 Subject: [PATCH 3/7] Adding CHECK_CBOR statements where they've been previously missing --- src/property/types/automation/CloudColoredLight.h | 9 +++++---- src/property/types/automation/CloudDimmedLight.h | 7 ++++--- src/property/types/automation/CloudTelevision.h | 13 +++++++------ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/property/types/automation/CloudColoredLight.h b/src/property/types/automation/CloudColoredLight.h index a19aef56c..8d8c0519b 100644 --- a/src/property/types/automation/CloudColoredLight.h +++ b/src/property/types/automation/CloudColoredLight.h @@ -119,10 +119,11 @@ class CloudColoredLight : public CloudColor { _cloud_value = _value; } virtual CborError appendAttributesToCloud() { - appendAttribute(_value.swi); - appendAttribute(_value.hue); - appendAttribute(_value.sat); - appendAttribute(_value.bri); + CHECK_CBOR(appendAttribute(_value.swi)); + CHECK_CBOR(appendAttribute(_value.hue)); + CHECK_CBOR(appendAttribute(_value.sat)); + CHECK_CBOR(appendAttribute(_value.bri)); + return CborNoError; } virtual void setAttributesFromCloud() { setAttribute(_cloud_value.swi); diff --git a/src/property/types/automation/CloudDimmedLight.h b/src/property/types/automation/CloudDimmedLight.h index 5b7e5d2c8..1a8f78eef 100644 --- a/src/property/types/automation/CloudDimmedLight.h +++ b/src/property/types/automation/CloudDimmedLight.h @@ -104,12 +104,13 @@ class CloudDimmedLight : public Property { // Start float hue = 0; float sat = 0; - appendAttributeReal(hue, getAttributeName(".hue", '.'), encoder); - appendAttributeReal(sat, getAttributeName(".sat", '.'), encoder); - appendAttribute(_value.bri); + CHECK_CBOR(appendAttributeReal(hue, getAttributeName(".hue", '.'), encoder)); + CHECK_CBOR(appendAttributeReal(sat, getAttributeName(".sat", '.'), encoder)); + CHECK_CBOR(appendAttribute(_value.bri)); // should be only: // appendAttribute(_value.bri); // end + return CborNoError; } virtual void setAttributesFromCloud() { diff --git a/src/property/types/automation/CloudTelevision.h b/src/property/types/automation/CloudTelevision.h index 6fcb9fb4b..1d4ecd2dd 100644 --- a/src/property/types/automation/CloudTelevision.h +++ b/src/property/types/automation/CloudTelevision.h @@ -214,12 +214,13 @@ class CloudTelevision : public Property { _cloud_value = _value; } virtual CborError appendAttributesToCloud() { - appendAttribute(_value.swi); - appendAttribute(_value.vol); - appendAttribute(_value.mut); - appendAttribute((int)_value.pbc); - appendAttribute((int)_value.inp); - appendAttribute(_value.cha); + CHECK_CBOR(appendAttribute(_value.swi)); + CHECK_CBOR(appendAttribute(_value.vol)); + CHECK_CBOR(appendAttribute(_value.mut)); + CHECK_CBOR(appendAttribute((int)_value.pbc)); + CHECK_CBOR(appendAttribute((int)_value.inp)); + CHECK_CBOR(appendAttribute(_value.cha)); + return CborNoError; } virtual void setAttributesFromCloud() { setAttribute(_cloud_value.swi); From 8a38a4cb716cbe9923b677ff105964a8f93da922 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Mon, 6 Jul 2020 10:55:29 +0200 Subject: [PATCH 4/7] Changing signature of CBOREncoder::encode to allow the propagation of internal errors to the outside --- src/ArduinoIoTCloudLPWAN.cpp | 9 +++++---- src/ArduinoIoTCloudTCP.cpp | 24 ++++++++++++----------- src/cbor/CBOREncoder.cpp | 37 ++++++++++++++++++++++-------------- src/cbor/CBOREncoder.h | 2 +- 4 files changed, 42 insertions(+), 30 deletions(-) diff --git a/src/ArduinoIoTCloudLPWAN.cpp b/src/ArduinoIoTCloudLPWAN.cpp index acf840ef4..0858193aa 100644 --- a/src/ArduinoIoTCloudLPWAN.cpp +++ b/src/ArduinoIoTCloudLPWAN.cpp @@ -126,11 +126,12 @@ void ArduinoIoTCloudLPWAN::disconnect() void ArduinoIoTCloudLPWAN::sendPropertiesToCloud() { + int bytes_encoded = 0; uint8_t data[CBOR_LORA_MSG_MAX_SIZE]; - int const length = CBOREncoder::encode(_property_container, data, sizeof(data), true); - if (length > 0) { - writeProperties(data, length); - } + + if (CBOREncoder::encode(_property_container, data, sizeof(data), bytes_encoded, true) == CborNoError) + if (bytes_encoded > 0) + writeProperties(data, bytes_encoded); } int ArduinoIoTCloudLPWAN::writeProperties(const byte data[], int length) diff --git a/src/ArduinoIoTCloudTCP.cpp b/src/ArduinoIoTCloudTCP.cpp index 0a075156a..f8adfdcc5 100644 --- a/src/ArduinoIoTCloudTCP.cpp +++ b/src/ArduinoIoTCloudTCP.cpp @@ -283,18 +283,20 @@ void ArduinoIoTCloudTCP::handleMessage(int length) void ArduinoIoTCloudTCP::sendPropertiesToCloud() { + int bytes_encoded = 0; uint8_t data[MQTT_TRANSMIT_BUFFER_SIZE]; - int const length = CBOREncoder::encode(_property_container, data, sizeof(data)); - if (length > 0) - { - /* If properties have been encoded store them in the back-up buffer - * in order to allow retransmission in case of failure. - */ - _mqtt_data_len = length; - memcpy(_mqtt_data_buf, data, _mqtt_data_len); - /* Transmit the properties to the MQTT broker */ - write(_dataTopicOut, _mqtt_data_buf, _mqtt_data_len); - } + + if (CBOREncoder::encode(_property_container, data, sizeof(data), bytes_encoded, false) == CborNoError) + if (bytes_encoded > 0) + { + /* If properties have been encoded store them in the back-up buffer + * in order to allow retransmission in case of failure. + */ + _mqtt_data_len = bytes_encoded; + memcpy(_mqtt_data_buf, data, _mqtt_data_len); + /* Transmit the properties to the MQTT broker */ + write(_dataTopicOut, _mqtt_data_buf, _mqtt_data_len); + } } void ArduinoIoTCloudTCP::requestLastValue() diff --git a/src/cbor/CBOREncoder.cpp b/src/cbor/CBOREncoder.cpp index 9d9ff8071..5731756ec 100644 --- a/src/cbor/CBOREncoder.cpp +++ b/src/cbor/CBOREncoder.cpp @@ -30,39 +30,48 @@ /****************************************************************************** * PUBLIC MEMBER FUNCTIONS ******************************************************************************/ - -int CBOREncoder::encode(PropertyContainer & property_container, uint8_t * data, size_t const size, bool lightPayload) +#include +CborError CBOREncoder::encode(PropertyContainer & property_container, uint8_t * data, size_t const size, int & bytes_encoded, bool lightPayload) { + CborError error = CborNoError; CborEncoder encoder, arrayEncoder; cbor_encoder_init(&encoder, data, size, 0); - if (cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength) != CborNoError) - return -1; + error = cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength); + if (CborNoError != error) + return error; /* Check if backing storage and cloud has diverged * time interval may be elapsed or property may be changed * and if that's the case encode the property into the CBOR. */ - CborError err = CborNoError; + int num_encoded_properties = 0; std::for_each(property_container.begin(), property_container.end(), - [lightPayload, &arrayEncoder, &err](Property * p) + [lightPayload, &arrayEncoder, &error, &num_encoded_properties](Property * p) { if (p->shouldBeUpdated() && p->isReadableByCloud()) { - err = p->append(&arrayEncoder, lightPayload); - if(err != CborNoError) + error = p->append(&arrayEncoder, lightPayload); + if(error == CborNoError) + num_encoded_properties++; + else return; } }); - if ((err != CborNoError) && (err != CborErrorOutOfMemory)) - return -1; + if ((CborNoError != error) && + (CborErrorOutOfMemory != error)) + return error; + error = cbor_encoder_close_container(&encoder, &arrayEncoder); + if (CborNoError != error) + return error; - if (cbor_encoder_close_container(&encoder, &arrayEncoder) != CborNoError) - return -1; + if (num_encoded_properties > 0) + bytes_encoded = cbor_encoder_get_buffer_size(&encoder, data); + else + bytes_encoded = 0; - int const bytes_encoded = cbor_encoder_get_buffer_size(&encoder, data); - return bytes_encoded; + return CborNoError; } diff --git a/src/cbor/CBOREncoder.h b/src/cbor/CBOREncoder.h index 90aff85c9..abaa9d4b7 100644 --- a/src/cbor/CBOREncoder.h +++ b/src/cbor/CBOREncoder.h @@ -35,7 +35,7 @@ class CBOREncoder /* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */ /* 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*/ - static int encode(PropertyContainer & property_container, uint8_t * data, size_t const size, bool lightPayload = false); + static CborError encode(PropertyContainer & property_container, uint8_t * data, size_t const size, int & bytes_encoded, bool lightPayload = false); private: From 13dda3d086c0b2082c709da176910d58998e0f92 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Mon, 6 Jul 2020 10:56:10 +0200 Subject: [PATCH 5/7] Adjusting test code for changed signature of CBOREncoder::encode --- extras/test/src/util/CBORTestUtil.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extras/test/src/util/CBORTestUtil.cpp b/extras/test/src/util/CBORTestUtil.cpp index b2bd1f751..04ad7b3f0 100644 --- a/extras/test/src/util/CBORTestUtil.cpp +++ b/extras/test/src/util/CBORTestUtil.cpp @@ -25,13 +25,13 @@ namespace cbor **************************************************************************************/ std::vector encode(PropertyContainer & property_container, bool lightPayload) { + int bytes_encoded = 0; uint8_t buf[200] = {0}; - int const bytes_buf = CBOREncoder::encode(property_container, buf, 200, lightPayload); - if (bytes_buf == -1) { + + if (CBOREncoder::encode(property_container, buf, 200, bytes_encoded, lightPayload) == CborNoError) + return std::vector(buf, buf + bytes_encoded); + else return std::vector(); - } else { - return std::vector(buf, buf + bytes_buf); - } } void print(std::vector const & vect) { From 7743c9888ee48f03f43f510afd9cac75e1d12ac3 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Mon, 6 Jul 2020 11:05:32 +0200 Subject: [PATCH 6/7] Extract CHECK_CBOR macro to also use it within CBOREncoder::encode --- src/cbor/CBOREncoder.cpp | 12 ++++-------- src/cbor/lib/tinycbor/cbor-lib.h | 18 ++++++++++++++++++ src/property/Property.h | 8 -------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/cbor/CBOREncoder.cpp b/src/cbor/CBOREncoder.cpp index 5731756ec..399d521fa 100644 --- a/src/cbor/CBOREncoder.cpp +++ b/src/cbor/CBOREncoder.cpp @@ -30,22 +30,20 @@ /****************************************************************************** * PUBLIC MEMBER FUNCTIONS ******************************************************************************/ -#include + CborError CBOREncoder::encode(PropertyContainer & property_container, uint8_t * data, size_t const size, int & bytes_encoded, bool lightPayload) { - CborError error = CborNoError; CborEncoder encoder, arrayEncoder; cbor_encoder_init(&encoder, data, size, 0); - error = cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength); - if (CborNoError != error) - return error; + CHECK_CBOR(cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength)); /* Check if backing storage and cloud has diverged * time interval may be elapsed or property may be changed * and if that's the case encode the property into the CBOR. */ + CborError error = CborNoError; int num_encoded_properties = 0; std::for_each(property_container.begin(), property_container.end(), @@ -64,9 +62,7 @@ CborError CBOREncoder::encode(PropertyContainer & property_container, uint8_t * (CborErrorOutOfMemory != error)) return error; - error = cbor_encoder_close_container(&encoder, &arrayEncoder); - if (CborNoError != error) - return error; + CHECK_CBOR(cbor_encoder_close_container(&encoder, &arrayEncoder)); if (num_encoded_properties > 0) bytes_encoded = cbor_encoder_get_buffer_size(&encoder, data); diff --git a/src/cbor/lib/tinycbor/cbor-lib.h b/src/cbor/lib/tinycbor/cbor-lib.h index 0ac58a17d..d66553bf8 100644 --- a/src/cbor/lib/tinycbor/cbor-lib.h +++ b/src/cbor/lib/tinycbor/cbor-lib.h @@ -1,6 +1,24 @@ #ifndef CBOR_LIB_H #define CBOR_LIB_H +/****************************************************************************** + INCLUDE + ******************************************************************************/ + #include "src/cbor.h" +/****************************************************************************** + * DEFINE + ******************************************************************************/ + +#ifndef CHECK_CBOR + #define CHECK_CBOR(expr) \ + do { \ + CborError error = CborNoError; \ + error = (expr); \ + if (CborNoError != error) \ + return error; \ + } while(0); +#endif /* CHECK_CBOR */ + #endif \ No newline at end of file diff --git a/src/property/Property.h b/src/property/Property.h index 864d44224..4b5900782 100644 --- a/src/property/Property.h +++ b/src/property/Property.h @@ -40,14 +40,6 @@ DEFINE ******************************************************************************/ -#define CHECK_CBOR(expr) \ - do { \ - CborError error = CborNoError; \ - error = (expr); \ - if (CborNoError != error) \ - return error; \ - } while(0); - #define appendAttributesToCloud() appendAttributesToCloudReal(CborEncoder *encoder) #define appendAttribute(x) appendAttributeReal(x, getAttributeName(#x, '.'), encoder) #define setAttribute(x) setAttributeReal(x, getAttributeName(#x, '.')) From b4cc9777665e72a57b4d68925ce3b63fb075f969 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Mon, 6 Jul 2020 11:48:33 +0200 Subject: [PATCH 7/7] Adding test code for testing scenario where the number of encoded properties exceeds the capacity of the MQTT payload buffer --- extras/test/src/test_encode.cpp | 57 ++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/extras/test/src/test_encode.cpp b/extras/test/src/test_encode.cpp index 063a97daa..24df1f0ac 100644 --- a/extras/test/src/test_encode.cpp +++ b/extras/test/src/test_encode.cpp @@ -20,7 +20,7 @@ TEST CODE **************************************************************************************/ -SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") { +SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode-1]") { /************************************************************************************/ WHEN("A 'bool' property is added") { @@ -381,4 +381,59 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") } /************************************************************************************/ + + WHEN("The size of the encoded properties is exceeding the CBOR buffer size") { + GIVEN("CloudProtocol::V2") { + PropertyContainer property_container; + + CloudString str_0; str_0 = "This string is 30 bytes long."; + CloudString str_1; str_1 = "This string is 30 bytes long."; + CloudString str_2; str_2 = "This string is 30 bytes long."; + CloudString str_3; str_3 = "This string is 30 bytes long."; + CloudString str_4; str_4 = "This string is 30 bytes long."; + CloudString str_5; str_5 = "This string is 30 bytes long."; + CloudString str_6; str_6 = "This string is 30 bytes long."; + CloudString str_7; str_7 = "This string is 30 bytes long."; + CloudString str_8; str_8 = "This string is 30 bytes long."; + CloudString str_9; str_9 = "This string is 30 bytes long."; + + addPropertyToContainer(property_container, str_0, "str_0", Permission::ReadWrite); + addPropertyToContainer(property_container, str_1, "str_1", Permission::ReadWrite); + addPropertyToContainer(property_container, str_2, "str_2", Permission::ReadWrite); + addPropertyToContainer(property_container, str_3, "str_3", Permission::ReadWrite); + addPropertyToContainer(property_container, str_4, "str_4", Permission::ReadWrite); + addPropertyToContainer(property_container, str_5, "str_5", Permission::ReadWrite); + addPropertyToContainer(property_container, str_6, "str_6", Permission::ReadWrite); + addPropertyToContainer(property_container, str_7, "str_7", Permission::ReadWrite); + addPropertyToContainer(property_container, str_8, "str_8", Permission::ReadWrite); + addPropertyToContainer(property_container, str_9, "str_9", Permission::ReadWrite); + + /* Due to the size if the encoded properties exceeding 256 bytes if encoded all at + * once they are encoded in subsequent calls to CBOREncoder::encode. + */ + + /* [{0: "str_0", 3: "This string is 30 bytes long."}, {0: "str_1", 3: "This string is 30 bytes long."}, {0: "str_2", 3: "This string is 30 bytes long."}, {0: "str_3", 3: "This string is 30 bytes long."}] + * = 9F A2 00 65 73 74 72 5F 30 03 78 1D 54 68 69 73 20 73 74 72 69 6E 67 20 69 73 20 33 30 20 62 79 74 65 73 20 6C 6F 6E 67 2E A2 00 65 73 74 72 5F 31 03 78 1D 54 68 69 73 20 73 74 72 69 6E 67 20 69 73 20 33 30 20 62 79 74 65 73 20 6C 6F 6E 67 2E A2 00 65 73 74 72 5F 32 03 78 1D 54 68 69 73 20 73 74 72 69 6E 67 20 69 73 20 33 30 20 62 79 74 65 73 20 6C 6F 6E 67 2E A2 00 65 73 74 72 5F 33 03 78 1D 54 68 69 73 20 73 74 72 69 6E 67 20 69 73 20 33 30 20 62 79 74 65 73 20 6C 6F 6E 67 2E FF + */ + std::vector const expected_1 = {0x9F, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x30, 0x03, 0x78, 0x1D, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20, 0x33, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x2E, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x31, 0x03, 0x78, 0x1D, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20, 0x33, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x2E, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x32, 0x03, 0x78, 0x1D, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20, 0x33, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x2E, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x33, 0x03, 0x78, 0x1D, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20, 0x33, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x2E, 0xFF}; + std::vector const actual_1 = cbor::encode(property_container); + REQUIRE(actual_1 == expected_1); + + /* [{0: "str_4", 3: "This string is 30 bytes long."}, {0: "str_5", 3: "This string is 30 bytes long."}, {0: "str_6", 3: "This string is 30 bytes long."}, {0: "str_7", 3: "This string is 30 bytes long."}] + * = 9F A2 00 65 73 74 72 5F 34 03 78 1D 54 68 69 73 20 73 74 72 69 6E 67 20 69 73 20 33 30 20 62 79 74 65 73 20 6C 6F 6E 67 2E A2 00 65 73 74 72 5F 35 03 78 1D 54 68 69 73 20 73 74 72 69 6E 67 20 69 73 20 33 30 20 62 79 74 65 73 20 6C 6F 6E 67 2E A2 00 65 73 74 72 5F 36 03 78 1D 54 68 69 73 20 73 74 72 69 6E 67 20 69 73 20 33 30 20 62 79 74 65 73 20 6C 6F 6E 67 2E A2 00 65 73 74 72 5F 37 03 78 1D 54 68 69 73 20 73 74 72 69 6E 67 20 69 73 20 33 30 20 62 79 74 65 73 20 6C 6F 6E 67 2E FF + */ + std::vector const expected_2 = {0x9F, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x34, 0x03, 0x78, 0x1D, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20, 0x33, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x2E, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x35, 0x03, 0x78, 0x1D, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20, 0x33, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x2E, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x36, 0x03, 0x78, 0x1D, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20, 0x33, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x2E, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x37, 0x03, 0x78, 0x1D, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20, 0x33, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x2E, 0xFF}; + std::vector const actual_2 = cbor::encode(property_container); + REQUIRE(actual_2 == expected_2); + + /* [{0: "str_8", 3: "This string is 30 bytes long."}, {0: "str_9", 3: "This string is 30 bytes long."}] + * = 9F A2 00 65 73 74 72 5F 38 03 78 1D 54 68 69 73 20 73 74 72 69 6E 67 20 69 73 20 33 30 20 62 79 74 65 73 20 6C 6F 6E 67 2E A2 00 65 73 74 72 5F 39 03 78 1D 54 68 69 73 20 73 74 72 69 6E 67 20 69 73 20 33 30 20 62 79 74 65 73 20 6C 6F 6E 67 2E FF + */ + std::vector const expected_3 = {0x9F, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x38, 0x03, 0x78, 0x1D, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20, 0x33, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x2E, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x39, 0x03, 0x78, 0x1D, 0x54, 0x68, 0x69, 0x73, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x69, 0x73, 0x20, 0x33, 0x30, 0x20, 0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6C, 0x6F, 0x6E, 0x67, 0x2E, 0xFF}; + std::vector const actual_3 = cbor::encode(property_container); + REQUIRE(actual_3 == expected_3); + } + } + + /************************************************************************************/ }