Remove TactilityCore (#550)
This commit is contained in:
committed by
GitHub
parent
dfaeb9a2d9
commit
66a4eb66d6
@@ -0,0 +1,113 @@
|
||||
#include "Tactility/Bundle.h"
|
||||
|
||||
namespace tt {
|
||||
|
||||
bool Bundle::getBool(const std::string& key) const {
|
||||
return this->entries.find(key)->second.value_bool;
|
||||
}
|
||||
|
||||
int32_t Bundle::getInt32(const std::string& key) const {
|
||||
return this->entries.find(key)->second.value_int32;
|
||||
}
|
||||
|
||||
int64_t Bundle::getInt64(const std::string& key) const {
|
||||
return this->entries.find(key)->second.value_int64;
|
||||
}
|
||||
|
||||
std::string Bundle::getString(const std::string& key) const {
|
||||
return this->entries.find(key)->second.value_string;
|
||||
}
|
||||
|
||||
bool Bundle::hasBool(const std::string& key) const {
|
||||
auto entry = this->entries.find(key);
|
||||
return entry != std::end(this->entries) && entry->second.type == Type::Bool;
|
||||
}
|
||||
|
||||
bool Bundle::hasInt32(const std::string& key) const {
|
||||
auto entry = this->entries.find(key);
|
||||
return entry != std::end(this->entries) && entry->second.type == Type::Int32;
|
||||
}
|
||||
|
||||
bool Bundle::hasInt64(const std::string& key) const {
|
||||
auto entry = this->entries.find(key);
|
||||
return entry != std::end(this->entries) && entry->second.type == Type::Int64;
|
||||
}
|
||||
|
||||
bool Bundle::hasString(const std::string& key) const {
|
||||
auto entry = this->entries.find(key);
|
||||
return entry != std::end(this->entries) && entry->second.type == Type::String;
|
||||
}
|
||||
|
||||
bool Bundle::optBool(const std::string& key, bool& out) const {
|
||||
auto entry = this->entries.find(key);
|
||||
if (entry != std::end(this->entries) && entry->second.type == Type::Bool) {
|
||||
out = entry->second.value_bool;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Bundle::optInt32(const std::string& key, int32_t& out) const {
|
||||
auto entry = this->entries.find(key);
|
||||
if (entry != std::end(this->entries) && entry->second.type == Type::Int32) {
|
||||
out = entry->second.value_int32;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Bundle::optInt64(const std::string& key, int64_t& out) const {
|
||||
auto entry = this->entries.find(key);
|
||||
if (entry != std::end(this->entries) && entry->second.type == Type::Int64) {
|
||||
out = entry->second.value_int64;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Bundle::optString(const std::string& key, std::string& out) const {
|
||||
auto entry = this->entries.find(key);
|
||||
if (entry != std::end(this->entries) && entry->second.type == Type::String) {
|
||||
out = entry->second.value_string;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Bundle::putBool(const std::string& key, bool value) {
|
||||
this->entries[key] = {
|
||||
.type = Type::Bool,
|
||||
.value_bool = value,
|
||||
.value_string = ""
|
||||
};
|
||||
}
|
||||
|
||||
void Bundle::putInt32(const std::string& key, int32_t value) {
|
||||
this->entries[key] = {
|
||||
.type = Type::Int32,
|
||||
.value_int32 = value,
|
||||
.value_string = ""
|
||||
};
|
||||
}
|
||||
|
||||
void Bundle::putInt64(const std::string& key, int64_t value) {
|
||||
this->entries[key] = {
|
||||
.type = Type::Int64,
|
||||
.value_int64 = value,
|
||||
.value_string = ""
|
||||
};
|
||||
}
|
||||
|
||||
void Bundle::putString(const std::string& key, const std::string& value) {
|
||||
this->entries[key] = {
|
||||
.type = Type::String,
|
||||
.value_bool = false,
|
||||
.value_string = value
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,108 @@
|
||||
#ifdef ESP_PLATFORM
|
||||
#include <sdkconfig.h>
|
||||
#endif
|
||||
|
||||
#include "Tactility/CpuAffinity.h"
|
||||
|
||||
#include <tactility/check.h>
|
||||
|
||||
namespace tt {
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#ifdef CONFIG_ESP_WIFI_ENABLED
|
||||
|
||||
static CpuAffinity getEspWifiAffinity() {
|
||||
#if CONFIG_FREERTOS_NUMBER_OF_CORES == 1
|
||||
return 0;
|
||||
#elif defined(CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0)
|
||||
return 0;
|
||||
#elif defined(CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1)
|
||||
return 1;
|
||||
#else // Assume core 0 (risky, but safer than "None")
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Warning: Must watch ESP WiFi, as this task is used by WiFi
|
||||
static CpuAffinity getEspMainSchedulerAffinity() {
|
||||
#if CONFIG_FREERTOS_NUMBER_OF_CORES == 1
|
||||
return 0;
|
||||
#elif defined(CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0)
|
||||
return 0;
|
||||
#elif defined(CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1)
|
||||
return 1;
|
||||
#else
|
||||
// Default to core 0 when no explicit WiFi pinning is configured
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static CpuAffinity getFreeRtosTimerAffinity() {
|
||||
#if CONFIG_FREERTOS_NUMBER_OF_CORES == 1
|
||||
return 0;
|
||||
#elif defined(CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY)
|
||||
return None;
|
||||
#elif defined(CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0)
|
||||
return 0;
|
||||
#elif defined(CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU1)
|
||||
return 1;
|
||||
#else
|
||||
static_assert(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if CONFIG_FREERTOS_NUMBER_OF_CORES == 1
|
||||
static const CpuAffinityConfiguration esp = {
|
||||
.system = 0,
|
||||
.graphics = 0,
|
||||
.wifi = 0,
|
||||
.mainDispatcher = 0,
|
||||
.apps = 0,
|
||||
.timer = getFreeRtosTimerAffinity()
|
||||
};
|
||||
#elif CONFIG_FREERTOS_NUMBER_OF_CORES == 2
|
||||
static const CpuAffinityConfiguration esp = {
|
||||
.system = 0,
|
||||
.graphics = 1,
|
||||
#ifdef CONFIG_ESP_WIFI_ENABLED
|
||||
.wifi = getEspWifiAffinity(),
|
||||
#else
|
||||
.wifi = 0,
|
||||
#endif
|
||||
.mainDispatcher = getEspMainSchedulerAffinity(),
|
||||
.apps = 1,
|
||||
.timer = getFreeRtosTimerAffinity()
|
||||
};
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
static const CpuAffinityConfiguration simulator = {
|
||||
.system = None,
|
||||
.graphics = None,
|
||||
.wifi = None,
|
||||
.mainDispatcher = 0,
|
||||
.apps = None,
|
||||
.timer = None
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
const CpuAffinityConfiguration& getCpuAffinityConfiguration() {
|
||||
#ifdef ESP_PLATFORM
|
||||
|
||||
#if CONFIG_FREERTOS_NUMBER_OF_CORES == 2
|
||||
// WiFi uses the main dispatcher to defer operations in the background
|
||||
assert(esp.wifi == esp.mainDispatcher);
|
||||
#endif // CORES
|
||||
return esp;
|
||||
|
||||
#else
|
||||
return simulator;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
#include "Tactility/StringUtils.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <ranges>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace tt::string {
|
||||
|
||||
bool getPathParent(const std::string& path, std::string& output) {
|
||||
auto index = path.find_last_of('/');
|
||||
if (index == std::string::npos) {
|
||||
return false;
|
||||
} else if (index == 0) {
|
||||
output = "/";
|
||||
return true;
|
||||
} else {
|
||||
output = path.substr(0, index);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::string getLastPathSegment(const std::string& path) {
|
||||
auto index = path.find_last_of('/');
|
||||
if (index != std::string::npos) {
|
||||
return path.substr(index + 1);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void split(const std::string& input, const std::string& delimiter, std::function<void(const std::string&)> callback) {
|
||||
size_t token_index = 0;
|
||||
size_t delimiter_index;
|
||||
const size_t delimiter_length = delimiter.length();
|
||||
|
||||
while ((delimiter_index = input.find(delimiter, token_index)) != std::string::npos) {
|
||||
std::string token = input.substr(token_index, delimiter_index - token_index);
|
||||
token_index = delimiter_index + delimiter_length;
|
||||
callback(token);
|
||||
}
|
||||
|
||||
auto end_token = input.substr(token_index);
|
||||
if (!end_token.empty()) {
|
||||
callback(end_token);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string&input, const std::string&delimiter) {
|
||||
std::vector<std::string> result;
|
||||
split(input, delimiter, [&result](const std::string& token) {
|
||||
result.push_back(token);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string join(const std::vector<const char*>& input, const std::string& delimiter) {
|
||||
std::stringstream stream;
|
||||
size_t size = input.size();
|
||||
|
||||
if (size == 0) {
|
||||
return "";
|
||||
} else if (size == 1) {
|
||||
return input.front();
|
||||
} else {
|
||||
auto iterator = input.begin();
|
||||
while (iterator != input.end()) {
|
||||
stream << *iterator;
|
||||
iterator++;
|
||||
if (iterator != input.end()) {
|
||||
stream << delimiter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string join(const std::vector<std::string>& input, const std::string& delimiter) {
|
||||
std::stringstream stream;
|
||||
size_t size = input.size();
|
||||
|
||||
if (size == 0) {
|
||||
return "";
|
||||
} else if (size == 1) {
|
||||
return input.front();
|
||||
} else {
|
||||
auto iterator = input.begin();
|
||||
while (iterator != input.end()) {
|
||||
stream << *iterator;
|
||||
iterator++;
|
||||
if (iterator != input.end()) {
|
||||
stream << delimiter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string removeFileExtension(const std::string& input) {
|
||||
auto index = input.find('.');
|
||||
if (index != std::string::npos) {
|
||||
return input.substr(0, index);
|
||||
} else {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
bool isAsciiHexString(const std::string& input) {
|
||||
// Find invalid characters
|
||||
return std::ranges::views::filter(input, [](char character){
|
||||
if (
|
||||
(('0' <= character) && ('9' >= character)) ||
|
||||
(('a' <= character) && ('f' >= character)) ||
|
||||
(('A' <= character) && ('F' >= character))
|
||||
) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}).empty();
|
||||
}
|
||||
|
||||
std::string trim(const std::string& input, const std::string& characters) {
|
||||
auto index = input.find_first_not_of(characters);
|
||||
if (index == std::string::npos) {
|
||||
return "";
|
||||
} else {
|
||||
auto end_index = input.find_last_not_of(characters);
|
||||
return input.substr(index, end_index - index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,391 @@
|
||||
#include <Tactility/file/File.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <tactility/log.h>
|
||||
#include <Tactility/StringUtils.h>
|
||||
|
||||
namespace tt::hal::sdcard {
|
||||
class SdCardDevice;
|
||||
}
|
||||
|
||||
namespace tt::file {
|
||||
|
||||
constexpr auto* TAG = "file";
|
||||
|
||||
class NoLock final : public Lock {
|
||||
bool lock(TickType_t timeout) const override { return true; }
|
||||
void unlock() const override { /* NO-OP */ }
|
||||
};
|
||||
|
||||
static std::shared_ptr<Lock> noLock = std::make_shared<NoLock>();
|
||||
static std::function<std::shared_ptr<Lock>(const std::string&)> findLockFunction = nullptr;
|
||||
|
||||
std::shared_ptr<Lock> getLock(const std::string& path) {
|
||||
if (findLockFunction == nullptr) {
|
||||
LOG_W(TAG, "File lock function not set!");
|
||||
return noLock;
|
||||
}
|
||||
|
||||
auto lock = findLockFunction(path);
|
||||
if (lock == nullptr) {
|
||||
return noLock;
|
||||
}
|
||||
|
||||
return lock;
|
||||
}
|
||||
|
||||
void setFindLockFunction(const FindLockFunction& function) {
|
||||
findLockFunction = function;
|
||||
}
|
||||
|
||||
std::string getChildPath(const std::string& basePath, const std::string& childPath) {
|
||||
// Postfix with "/" when the current path isn't "/"
|
||||
if (basePath.length() != 1) {
|
||||
return basePath + "/" + childPath;
|
||||
} else {
|
||||
return "/" + childPath;
|
||||
}
|
||||
}
|
||||
|
||||
int direntFilterDotEntries(const dirent* entry) {
|
||||
return (strcmp(entry->d_name, "..") == 0 || strcmp(entry->d_name, ".") == 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
bool direntSortAlphaAndType(const dirent& left, const dirent& right) {
|
||||
bool left_is_dir = left.d_type == TT_DT_DIR || left.d_type == TT_DT_CHR;
|
||||
bool right_is_dir = right.d_type == TT_DT_DIR || right.d_type == TT_DT_CHR;
|
||||
if (left_is_dir == right_is_dir) {
|
||||
return strcmp(left.d_name, right.d_name) < 0;
|
||||
} else {
|
||||
return left_is_dir > right_is_dir;
|
||||
}
|
||||
}
|
||||
|
||||
bool listDirectory(
|
||||
const std::string& path,
|
||||
std::function<void(const dirent&)> onEntry
|
||||
) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
LOG_I(TAG, "listDir start %s", path.c_str());
|
||||
DIR* dir = opendir(path.c_str());
|
||||
if (dir == nullptr) {
|
||||
LOG_E(TAG, "Failed to open dir %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
dirent* current_entry;
|
||||
while ((current_entry = readdir(dir)) != nullptr) {
|
||||
onEntry(*current_entry);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
LOG_I(TAG, "listDir stop %s", path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
int scandir(
|
||||
const std::string& path,
|
||||
std::vector<dirent>& outList,
|
||||
ScandirFilter filterMethod,
|
||||
ScandirSort sortMethod
|
||||
) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
LOG_I(TAG, "scandir start");
|
||||
DIR* dir = opendir(path.c_str());
|
||||
if (dir == nullptr) {
|
||||
LOG_E(TAG, "Failed to open dir %s", path.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
dirent* current_entry;
|
||||
while ((current_entry = readdir(dir)) != nullptr) {
|
||||
if (filterMethod == nullptr || filterMethod(current_entry) == 0) {
|
||||
outList.push_back(*current_entry);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
if (sortMethod != nullptr) {
|
||||
std::ranges::sort(outList, sortMethod);
|
||||
}
|
||||
|
||||
LOG_I(TAG, "scandir finish");
|
||||
return outList.size();
|
||||
}
|
||||
|
||||
|
||||
long getSize(FILE* file) {
|
||||
long original_offset = ftell(file);
|
||||
|
||||
if (fseek(file, 0, SEEK_END) != 0) {
|
||||
LOG_E(TAG, "fseek failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
long file_size = ftell(file);
|
||||
if (file_size == -1) {
|
||||
LOG_E(TAG, "Could not get file length");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fseek(file, original_offset, SEEK_SET) != 0) {
|
||||
LOG_E(TAG, "fseek Failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return file_size;
|
||||
}
|
||||
|
||||
/** Read a file.
|
||||
* @param[in] filepath
|
||||
* @param[out] outSize the amount of bytes that were read, excluding the sizePadding
|
||||
* @param[in] sizePadding optional padding to add at the end of the output data (the values are not set)
|
||||
*/
|
||||
static std::unique_ptr<uint8_t[]> readBinaryInternal(const std::string& filepath, size_t& outSize, size_t sizePadding = 0) {
|
||||
FILE* file = fopen(filepath.c_str(), "rb");
|
||||
|
||||
if (file == nullptr) {
|
||||
LOG_E(TAG, "Failed to open %s", filepath.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
long content_length = getSize(file);
|
||||
if (content_length == -1) {
|
||||
LOG_E(TAG, "Failed to determine content length for %s", filepath.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto data = std::make_unique<uint8_t[]>(content_length + sizePadding);
|
||||
if (data == nullptr) {
|
||||
LOG_E(TAG, "Insufficient memory. Failed to allocate %ld bytes.", content_length);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t buffer_offset = 0;
|
||||
while (buffer_offset < content_length) {
|
||||
size_t bytes_read = fread(&data.get()[buffer_offset], 1, content_length - buffer_offset, file);
|
||||
LOG_D(TAG, "Read %u bytes", (unsigned)bytes_read);
|
||||
if (bytes_read > 0) {
|
||||
buffer_offset += bytes_read;
|
||||
} else { // Something went wrong?
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
if (buffer_offset == content_length) {
|
||||
outSize = buffer_offset;
|
||||
return data;
|
||||
} else {
|
||||
outSize = 0;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> readBinary(const std::string& filepath, size_t& outSize) {
|
||||
return readBinaryInternal(filepath, outSize);
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> readString(const std::string& filepath) {
|
||||
size_t size = 0;
|
||||
auto data = readBinaryInternal(filepath, size, 1);
|
||||
if (data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
data.get()[size] = 0; // Append null terminator
|
||||
return data;
|
||||
}
|
||||
|
||||
bool writeString(const std::string& filepath, const std::string& content) {
|
||||
std::ofstream fileStream(filepath);
|
||||
|
||||
if (!fileStream.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fileStream << content;
|
||||
fileStream.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool findOrCreateDirectoryInternal(std::string path, mode_t mode) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
struct stat dir_stat;
|
||||
if (mkdir(path.c_str(), mode) == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (errno != EEXIST) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stat(path.c_str(), &dir_stat) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(dir_stat.st_mode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string getLastPathSegment(const std::string& path) {
|
||||
auto index = path.find_last_of('/');
|
||||
if (index != std::string::npos) {
|
||||
return path.substr(index + 1);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
std::string getFirstPathSegment(const std::string& path) {
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
auto start_index = path[0] == SEPARATOR ? 1 : 0;
|
||||
auto index = path.find_first_of(SEPARATOR, start_index);
|
||||
if (index != std::string::npos) {
|
||||
return path.substr(0, index);
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
bool findOrCreateDirectory(const std::string& path, mode_t mode) {
|
||||
if (path.empty()) {
|
||||
return true;
|
||||
}
|
||||
LOG_D(TAG, "findOrCreate: %s %u", path.c_str(), (unsigned)mode);
|
||||
|
||||
const char separator_to_find[] = {SEPARATOR, 0x00};
|
||||
auto first_index = path[0] == SEPARATOR ? 1 : 0;
|
||||
auto separator_index = path.find(separator_to_find, first_index);
|
||||
|
||||
bool should_break = false;
|
||||
while (!should_break) {
|
||||
bool is_last_segment = (separator_index == std::string::npos);
|
||||
auto to_create = is_last_segment ? path : path.substr(0, separator_index);
|
||||
should_break = is_last_segment;
|
||||
if (!findOrCreateDirectoryInternal(to_create, mode)) {
|
||||
LOG_E(TAG, "Failed to create %s", to_create.c_str());
|
||||
return false;
|
||||
} else {
|
||||
LOG_D(TAG, " - got: %s", to_create.c_str());
|
||||
}
|
||||
|
||||
// Find next file separator index
|
||||
separator_index = path.find(separator_to_find, separator_index + 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool findOrCreateParentDirectory(const std::string& path, mode_t mode) {
|
||||
std::string parent;
|
||||
if (!string::getPathParent(path, parent)) {
|
||||
return false;
|
||||
}
|
||||
return findOrCreateDirectory(parent, mode);
|
||||
}
|
||||
|
||||
bool deleteRecursively(const std::string& path) {
|
||||
if (path.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isDirectory(path)) {
|
||||
std::vector<dirent> entries;
|
||||
if (scandir(path, entries) < 0) {
|
||||
LOG_E(TAG, "Failed to scan directory %s", path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& entry : entries) {
|
||||
auto child_path = path + "/" + entry.d_name;
|
||||
if (!deleteRecursively(child_path)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
LOG_I(TAG, "Deleting %s", path.c_str());
|
||||
return deleteDirectory(path);
|
||||
} else if (isFile(path)) {
|
||||
LOG_I(TAG, "Deleting %s", path.c_str());
|
||||
return deleteFile(path);
|
||||
} else if (path == "/" || path == "." || path == "..") {
|
||||
// No-op
|
||||
return true;
|
||||
} else {
|
||||
LOG_E(TAG, "Failed to delete \"%s\": unknown type", path.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool deleteFile(const std::string& path) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
return remove(path.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool deleteDirectory(const std::string& path) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
return rmdir(path.c_str()) == 0;
|
||||
}
|
||||
|
||||
bool isFile(const std::string& path) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
return access(path.c_str(), F_OK) == 0;
|
||||
}
|
||||
|
||||
bool isDirectory(const std::string& path) {
|
||||
auto lock = getLock(path)->asScopedLock();
|
||||
lock.lock();
|
||||
struct stat stat_result;
|
||||
return stat(path.c_str(), &stat_result) == 0 && S_ISDIR(stat_result.st_mode);
|
||||
}
|
||||
|
||||
bool readLines(const std::string& filePath, bool stripNewLine, std::function<void(const char* line)> callback) {
|
||||
auto lock = getLock(filePath)->asScopedLock();
|
||||
lock.lock();
|
||||
|
||||
auto* file = fopen(filePath.c_str(), "r");
|
||||
if (file == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char line[1024];
|
||||
|
||||
while (fgets(line, sizeof(line), file) != nullptr) {
|
||||
// Strip newline
|
||||
if (stripNewLine) {
|
||||
size_t line_length = strlen(line);
|
||||
if (line_length > 0 && line[line_length - 1] == '\n') {
|
||||
line[line_length - 1] = '\0';
|
||||
}
|
||||
}
|
||||
// Publish
|
||||
callback(line);
|
||||
}
|
||||
|
||||
bool success = feof(file);
|
||||
fclose(file);
|
||||
return success;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#if defined(ESP_PLATFORM) && defined(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
||||
|
||||
#include "Tactility/kernel/PanicHandler.h"
|
||||
|
||||
#include <esp_debug_helpers.h>
|
||||
#include <esp_attr.h>
|
||||
#include <esp_memory_utils.h>
|
||||
#include <esp_cpu.h>
|
||||
#include <esp_cpu_utils.h>
|
||||
#include <xtensa/xtruntime.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
/**
|
||||
* This static variable survives a crash reboot.
|
||||
* It is reset by the Boot app.
|
||||
*/
|
||||
static RTC_NOINIT_ATTR CrashData crashData;
|
||||
|
||||
void __real_esp_panic_handler(void* info);
|
||||
|
||||
void __wrap_esp_panic_handler(void* info) {
|
||||
|
||||
esp_backtrace_frame_t frame = {
|
||||
.pc = 0,
|
||||
.sp = 0,
|
||||
.next_pc = 0,
|
||||
.exc_frame = nullptr
|
||||
};
|
||||
|
||||
crashData.callstackLength = 0;
|
||||
|
||||
esp_backtrace_get_start(&frame.pc, &frame.sp, &frame.next_pc);
|
||||
crashData.callstack[0].pc = frame.pc;
|
||||
#if CRASH_DATA_INCLUDES_SP
|
||||
crashData.callstack[0].sp = frame.sp;
|
||||
#endif
|
||||
crashData.callstackLength++;
|
||||
|
||||
uint32_t processed_pc = esp_cpu_process_stack_pc(frame.pc);
|
||||
bool pc_is_valid = esp_ptr_executable((void *)processed_pc);
|
||||
|
||||
/* Ignore the first corrupted PC in case of InstrFetchProhibited on Xtensa */
|
||||
if (frame.exc_frame && ((XtExcFrame *)frame.exc_frame)->exccause == EXCCAUSE_INSTR_PROHIBITED) {
|
||||
pc_is_valid = true;
|
||||
}
|
||||
|
||||
crashData.callstackCorrupted = !(esp_stack_ptr_is_sane(frame.sp) && pc_is_valid);
|
||||
|
||||
while (
|
||||
frame.next_pc != 0 &&
|
||||
!crashData.callstackCorrupted
|
||||
&& crashData.callstackLength < CRASH_DATA_CALLSTACK_LIMIT
|
||||
) {
|
||||
if (esp_backtrace_get_next_frame(&frame)) {
|
||||
// Validate the current frame
|
||||
uint32_t processed_frame_pc = esp_cpu_process_stack_pc(frame.pc);
|
||||
bool frame_pc_is_valid = esp_ptr_executable((void *)processed_frame_pc);
|
||||
|
||||
if (!esp_stack_ptr_is_sane(frame.sp) || !frame_pc_is_valid) {
|
||||
crashData.callstackCorrupted = true;
|
||||
break;
|
||||
}
|
||||
crashData.callstack[crashData.callstackLength].pc = frame.pc;
|
||||
#if CRASH_DATA_INCLUDES_SP
|
||||
crashData.callstack[crashData.callstackLength].sp = frame.sp;
|
||||
#endif
|
||||
crashData.callstackLength++;
|
||||
} else {
|
||||
crashData.callstackCorrupted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Handle corrupted logic
|
||||
|
||||
__real_esp_panic_handler(info);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const CrashData& getRtcCrashData() { return crashData; }
|
||||
|
||||
#elif defined(ESP_PLATFORM)
|
||||
|
||||
// Stub implementation for RISC-V and other architectures
|
||||
// TODO: Implement crash data collection for RISC-V using frame pointer or EH frame
|
||||
|
||||
#include <Tactility/kernel/PanicHandler.h>
|
||||
|
||||
static CrashData emptyCrashData = {};
|
||||
|
||||
const CrashData& getRtcCrashData() { return emptyCrashData; }
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "Tactility/kernel/Platform.h"
|
||||
|
||||
namespace tt::kernel {
|
||||
|
||||
Platform getPlatform() {
|
||||
#ifdef ESP_PLATFORM
|
||||
return PlatformEsp;
|
||||
#else
|
||||
return PlatformSimulator;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <Tactility/kernel/critical/Critical.h>
|
||||
|
||||
#include <Tactility/freertoscompat/Task.h>
|
||||
#include <Tactility/kernel/Kernel.h>
|
||||
|
||||
#ifdef ESP_PLATFORM
|
||||
static portMUX_TYPE critical_mutex;
|
||||
#define TT_ENTER_CRITICAL() taskENTER_CRITICAL(&critical_mutex)
|
||||
#define TT_EXIT_CRITICAL() taskEXIT_CRITICAL(&critical_mutex)
|
||||
#else
|
||||
#define TT_ENTER_CRITICAL() taskENTER_CRITICAL()
|
||||
#define TT_EXIT_CRITICAL() taskEXIT_CRITICAL()
|
||||
#endif
|
||||
|
||||
namespace tt::kernel::critical {
|
||||
|
||||
CriticalInfo enter() {
|
||||
CriticalInfo info = {
|
||||
.isrm = 0,
|
||||
.fromIsr = (xPortInIsrContext() == pdTRUE),
|
||||
.kernelRunning = (xTaskGetSchedulerState() == taskSCHEDULER_RUNNING)
|
||||
};
|
||||
|
||||
if (info.fromIsr) {
|
||||
info.isrm = taskENTER_CRITICAL_FROM_ISR();
|
||||
} else if (info.kernelRunning) {
|
||||
TT_ENTER_CRITICAL();
|
||||
} else {
|
||||
portDISABLE_INTERRUPTS();
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void exit(CriticalInfo info) {
|
||||
if (info.fromIsr) {
|
||||
taskEXIT_CRITICAL_FROM_ISR(info.isrm);
|
||||
} else if (info.kernelRunning) {
|
||||
TT_EXIT_CRITICAL();
|
||||
} else {
|
||||
portENABLE_INTERRUPTS();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user