Files
bobbycar-boardcomputer-firm…/main/settingspersister.cpp
2022-06-25 22:16:21 +02:00

367 lines
13 KiB
C++

#include "settingspersister.h"
// system includes
#include <type_traits>
// esp-idf includes
#include <esp_log.h>
#include <nvs_flash.h>
#include <lwip/apps/snmp.h>
#include <esp_sntp.h>
// 3rdparty lib includes
#include <fmt/core.h>
#include <cpputils.h>
#include <espchrono.h>
// local includes
#include "profilesettings.h"
#ifdef FEATURE_BLUETOOTH
#include "bluetoothmode.h"
#endif
#include "unifiedmodelmode.h"
#include "globals.h"
bool SettingsPersister::init()
{
if (esp_err_t result = nvs_flash_init();
cpputils::is_in(result, ESP_ERR_NVS_NO_FREE_PAGES, ESP_ERR_NVS_NEW_VERSION_FOUND))
{
ESP_LOGE("BOBBY", "nvs_flash_init() failed with %s, trying to erase...", esp_err_to_name(result));
return erase();
}
else if (result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_flash_init() failed with %s", esp_err_to_name(result));
return false;
}
return true;
}
bool SettingsPersister::erase()
{
closeProfile();
bool result{true};
if (esp_err_t result = nvs_flash_erase(); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_flash_erase() failed with %s", esp_err_to_name(result));
result = false;
}
if (esp_err_t result = nvs_flash_init(); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_flash_init() failed with %s", esp_err_to_name(result));
result = false;
}
return result;
}
bool SettingsPersister::openProfile(uint8_t index)
{
closeProfile();
nvs_handle handle;
const auto name = fmt::format("bobbycar{}", index);
if (esp_err_t result = nvs_open(name.c_str(), NVS_READWRITE, &handle); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_open() PROFILE %s failed with %s", name.c_str(), esp_err_to_name(result));
return false;
}
m_profile = {handle, index};
return true;
}
void SettingsPersister::closeProfile()
{
if (!m_profile)
return;
nvs_close(m_profile->handle);
m_profile = std::nullopt;
}
template<typename T> struct nvsGetterHelper;
template<> struct nvsGetterHelper<int8_t> { static constexpr auto nvs_get = &nvs_get_i8; };
template<> struct nvsGetterHelper<uint8_t> { static constexpr auto nvs_get = &nvs_get_u8; };
template<> struct nvsGetterHelper<int16_t> { static constexpr auto nvs_get = &nvs_get_i16; };
template<> struct nvsGetterHelper<uint16_t> { static constexpr auto nvs_get = &nvs_get_u16; };
template<> struct nvsGetterHelper<int32_t> { static constexpr auto nvs_get = &nvs_get_i32; };
template<> struct nvsGetterHelper<uint32_t> { static constexpr auto nvs_get = &nvs_get_u32; };
template<> struct nvsGetterHelper<std::string> { static esp_err_t nvs_get(nvs_handle handle, const char* key, std::string* out_value)
{
size_t length;
if (const esp_err_t result = nvs_get_str(handle, key, nullptr, &length); result != ESP_OK)
return result;
char buf[length];
if (const esp_err_t result = nvs_get_str(handle, key, buf, &length); result != ESP_OK)
return result;
*out_value = buf;
return ESP_OK;
}};
template<> struct nvsGetterHelper<bool> { static esp_err_t nvs_get(nvs_handle handle, const char* key, bool* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = tempValue;
return err;
}};
template<> struct nvsGetterHelper<bobbycar::protocol::ControlType> { static esp_err_t nvs_get(nvs_handle handle, const char* key, bobbycar::protocol::ControlType* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = bobbycar::protocol::ControlType(tempValue);
return err;
}};
template<> struct nvsGetterHelper<bobbycar::protocol::ControlMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, bobbycar::protocol::ControlMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = bobbycar::protocol::ControlMode(tempValue);
return err;
}};
template<> struct nvsGetterHelper<LarsmModeMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, LarsmModeMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = LarsmModeMode(tempValue);
return err;
}};
#ifdef FEATURE_BLUETOOTH
template<> struct nvsGetterHelper<BluetoothMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, BluetoothMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = BluetoothMode(tempValue);
return err;
}};
#endif
template<> struct nvsGetterHelper<UnifiedModelMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, UnifiedModelMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = UnifiedModelMode(tempValue);
return err;
}};
template<> struct nvsGetterHelper<wifi_mode_t> { static esp_err_t nvs_get(nvs_handle handle, const char* key, wifi_mode_t* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = wifi_mode_t(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::DayLightSavingMode> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::DayLightSavingMode* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::DayLightSavingMode(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::milliseconds32> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::milliseconds32* out_value)
{
int32_t tempValue;
esp_err_t err = nvs_get_i32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::milliseconds32(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::seconds32> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::seconds32* out_value)
{
int32_t tempValue;
esp_err_t err = nvs_get_i32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::seconds32(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::minutes32> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::minutes32* out_value)
{
int32_t tempValue;
esp_err_t err = nvs_get_i32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::minutes32(tempValue);
return err;
}};
template<> struct nvsGetterHelper<espchrono::hours32> { static esp_err_t nvs_get(nvs_handle handle, const char* key, espchrono::hours32* out_value)
{
int32_t tempValue;
esp_err_t err = nvs_get_i32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = espchrono::hours32(tempValue);
return err;
}};
template<> struct nvsGetterHelper<sntp_sync_mode_t> { static esp_err_t nvs_get(nvs_handle handle, const char* key, sntp_sync_mode_t* out_value)
{
uint8_t tempValue;
esp_err_t err = nvs_get_u8(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = sntp_sync_mode_t(tempValue);
return err;
}};
template<> struct nvsGetterHelper<std::array<int8_t, 4>> { static esp_err_t nvs_get(nvs_handle handle, const char* key, std::array<int8_t, 4>* out_value)
{
uint32_t tempValue;
esp_err_t err = nvs_get_u32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = std::bit_cast<std::array<int8_t, 4>>(tempValue);
return err;
}};
template<> struct nvsGetterHelper<std::array<uint8_t, 4>> { static esp_err_t nvs_get(nvs_handle handle, const char* key, std::array<uint8_t, 4>* out_value)
{
uint32_t tempValue;
esp_err_t err = nvs_get_u32(handle, key, &tempValue);
if (err == ESP_OK)
*out_value = std::bit_cast<std::array<uint8_t, 4>>(tempValue);
return err;
}};
template<typename T>
bool SettingsPersister::load(T &settings)
{
bool result{true};
if (m_profile)
{
profileSettings.executeForEveryProfileSetting([&](const char *key, auto &value)
{
if (esp_err_t result = nvsGetterHelper<std::decay_t<decltype(value)>>::nvs_get(m_profile->handle, key, &value); result != ESP_OK)
{
if (result != ESP_ERR_NVS_NOT_FOUND)
ESP_LOGE("BOBBY", "nvs_get() PROFILE %s failed with %s", key, esp_err_to_name(result));
result = false;
}
});
}
else
{
ESP_LOGW("BOBBY", "no profile open currently!");
result = false;
}
return result;
}
template bool SettingsPersister::load<ProfileSettings>(ProfileSettings &profileSettings);
template<typename T> struct nvsSetterHelper;
template<> struct nvsSetterHelper<int8_t> { static constexpr auto nvs_set = &nvs_set_i8; };
template<> struct nvsSetterHelper<uint8_t> { static constexpr auto nvs_set = &nvs_set_u8; };
template<> struct nvsSetterHelper<int16_t> { static constexpr auto nvs_set = &nvs_set_i16; };
template<> struct nvsSetterHelper<uint16_t> { static constexpr auto nvs_set = &nvs_set_u16; };
template<> struct nvsSetterHelper<int32_t> { static constexpr auto nvs_set = &nvs_set_i32; };
template<> struct nvsSetterHelper<uint32_t> { static constexpr auto nvs_set = &nvs_set_u32; };
template<> struct nvsSetterHelper<bool> { static constexpr auto nvs_set = &nvs_set_u8; };
template<> struct nvsSetterHelper<std::string> { static esp_err_t nvs_set(nvs_handle handle, const char* key, const std::string &value)
{
return nvs_set_str(handle, key, value.c_str());
}};
template<> struct nvsSetterHelper<bobbycar::protocol::ControlType> { static esp_err_t nvs_set(nvs_handle handle, const char* key, bobbycar::protocol::ControlType value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<bobbycar::protocol::ControlMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, bobbycar::protocol::ControlMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<LarsmModeMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, LarsmModeMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
#ifdef FEATURE_BLUETOOTH
template<> struct nvsSetterHelper<BluetoothMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, BluetoothMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
#endif
template<> struct nvsSetterHelper<UnifiedModelMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, UnifiedModelMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<wifi_mode_t> { static esp_err_t nvs_set(nvs_handle handle, const char* key, wifi_mode_t value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<espchrono::DayLightSavingMode> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::DayLightSavingMode value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<espchrono::milliseconds32> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::milliseconds32 value)
{
return nvs_set_i32(handle, key, value.count());
}};
template<> struct nvsSetterHelper<espchrono::seconds32> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::seconds32 value)
{
return nvs_set_i32(handle, key, value.count());
}};
template<> struct nvsSetterHelper<espchrono::minutes32> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::minutes32 value)
{
return nvs_set_i32(handle, key, value.count());
}};
template<> struct nvsSetterHelper<espchrono::hours32> { static esp_err_t nvs_set(nvs_handle handle, const char* key, espchrono::hours32 value)
{
return nvs_set_i32(handle, key, value.count());
}};
template<> struct nvsSetterHelper<sntp_sync_mode_t> { static esp_err_t nvs_set(nvs_handle handle, const char* key, sntp_sync_mode_t value)
{
return nvs_set_u8(handle, key, uint8_t(value));
}};
template<> struct nvsSetterHelper<std::array<int8_t, 4>> { static esp_err_t nvs_set(nvs_handle handle, const char* key, std::array<int8_t, 4> value)
{
return nvs_set_u32(handle, key, std::bit_cast<uint32_t>(value));
}};
template<> struct nvsSetterHelper<std::array<uint8_t, 4>> { static esp_err_t nvs_set(nvs_handle handle, const char* key, std::array<uint8_t, 4> value)
{
return nvs_set_u32(handle, key, std::bit_cast<uint32_t>(value));
}};
template<typename T>
bool SettingsPersister::save(T &settings)
{
bool result{true};
if (m_profile)
{
profileSettings.executeForEveryProfileSetting([&](const char *key, const auto &value)
{
if (esp_err_t result = nvsSetterHelper<std::decay_t<decltype(value)>>::nvs_set(m_profile->handle, key, value); result != ESP_OK)
{
ESP_LOGE("BOBBY", "nvs_set() PROFILE %s failed with %s", key, esp_err_to_name(result));
result = false;
}
});
}
else
{
ESP_LOGW("BOBBY", "no profile open currently!");
result = false;
}
return result;
}
template bool SettingsPersister::save<ProfileSettings>(ProfileSettings &settings);
std::optional<uint8_t> SettingsPersister::currentlyOpenProfileIndex() const
{
if (m_profile)
return m_profile->profileIndex;
return std::nullopt;
}