Implemented cloud nvs
This commit is contained in:
@ -241,6 +241,7 @@ set(headers
|
|||||||
texthelpers/wifistatexthelpers.h
|
texthelpers/wifistatexthelpers.h
|
||||||
time_bobbycar.h
|
time_bobbycar.h
|
||||||
types.h
|
types.h
|
||||||
|
typeutils.h
|
||||||
udpcloud.h
|
udpcloud.h
|
||||||
unifiedmodelmode.h
|
unifiedmodelmode.h
|
||||||
utils.h
|
utils.h
|
||||||
|
201
main/cloud.cpp
201
main/cloud.cpp
@ -20,6 +20,7 @@
|
|||||||
#include "bobbyerrorhandler.h"
|
#include "bobbyerrorhandler.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "newsettings.h"
|
#include "newsettings.h"
|
||||||
|
#include "typeutils.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
@ -30,15 +31,15 @@ espchrono::millis_clock::time_point lastCreateTry;
|
|||||||
espchrono::millis_clock::time_point lastStartTry;
|
espchrono::millis_clock::time_point lastStartTry;
|
||||||
std::string cloudBuffer;
|
std::string cloudBuffer;
|
||||||
|
|
||||||
|
namespace {
|
||||||
std::optional<espchrono::millis_clock::time_point> lastCloudCollect;
|
std::optional<espchrono::millis_clock::time_point> lastCloudCollect;
|
||||||
std::optional<espchrono::millis_clock::time_point> lastCloudSend;
|
std::optional<espchrono::millis_clock::time_point> lastCloudSend;
|
||||||
std::optional<espchrono::millis_clock::time_point> lastHeartbeat;
|
std::optional<espchrono::millis_clock::time_point> lastHeartbeat;
|
||||||
|
|
||||||
bool hasAnnouncedItself{};
|
bool hasAnnouncedItself{};
|
||||||
|
|
||||||
namespace {
|
|
||||||
constexpr const char * const TAG = "BOBBYCLOUD";
|
constexpr const char * const TAG = "BOBBYCLOUD";
|
||||||
constexpr const auto json_document_size = 256;
|
constexpr const auto json_document_size = 1024;
|
||||||
StaticJsonDocument<json_document_size> doc;
|
StaticJsonDocument<json_document_size> doc;
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
@ -66,149 +67,89 @@ typename std::enable_if<
|
|||||||
!std::is_same_v<T, LedstripAnimation> &&
|
!std::is_same_v<T, LedstripAnimation> &&
|
||||||
!std::is_same_v<T, HandbremseMode> &&
|
!std::is_same_v<T, HandbremseMode> &&
|
||||||
!std::is_same_v<T, CloudMode> &&
|
!std::is_same_v<T, CloudMode> &&
|
||||||
!std::is_same_v<T, BobbyQuickActions>
|
!std::is_same_v<T, BobbyQuickActions> &&
|
||||||
|
!std::is_same_v<T, BatteryCellType>
|
||||||
, void>::type
|
, void>::type
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
toArduinoJson(std::string_view key, T value, JsonObject &object)
|
||||||
{
|
{
|
||||||
doc[key.data()] = nullptr;
|
object["name"] = key;
|
||||||
|
object["value"] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
std::is_same_v<T, bool>
|
std::is_same_v<T, bool> ||
|
||||||
, void>::type
|
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
|
||||||
{
|
|
||||||
doc[key.data()] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
typename std::enable_if<
|
|
||||||
!std::is_same_v<T, bool> &&
|
|
||||||
std::is_integral_v<T>
|
std::is_integral_v<T>
|
||||||
, void>::type
|
, void>::type
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
toArduinoJson(std::string_view key, T value, JsonObject &object)
|
||||||
{
|
{
|
||||||
doc[key.data()] = value;
|
object["name"] = key;
|
||||||
|
object["value"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
is_duration_v<T>
|
is_duration_v<T>
|
||||||
, void>::type
|
, void>::type
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
toArduinoJson(std::string_view key, T value, JsonObject &object)
|
||||||
{
|
{
|
||||||
doc[key.data()] = value.count();
|
object["name"] = key;
|
||||||
|
object["value"] = value.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
std::is_same_v<T, std::string>
|
std::is_same_v<T, std::string>
|
||||||
, void>::type
|
, void>::type
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
toArduinoJson(std::string_view key, T value, JsonObject &object)
|
||||||
{
|
{
|
||||||
doc[key.data()] = value.data();
|
object["name"] = key;
|
||||||
|
object["value"] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
std::is_same_v<T, wifi_stack::ip_address_t>
|
std::is_same_v<T, wifi_stack::ip_address_t> ||
|
||||||
|
std::is_same_v<T, wifi_stack::mac_t> ||
|
||||||
|
std::is_same_v<T, wifi_auth_mode_t>
|
||||||
, void>::type
|
, void>::type
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
toArduinoJson(std::string_view key, T value, JsonObject &object)
|
||||||
{
|
{
|
||||||
doc[key.data()] = wifi_stack::toString(value);
|
object["name"] = key;
|
||||||
}
|
object["value"] = wifi_stack::toString(value);
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same_v<T, wifi_stack::mac_t>
|
|
||||||
, void>::type
|
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
|
||||||
{
|
|
||||||
doc[key.data()] = wifi_stack::toString(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
std::is_same_v<T, std::optional<wifi_stack::mac_t>>
|
std::is_same_v<T, std::optional<wifi_stack::mac_t>>
|
||||||
, void>::type
|
, void>::type
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
toArduinoJson(std::string_view key, T value, JsonObject &object)
|
||||||
{
|
{
|
||||||
doc[key.data()] = value ? wifi_stack::toString(*value) : std::string{};
|
object["name"] = key;
|
||||||
|
if (value)
|
||||||
|
object["value"] = wifi_stack::toString(*value);
|
||||||
|
else
|
||||||
|
object["value"] = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
std::is_same_v<T, wifi_auth_mode_t>
|
std::is_same_v<T, sntp_sync_mode_t> ||
|
||||||
|
std::is_same_v<T, espchrono::DayLightSavingMode> ||
|
||||||
|
std::is_same_v<T, OtaAnimationModes> ||
|
||||||
|
std::is_same_v<T, LedstripAnimation> ||
|
||||||
|
std::is_same_v<T, HandbremseMode> ||
|
||||||
|
std::is_same_v<T, BobbyQuickActions> ||
|
||||||
|
std::is_same_v<T, CloudMode> ||
|
||||||
|
std::is_same_v<T, BatteryCellType>
|
||||||
, void>::type
|
, void>::type
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
toArduinoJson(std::string_view key, T value, JsonObject &object)
|
||||||
{
|
{
|
||||||
doc[key.data()] = wifi_stack::toString(value);
|
object["name"] = key;
|
||||||
}
|
object["value"] = std::to_underlying(value);
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same_v<T, sntp_sync_mode_t>
|
|
||||||
, void>::type
|
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
|
||||||
{
|
|
||||||
doc[key.data()] = std::to_underlying(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same_v<T, espchrono::DayLightSavingMode>
|
|
||||||
, void>::type
|
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
|
||||||
{
|
|
||||||
doc[key.data()] = std::to_underlying(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same_v<T, OtaAnimationModes>
|
|
||||||
, void>::type
|
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
|
||||||
{
|
|
||||||
doc[key.data()] = std::to_underlying(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same_v<T, LedstripAnimation>
|
|
||||||
, void>::type
|
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
|
||||||
{
|
|
||||||
doc[key.data()] = std::to_underlying(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same_v<T, HandbremseMode>
|
|
||||||
, void>::type
|
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
|
||||||
{
|
|
||||||
doc[key.data()] = std::to_underlying(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same_v<T, BobbyQuickActions>
|
|
||||||
, void>::type
|
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
|
||||||
{
|
|
||||||
doc[key.data()] = std::to_underlying(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
typename std::enable_if<
|
|
||||||
std::is_same_v<T, CloudMode>
|
|
||||||
, void>::type
|
|
||||||
toArduinoJson(std::string_view key, T value, StaticJsonDocument<json_document_size> &doc)
|
|
||||||
{
|
|
||||||
doc[key.data()] = std::to_underlying(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setter
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
typename std::enable_if<
|
typename std::enable_if<
|
||||||
@ -225,7 +166,8 @@ typename std::enable_if<
|
|||||||
!std::is_same_v<T, LedstripAnimation> &&
|
!std::is_same_v<T, LedstripAnimation> &&
|
||||||
!std::is_same_v<T, HandbremseMode> &&
|
!std::is_same_v<T, HandbremseMode> &&
|
||||||
!std::is_same_v<T, CloudMode> &&
|
!std::is_same_v<T, CloudMode> &&
|
||||||
!std::is_same_v<T, BobbyQuickActions>
|
!std::is_same_v<T, BobbyQuickActions> &&
|
||||||
|
!std::is_same_v<T, BatteryCellType>
|
||||||
, tl::expected<void, std::string>>::type
|
, tl::expected<void, std::string>>::type
|
||||||
set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
||||||
{
|
{
|
||||||
@ -313,7 +255,8 @@ typename std::enable_if<
|
|||||||
std::is_same_v<T, LedstripAnimation> ||
|
std::is_same_v<T, LedstripAnimation> ||
|
||||||
std::is_same_v<T, HandbremseMode> ||
|
std::is_same_v<T, HandbremseMode> ||
|
||||||
std::is_same_v<T, BobbyQuickActions> ||
|
std::is_same_v<T, BobbyQuickActions> ||
|
||||||
std::is_same_v<T, CloudMode>
|
std::is_same_v<T, CloudMode> ||
|
||||||
|
std::is_same_v<T, BatteryCellType>
|
||||||
, tl::expected<void, std::string>>::type
|
, tl::expected<void, std::string>>::type
|
||||||
set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
||||||
{
|
{
|
||||||
@ -325,13 +268,27 @@ set_config(ConfigWrapper<T> &config, std::string_view newValue)
|
|||||||
|
|
||||||
void send_config(uint32_t skipCount)
|
void send_config(uint32_t skipCount)
|
||||||
{
|
{
|
||||||
doc.clear();
|
|
||||||
doc["type"] = "config";
|
|
||||||
|
|
||||||
if (!cloudClient.is_connected())
|
if (!cloudClient.is_connected())
|
||||||
return;
|
return;
|
||||||
|
doc.clear();
|
||||||
|
|
||||||
|
{
|
||||||
|
const std::string body = fmt::format(R"({{"type":"configCount","count":{}}})", configs.getConfigCount());
|
||||||
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(
|
||||||
|
espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
|
const auto result_size = cloudClient.send_text(body, timeout);
|
||||||
|
if (result_size != body.size()) {
|
||||||
|
ESP_LOGE(TAG, "send_text failed: %d", result_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doc["type"] = "config";
|
||||||
|
JsonArray configsArray = doc.createNestedArray("configs");
|
||||||
|
|
||||||
bool stop{false};
|
bool stop{false};
|
||||||
|
bool has_overflowed{false};
|
||||||
|
|
||||||
|
size_t i{0};
|
||||||
|
|
||||||
configs.callForEveryConfig([&](auto &config) {
|
configs.callForEveryConfig([&](auto &config) {
|
||||||
if (skipCount > 0)
|
if (skipCount > 0)
|
||||||
@ -344,24 +301,43 @@ void send_config(uint32_t skipCount)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const std::string_view nvsName{config.nvsName()};
|
const std::string_view nvsName{config.nvsName()};
|
||||||
toArduinoJson(nvsName, config.value(), doc);
|
|
||||||
|
i++;
|
||||||
|
|
||||||
|
JsonObject configObject = configsArray.createNestedObject();
|
||||||
|
toArduinoJson(nvsName, config.value(), configObject);
|
||||||
|
configObject["type"] = typeutils::t_to_str<decltype(config.value())>::str;
|
||||||
|
|
||||||
if (doc.overflowed())
|
if (doc.overflowed())
|
||||||
{
|
{
|
||||||
|
has_overflowed = true;
|
||||||
// send data, clear doc and try again
|
// send data, clear doc and try again
|
||||||
std::string body;
|
std::string body;
|
||||||
doc.remove(nvsName);
|
configsArray.remove(i-1);
|
||||||
serializeJson(doc, body);
|
serializeJson(doc, body);
|
||||||
ESP_LOGI(TAG, "body_size: %d, heap free: %d, stack free: %d", body.size(), esp_get_free_heap_size(), uxTaskGetStackHighWaterMark(nullptr));
|
|
||||||
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
const auto result_size = cloudClient.send_text(body, timeout);
|
const auto result_size = cloudClient.send_text(body, timeout);
|
||||||
if (result_size != body.size())
|
if (result_size != body.size())
|
||||||
{
|
{
|
||||||
ESP_LOGE(TAG, "send_text failed: %d", result_size);
|
ESP_LOGE(TAG, "send_text failed: %d", result_size);
|
||||||
}
|
}
|
||||||
doc["type"] = "config";
|
|
||||||
stop = true;
|
stop = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!has_overflowed)
|
||||||
|
{
|
||||||
|
std::string body;
|
||||||
|
doc["last"] = true;
|
||||||
|
doc["type"] = "lastConfig";
|
||||||
|
serializeJson(doc, body);
|
||||||
|
const auto timeout = std::chrono::ceil<espcpputils::ticks>(espchrono::milliseconds32{configs.cloudSettings.cloudTransmitTimeout.value()}).count();
|
||||||
|
const auto result_size = cloudClient.send_text(body, timeout);
|
||||||
|
if (result_size != body.size())
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "send_text failed: %d", result_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_single_config(const std::string &nvsName)
|
void send_single_config(const std::string &nvsName)
|
||||||
@ -370,11 +346,14 @@ void send_single_config(const std::string &nvsName)
|
|||||||
return;
|
return;
|
||||||
doc.clear();
|
doc.clear();
|
||||||
doc["type"] = "singleConfig";
|
doc["type"] = "singleConfig";
|
||||||
|
|
||||||
bool success{false};
|
bool success{false};
|
||||||
configs.callForEveryConfig([&](auto &config) {
|
configs.callForEveryConfig([&](auto &config) {
|
||||||
if (config.nvsName() == nvsName)
|
if (config.nvsName() == nvsName)
|
||||||
{
|
{
|
||||||
toArduinoJson(nvsName, config.value(), doc);
|
JsonObject configObject = doc.createNestedObject("config");
|
||||||
|
toArduinoJson(nvsName, config.value(), configObject);
|
||||||
|
configObject["type"] = typeutils::t_to_str<decltype(config.value())>::str;
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -852,6 +852,15 @@ public:
|
|||||||
std::ref<ConfiguredFeatureFlag>(feature.webserver_disable_lock)
|
std::ref<ConfiguredFeatureFlag>(feature.webserver_disable_lock)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr size_t getConfigCount() const
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
#define HELPER(x) count++;
|
||||||
|
NEW_SETTINGS(HELPER)
|
||||||
|
#undef HELPER
|
||||||
|
return count;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extern ConfigManager<ConfigContainer> configs;
|
extern ConfigManager<ConfigContainer> configs;
|
||||||
|
Reference in New Issue
Block a user