Buildserver is now selectable
This commit is contained in:
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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>>>();
|
||||
|
39
main/displays/menus/otamenu.h
Normal file
39
main/displays/menus/otamenu.h
Normal 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
|
147
main/displays/menus/selectotabuildmenu.h
Normal file
147
main/displays/menus/selectotabuildmenu.h
Normal 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
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user