Files
tactility/Tactility/Source/service/development/DevelopmentService.cpp
T
Adolfo Reyna 5c535490ea
Build Simulator / Linux (push) Has been cancelled
Build Simulator / macOS (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32 id:generic-esp32]) (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32c6 id:generic-esp32c6]) (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32p4 id:generic-esp32p4]) (push) Has been cancelled
Build Firmware / BuildSdk (map[arch:esp32s3 id:generic-esp32s3]) (push) Has been cancelled
Tests / TactilityTests (push) Has been cancelled
Tests / DevicetreeTests (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s024c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s024r]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s028r]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s028rv3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-2432s032c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-3248s035c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-e32r28t]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:cyd-e32r32p]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:elecrow-crowpanel-basic-28]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:elecrow-crowpanel-basic-35]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:guition-jc2432w328c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:lilygo-tdisplay]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:m5stack-core2]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:m5stack-stickc-plus2]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32 id:m5stack-stickc-plus]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32p4 id:guition-jc1060p470ciwy]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32p4 id:m5stack-tab5]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:btt-panda-touch]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:cyd-4848s040c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:cyd-8048s043c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:elecrow-crowpanel-advance-28]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:elecrow-crowpanel-advance-35]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:elecrow-crowpanel-advance-50]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:elecrow-crowpanel-basic-50]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:guition-jc3248w535c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:guition-jc8048w550c]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:heltec-wifi-lora-32-v3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-tdeck]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-tdisplay-s3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-tdongle-s3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-thmi]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:lilygo-tlora-pager]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-cardputer-adv]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-cardputer]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-cores3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-papers3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-stackchan]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:m5stack-sticks3]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:unphone]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-esp32-s3-geek]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-s3-lcd-13]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-s3-touch-lcd-128]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-s3-touch-lcd-147]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:waveshare-s3-touch-lcd-43]) (push) Has been cancelled
Build Firmware / BuildFirmware (map[arch:esp32s3 id:wireless-tag-wt32-sc01-plus]) (push) Has been cancelled
Build Firmware / BundleArtifacts (push) Has been cancelled
Build Firmware / PublishFirmwareSnapshot (push) Has been cancelled
Build Firmware / PublishFirmwareStable (push) Has been cancelled
Build Firmware / PublishSdk (push) Has been cancelled
Fix compatibility issues for ESP-IDF v5.3.2 and add es3c28p target support
2026-06-23 11:59:47 -04:00

249 lines
7.2 KiB
C++

#ifdef ESP_PLATFORM
#include <Tactility/service/development/DevelopmentService.h>
#include <Tactility/app/App.h>
#include <Tactility/app/AppRegistration.h>
#include <Tactility/file/File.h>
#include <Tactility/network/HttpdReq.h>
#include <Tactility/network/Url.h>
#include <Tactility/Logger.h>
#include <Tactility/Paths.h>
#include <Tactility/service/development/DevelopmentSettings.h>
#include <Tactility/service/ServiceRegistration.h>
#include <Tactility/StringUtils.h>
#include <ranges>
#include <sstream>
namespace tt::service::development {
extern const ServiceManifest manifest;
static const auto LOGGER = Logger("DevService");
bool DevelopmentService::onStart(ServiceContext& service) {
std::stringstream stream;
stream << "{";
stream << "\"cpuFamily\":\"" << CONFIG_IDF_TARGET << "\", ";
stream << "\"osVersion\":\"" << TT_VERSION << "\", ";
stream << "\"protocolVersion\":\"1.0.0\"";
stream << "}";
deviceResponse = stream.str();
setEnabled(shouldEnableOnBoot());
return true;
}
void DevelopmentService::onStop(ServiceContext& service) {
setEnabled(false);
}
// region Enable/disable
void DevelopmentService::setEnabled(bool enabled) {
auto lock = mutex.asScopedLock();
lock.lock();
if (enabled) {
if (!httpServer.isStarted()) {
httpServer.start();
}
} else {
if (httpServer.isStarted()) {
httpServer.stop();
}
}
}
bool DevelopmentService::isEnabled() const {
auto lock = mutex.asScopedLock();
lock.lock();
return httpServer.isStarted();
}
// region endpoints
esp_err_t DevelopmentService::handleGetInfo(httpd_req_t* request) {
LOGGER.info("GET /device");
if (httpd_resp_set_type(request, "application/json") != ESP_OK) {
LOGGER.warn("Failed to send header");
return ESP_FAIL;
}
auto* service = static_cast<DevelopmentService*>(request->user_ctx);
if (httpd_resp_sendstr(request, service->deviceResponse.c_str()) != ESP_OK) {
LOGGER.warn("Failed to send response body");
return ESP_FAIL;
}
LOGGER.info("[200] /device");
return ESP_OK;
}
esp_err_t DevelopmentService::handleAppRun(httpd_req_t* request) {
LOGGER.info("POST /app/run");
std::string query;
if (!network::getQueryOrSendError(request, query)) {
return ESP_FAIL;
}
auto parameters = network::parseUrlQuery(query);
auto id_key_pos = parameters.find("id");
if (id_key_pos == parameters.end()) {
LOGGER.warn("[400] /app/run id not specified");
httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "id not specified");
return ESP_FAIL;
}
const auto& app_id = id_key_pos->second;
if (app::isRunning(app_id)) {
app::stopAll(app_id);
}
app::start(app_id);
LOGGER.info("[200] /app/run {}", id_key_pos->second);
httpd_resp_send(request, nullptr, 0);
return ESP_OK;
}
esp_err_t DevelopmentService::handleAppInstall(httpd_req_t* request) {
LOGGER.info("PUT /app/install");
std::string boundary;
if (!network::getMultiPartBoundaryOrSendError(request, boundary)) {
return false;
}
size_t content_left = request->content_len;
// Skip newline after reading boundary
auto content_headers_data = network::receiveTextUntil(request, "\r\n\r\n");
content_left -= content_headers_data.length();
auto raw_headers = string::split(content_headers_data, "\r\n");
std::vector<std::string> content_headers;
content_headers.reserve(raw_headers.size());
for (const auto& line : raw_headers) {
if (line.length() > 0) {
content_headers.push_back(line);
}
}
auto content_disposition_map = network::parseContentDisposition(content_headers);
if (content_disposition_map.empty()) {
httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "Multipart form error: invalid content disposition");
return ESP_FAIL;
}
auto name_entry = content_disposition_map.find("name");
auto filename_entry = content_disposition_map.find("filename");
if (
name_entry == content_disposition_map.end() ||
filename_entry == content_disposition_map.end() ||
name_entry->second != "elf"
) {
httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "Multipart form error: name or filename parameter missing or mismatching");
return ESP_FAIL;
}
// Receive boundary
auto boundary_and_newlines_after_file = std::format("\r\n--{}--\r\n", boundary);
auto file_size = content_left - boundary_and_newlines_after_file.length();
// Create tmp directory
const std::string tmp_path = getTempPath();
if (!file::findOrCreateDirectory(tmp_path, 0777)) {
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to save file");
return ESP_FAIL;
}
auto file_path = std::format("{}/{}", tmp_path, filename_entry->second);
if (network::receiveFile(request, file_size, file_path) != file_size) {
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to save file");
return ESP_FAIL;
}
content_left -= file_size;
// Read and verify part
if (!network::readAndDiscardOrSendError(request, boundary_and_newlines_after_file)) {
return ESP_FAIL;
}
content_left -= boundary_and_newlines_after_file.length();
if (content_left != 0) {
LOGGER.warn("We have more bytes at the end of the request parsing?!");
}
if (!app::install(file_path)) {
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to install");
return ESP_FAIL;
}
if (!file::deleteFile(file_path)) {
LOGGER.warn("Failed to delete {}", file_path);
}
LOGGER.info("[200] /app/install -> {}", file_path);
httpd_resp_send(request, nullptr, 0);
return ESP_OK;
}
esp_err_t DevelopmentService::handleAppUninstall(httpd_req_t* request) {
LOGGER.info("PUT /app/uninstall");
std::string query;
if (!network::getQueryOrSendError(request, query)) {
return ESP_FAIL;
}
auto parameters = network::parseUrlQuery(query);
auto id_key_pos = parameters.find("id");
if (id_key_pos == parameters.end()) {
LOGGER.warn("[400] /app/uninstall id not specified");
httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "id not specified");
return ESP_FAIL;
}
if (!app::findAppManifestById(id_key_pos->second)) {
LOGGER.info("[200] /app/uninstall {} (app wasn't installed)", id_key_pos->second);
httpd_resp_send(request, nullptr, 0);
return ESP_OK;
}
if (app::uninstall(id_key_pos->second)) {
LOGGER.info("[200] /app/uninstall {}", id_key_pos->second);
httpd_resp_send(request, nullptr, 0);
return ESP_OK;
} else {
LOGGER.warn("[500] /app/uninstall {}", id_key_pos->second);
httpd_resp_send_err(request, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to uninstall");
return ESP_FAIL;
}
}
// endregion
std::shared_ptr<DevelopmentService> findService() {
return std::static_pointer_cast<DevelopmentService>(
findServiceById(manifest.id)
);
}
extern const ServiceManifest manifest = {
.id = "Development",
.createService = create<DevelopmentService>
};
}
#endif // ESP_PLATFORM