Buildserver is now selectable

This commit is contained in:
CommanderRedYT
2021-10-21 00:08:21 +02:00
committed by 0xFEEDC0DE64
parent bc2b194acb
commit ea98b02d92
8 changed files with 364 additions and 27 deletions

View File

@ -125,6 +125,8 @@ set(headers
displays/menus/mosfetsmenu.h
displays/menus/motorfeedbackdebugmenu.h
displays/menus/motorstatedebugmenu.h
displays/menus/otamenu.h
displays/menus/selectotabuildmenu.h
displays/menus/presetsmenu.h
displays/menus/profilesmenu.h
displays/menus/selectbatterytypemenu.h

View File

@ -1,5 +1,14 @@
#pragma once
#include <ArduinoJson.h>
#include <cpputils.h>
#include <cleanuphelper.h>
// 3rdparty lib includes
#include <asynchttprequest.h>
#include <delayedconstruction.h>
// local includes
#include "globals.h"
#include "esp_log.h"
#include "fmt/core.h"
@ -7,39 +16,169 @@
// esp-idf
#include "esp_http_client.h"
/*
* ToDo:
* - get_ota_decriptor_json_string => returns std::string, takes std::string url
*/
namespace {
void buildMenuFromJson(std::string json);
void buildMenuRequestError(std::string error);
static std::string url_for_hashes = "";
static std::string url_for_latest = "";
static std::array<std::string, 10> availableVersions = {};
bool request_running = false;
uint16_t request_failed = false;
bool parsing_finished = false;
cpputils::DelayedConstruction<AsyncHttpRequest> request;
std::string ota_descriptor_json = "";
std::string get_ota_url_from_index(uint16_t index)
{
if (index < stringSettings.otaServers.size())
std::string get_ota_url_from_index(uint16_t index)
{
auto otaServer = stringSettings.otaServers[index];
if (!otaServer.url.empty())
if (index < stringSettings.otaServers.size())
{
return otaServer.url;
auto otaServer = stringSettings.otaServers[index];
if (!otaServer.url.empty())
{
return otaServer.url;
}
else
{
ESP_LOGE("BOBBY", "Cannot get OTA url: otaServer.url is empty");
return "";
}
}
else
{
ESP_LOGE("BOBBY", "Cannot get OTA url: otaServer.url is empty");
ESP_LOGE("BOBBY", "Cannot get OTA url: Invalid Index");
return "";
}
}
else
uint16_t count_available_buildserver()
{
ESP_LOGE("BOBBY", "Cannot get OTA url: Invalid Index");
return "";
uint16_t count = 0;
for (const auto &otaServer : stringSettings.otaServers) {
if (!otaServer.url.empty()) count++;
}
return count;
}
std::string get_hash_url(std::string hash)
{
return fmt::format(url_for_hashes, hash);
}
std::string get_latest_url()
{
return url_for_latest;
}
std::string fix_url(std::string url)
{
std::string fixed_url = url;
if (fixed_url.find("http") == std::string::npos)
{
fixed_url = fmt::format("http://{}", fixed_url);
}
return fixed_url;
}
std::string get_descriptor_url(std::string base_url)
{
std::string url = fix_url(base_url);
return fmt::format("{}/otaDescriptor?username={}", url, OTA_USERNAME);
}
void parse_response_into_variables(std::string response)
{
StaticJsonDocument<512> doc;
if (const auto error = deserializeJson(doc, response))
{
ESP_LOGE("BOBBY", "Error parsing server-response => %s", response.c_str());
return;
}
JsonObject availableVersionsObject = doc["availableVersions"];
for (JsonPair kv : availableVersionsObject)
{
static auto index = 0;
auto hash = kv.key().c_str();
if (index > availableVersions.size())
{
break;
}
availableVersions.at(index) = hash;
index++;
}
url_for_latest = doc["latest"].as<std::string>();
url_for_hashes = doc["url"].as<std::string>();
parsing_finished = true;
}
void setup_request()
{
if (!request.constructed())
request.construct("ota-descriptor-request", espcpputils::CoreAffinity::Core0);
}
void start_descriptor_request(std::string server_base_url)
{
if (!request.constructed())
{
ESP_LOGW("BOBBY", "request is im oarsch");
return;
}
const auto url = get_descriptor_url(server_base_url);
ESP_LOGD("BOBBY", "requesting data...");
if (const auto result = request->start(url); !result)
{
ESP_LOGW("BOBBY", "request start failed");
return;
}
request_running = true;
request_failed = false;
url_for_latest.clear();
url_for_hashes.clear();
availableVersions = {};
parsing_finished = false;
}
void check_descriptor_request()
{
if (!request.constructed())
{
ESP_LOGW("BOBBY", "request is im oarsch");
request_running = false;
request_failed = true;
return;
}
if (!request->finished())
{
// ESP_LOGW("BOBBY", "Request has not finished yet.");
return;
}
const auto helper = cpputils::makeCleanupHelper([](){ request->clearFinished(); });
const std::string content = std::move(request->takeBuffer());
if (const auto result = request->result(); !result)
{
ESP_LOGW("BOBBY", "request failed: %.*s", result.error().size(), result.error().data());
std::string failed_str = result.error().data();
auto statuscode = failed_str.substr(failed_str.length() - 3);
request_running = false;
request_failed = std::stoi(statuscode);
return;
}
const auto result = request->result();
ESP_LOGW("BOBBY", "Request finished: %s", content.c_str());
parse_response_into_variables(content);
request_running = false;
request_failed = false;
}
bool get_request_running()
{
return request_running;
}
}
void get_ota_descriptor(std::string url)
{
auto descriptorUrl = fmt::format("{}/otaDescriptor", url);
// Make GET request to descriptorUrl and store json somewhere to be decoded when needed, for example in ota_descriptor_json (buildserver.h:16)
}
}

View File

@ -46,7 +46,7 @@ class Lockscreen;
class MosfetsMenu;
class DemosMenu;
class GarageDisplay;
class UpdateDisplay;
class OtaMenu;
class PoweroffDisplay;
class DebugMenu;
class BatteryMenu;
@ -97,7 +97,7 @@ public:
if (SHOWITEM) { constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_GARAGE>, SwitchScreenAction<GarageDisplay>>>(); }
#endif
#ifdef FEATURE_OTA
if (SHOWITEM) { constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_UPDATE>, SwitchScreenAction<UpdateDisplay>, StaticMenuItemIcon<&bobbyicons::update>>>(); }
if (SHOWITEM) { constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_UPDATE>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&bobbyicons::update>>>(); }
#endif
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_POWEROFF>, SwitchScreenAction<PoweroffDisplay>, StaticMenuItemIcon<&bobbyicons::poweroff>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_REBOOT>, RebootAction, StaticMenuItemIcon<&bobbyicons::reboot>>>();

View File

@ -0,0 +1,39 @@
#pragma once
// local includes
#include "menudisplay.h"
#include "utils.h"
#include "actions/dummyaction.h"
#include "icons/back.h"
#include "icons/update.h"
#include "icons/presets.h"
#include "texts.h"
// forward declares
namespace {
class MainMenu;
class UpdateDisplay;
class SelectBuildMenu;
class SelectBuildServerMenu;
} // namespace
using namespace espgui;
namespace {
class OtaMenu :
public MenuDisplay,
public StaticText<TEXT_UPDATE>,
public BackActionInterface<SwitchScreenAction<MainMenu>>
{
public:
OtaMenu()
{
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SELECTBUILD>, SwitchScreenAction<SelectBuildMenu>, StaticMenuItemIcon<&bobbyicons::presets>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_UPDATENOW>, SwitchScreenAction<UpdateDisplay>, StaticMenuItemIcon<&bobbyicons::update>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_SELECTBUILDSERVERMENU>, SwitchScreenAction<SelectBuildServerMenu>>>();
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<MainMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}
};
} // namespace

View File

@ -0,0 +1,147 @@
#pragma once
#include <espwifistack.h>
#include "esp_log.h"
#include "fmt/core.h"
// local includes
#include "menudisplay.h"
#include "utils.h"
#include "actions/dummyaction.h"
#include "icons/back.h"
#include "icons/update.h"
#include "texts.h"
#include "buildserver.h"
#include "globals.h"
#define MESSAGE(text) constructMenuItem<makeComponent<MenuItem, StaticText<text>, DummyAction>>()
// forward declares
namespace {
class OtaMenu;
} // namespace
using namespace espgui;
namespace {
// ToDo: if (request_failed) => MESSAGE("An error occurred")
class VersionMenuItem : public MenuItem
{
public:
std::string text() const override { return m_hash; }
void setHash(std::string &&hash) { m_hash = std::move(hash); }
void setHash(const std::string &hash) { m_hash = hash; }
void setUrl(std::string &&url) { m_url = std::move(url); }
void setUrl(const std::string &url) { m_url = url; }
void triggered() override
{
stringSettings.otaUrl = m_url;
saveSettings();
}
private:
std::string m_url;
std::string m_hash;
};
class SelectBuildMenu :
public MenuDisplay,
public StaticText<TEXT_SELECTBUILD>,
public BackActionInterface<SwitchScreenAction<OtaMenu>>
{
using Base = MenuDisplay;
public:
void update() override;
void buildMenuFromJson();
void buildMenuRequestError(std::string error);
SelectBuildMenu()
{
if (count_available_buildserver() < 1)
{
MESSAGE(TEXT_OTA_NOBUILDSERVERAVAILABLE);
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}
else if (stringSettings.otaServerUrl.empty())
{
MESSAGE(TEXT_OTA_NOBUILDSERVERSELECTED);
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}
else
{
const auto staStatus = wifi_stack::get_sta_status();
if (staStatus != wifi_stack::WiFiStaStatus::CONNECTED)
{
MESSAGE(TEXT_OTA_NOCONNECTION);
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}
else
{
std::string serverUrl = stringSettings.otaServerUrl;
if (serverUrl.substr(serverUrl.length() - 4) == ".bin")
{
auto &menuitem = constructMenuItem<VersionMenuItem>();
std::size_t last_slash_index = serverUrl.find_last_of("/");
auto filename = serverUrl.substr(last_slash_index+1);
auto hash = filename.substr(0, filename.length() - 4);
menuitem.setHash(hash);
menuitem.setUrl(fix_url(serverUrl));
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}
else
{
setup_request();
start_descriptor_request(serverUrl);
}
}
}
}
};
void SelectBuildMenu::update()
{
if(get_request_running())
{
check_descriptor_request();
if (request_failed)
{
this->buildMenuRequestError(fmt::format("Error: {}", request_failed));
request_failed = false;
}
}
if (parsing_finished)
{
parsing_finished = false;
if (!availableVersions.empty())
{
this->buildMenuFromJson();
}
}
Base::update();
}
void SelectBuildMenu::buildMenuFromJson()
{
auto &latest = constructMenuItem<VersionMenuItem>();
latest.setHash("latest");
latest.setUrl(url_for_latest);
for (const std::string &hash : availableVersions)
{
auto &menuitem = constructMenuItem<VersionMenuItem>();
menuitem.setHash(hash);
menuitem.setUrl(fmt::format(url_for_hashes, hash));
}
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}
void SelectBuildMenu::buildMenuRequestError(std::string error)
{
auto &item = constructMenuItem<makeComponent<MenuItem, ChangeableText, DummyAction>>();
item.setTitle(error);
constructMenuItem<makeComponent<MenuItem, StaticText<TEXT_BACK>, SwitchScreenAction<OtaMenu>, StaticMenuItemIcon<&espgui::icons::back>>>();
}
} // namespace

View File

@ -23,12 +23,12 @@
#include "ota.h"
namespace {
class MainMenu;
class OtaMenu;
}
namespace {
#ifdef FEATURE_OTA
class UpdateDisplay : public Display, public BackActionInterface<SwitchScreenAction<MainMenu>>
class UpdateDisplay : public Display, public BackActionInterface<SwitchScreenAction<OtaMenu>>
{
public:
void start() override;

View File

@ -106,6 +106,8 @@ using namespace std::chrono_literals;
#include "displays/statusdisplay.h"
#ifdef FEATURE_OTA
#include "displays/updatedisplay.h"
#include "displays/menus/otamenu.h"
#include "displays/menus/selectotabuildmenu.h"
#endif
#include "screens.h"
#include "dpad.h"

View File

@ -475,6 +475,14 @@ constexpr char TEXT_CRASH_DIVZERO[] = "42 / 0";
constexpr char TEXT_SELECTBUILDSERVERMENU[] = "Select Buildserver";
constexpr char TEXT_NOBUILDSERVERCONFIGURED[] = "Not configured";
//Otamenu
constexpr char TEXT_UPDATENOW[] = "Update now";
constexpr char TEXT_SELECTBUILD[] = "Select build";
constexpr char TEXT_OTA_NOBUILDSERVERAVAILABLE[] = "E:No server saved.";
constexpr char TEXT_OTA_NOBUILDSERVERSELECTED[] = "E:No server selected.";
constexpr char TEXT_OTA_NOCONNECTION[] = "E:No internet.";
constexpr char TEXT_OTA_WAITFORRESPONSE[] = "Wait for response...";
#ifdef FEATURE_CAN
constexpr char TEXT_POWERSUPPLY[] = "Powersupply";
#endif