GPS refactored (#262)

- Refactored GPS service and HAL: GPS is no longer part of the HAL configuration. You can now add configure new GPS devices from the GPS settings app.
- T-Deck adds a boot hook to check if a GPS configuration exists and adds it when the config is empty.
- Implemented the concept of ObjectFile to read/write arrays of a raw data type (e.g. struct) to disk.
- Implemented more file utils (e.g. to create all directories of a path)
This commit is contained in:
Ken Van Hoeylandt
2025-03-30 01:14:22 +01:00
committed by GitHub
parent 81ece6f2e7
commit d0ca3b16f8
47 changed files with 1266 additions and 277 deletions
-2
View File
@@ -29,8 +29,6 @@ void init(const Configuration& configuration) {
tt_check(uart::init(configuration.uart), "UART init failed");
kernel::systemEventPublish(kernel::SystemEvent::BootInitUartEnd);
tt_check(gps::init(configuration.gps), "GPS init failed");
if (configuration.initBoot != nullptr) {
TT_LOG_I(TAG, "Init power");
tt_check(configuration.initBoot(), "Init power failed");
@@ -0,0 +1,51 @@
#include "Tactility/hal/gps/GpsConfiguration.h"
#include "Tactility/service/gps/GpsService.h"
#include <Tactility/TactilityCore.h>
#include <Tactility/file/ObjectFile.h>
namespace tt::hal::gps {
const char* toString(GpsModel model) {
using enum GpsModel;
switch (model) {
case AG3335:
return TT_STRINGIFY(AG3335);
case AG3352:
return TT_STRINGIFY(AG3352);
case ATGM336H:
return TT_STRINGIFY(ATGM336H);
case LS20031:
return TT_STRINGIFY(LS20031);
case MTK:
return TT_STRINGIFY(MTK);
case MTK_L76B:
return TT_STRINGIFY(MTK_L76B);
case MTK_PA1616S:
return TT_STRINGIFY(MTK_PA1616S);
case UBLOX6:
return TT_STRINGIFY(UBLOX6);
case UBLOX7:
return TT_STRINGIFY(UBLOX7);
case UBLOX8:
return TT_STRINGIFY(UBLOX8);
case UBLOX9:
return TT_STRINGIFY(UBLOX9);
case UBLOX10:
return TT_STRINGIFY(UBLOX10);
case UC6580:
return TT_STRINGIFY(UC6580);
default:
return TT_STRINGIFY(Unknown);
}
}
std::vector<std::string> getModels() {
std::vector<std::string> result;
for (GpsModel model = GpsModel::Unknown; model <= GpsModel::UC6580; ++(int&)model) {
result.push_back(toString(model));
}
return result;
}
}
+7 -42
View File
@@ -5,44 +5,10 @@
#include <cstring>
#include <minmea.h>
#define TAG "gps"
#define GPS_UART_BUFFER_SIZE 256
namespace tt::hal::gps {
const char* toString(GpsModel model) {
using enum GpsModel;
switch (model) {
case AG3335:
return TT_STRINGIFY(AG3335);
case AG3352:
return TT_STRINGIFY(AG3352);
case ATGM336H:
return TT_STRINGIFY(ATGM336H);
case LS20031:
return TT_STRINGIFY(LS20031);
case MTK:
return TT_STRINGIFY(MTK);
case MTK_L76B:
return TT_STRINGIFY(MTK_L76B);
case MTK_PA1616S:
return TT_STRINGIFY(MTK_PA1616S);
case UBLOX6:
return TT_STRINGIFY(UBLOX6);
case UBLOX7:
return TT_STRINGIFY(UBLOX7);
case UBLOX8:
return TT_STRINGIFY(UBLOX8);
case UBLOX9:
return TT_STRINGIFY(UBLOX9);
case UBLOX10:
return TT_STRINGIFY(UBLOX10);
case UC6580:
return TT_STRINGIFY(UC6580);
default:
return TT_STRINGIFY(Unknown);
}
}
constexpr uint32_t GPS_UART_BUFFER_SIZE = 256;
constexpr const char* TAG = "GpsDevice";
int32_t GpsDevice::threadMainStatic(void* parameter) {
auto* gps_device = (GpsDevice*)parameter;
@@ -54,17 +20,17 @@ int32_t GpsDevice::threadMain() {
auto uart = uart::open(configuration.uartName);
if (uart == nullptr) {
TT_LOG_E(TAG, "Failed to open UART %s", configuration.uartName.c_str());
TT_LOG_E(TAG, "Failed to open UART %s", configuration.uartName);
return -1;
}
if (!uart->start()) {
TT_LOG_E(TAG, "Failed to start UART %s", configuration.uartName.c_str());
TT_LOG_E(TAG, "Failed to start UART %s", configuration.uartName);
return -1;
}
if (!uart->setBaudRate((int)configuration.baudRate)) {
TT_LOG_E(TAG, "Failed to set baud rate to %lu for UART %s", configuration.baudRate, configuration.uartName.c_str());
TT_LOG_E(TAG, "Failed to set baud rate to %lu for UART %s", configuration.baudRate, configuration.uartName);
return -1;
}
@@ -77,7 +43,6 @@ int32_t GpsDevice::threadMain() {
return -1;
}
}
mutex.lock();
this->model = model;
mutex.unlock();
@@ -101,7 +66,7 @@ int32_t GpsDevice::threadMain() {
if (bytes_read > 0U) {
TT_LOG_D(TAG, "%s", buffer);
TT_LOG_I(TAG, "[%ul] %s", bytes_read, buffer);
switch (minmea_sentence_id((char*)buffer, false)) {
case MINMEA_SENTENCE_RMC:
@@ -137,7 +102,7 @@ int32_t GpsDevice::threadMain() {
}
if (uart->isStarted() && !uart->stop()) {
TT_LOG_W(TAG, "Failed to stop UART %s", configuration.uartName.c_str());
TT_LOG_W(TAG, "Failed to stop UART %s", configuration.uartName);
}
return 0;
@@ -1,7 +1,6 @@
#include "Tactility/hal/gps/Cas.h"
#include "Tactility/hal/gps/GpsDevice.h"
#include "Tactility/hal/gps/Ublox.h"
#include "Tactility/service/Service.h"
#include <cstring>
#define TAG "gps"
@@ -133,17 +132,6 @@ GpsResponse getACKCas(uart::Uart& uart, uint8_t class_id, uint8_t msg_id, uint32
// endregion
/** Initialize the HAL with the provided configuration */
bool init(const std::vector<GpsDevice::Configuration>& configurations) {
for (auto& configuration : configurations) {
auto device = std::make_shared<GpsDevice>(configuration);
hal::registerDevice(std::move(device));
}
return true;
}
bool init(uart::Uart& uart, GpsModel type) {
switch (type) {
case GpsModel::Unknown:
@@ -69,7 +69,7 @@ void systemEventPublish(SystemEvent event) {
}
}
SystemEventSubscription systemEventAddListener(SystemEvent event, OnSystemEvent handler) {
SystemEventSubscription systemEventAddListener(SystemEvent event, std::function<void(SystemEvent)> handler) {
if (mutex.lock(portMAX_DELAY)) {
auto id = ++subscriptionCounter;
@@ -3,6 +3,7 @@
#include "Tactility/Partitions.h"
#define LVGL_PATH_PREFIX std::string("A:/")
#ifdef ESP_PLATFORM
#define PARTITION_PREFIX std::string("/")
#else
@@ -1,50 +0,0 @@
#include "Tactility/PubSub.h"
#include "Tactility/service/ServiceManifest.h"
#include "Tactility/service/ServiceRegistry.h"
#include "Tactility/service/gps/GpsService.h"
using tt::hal::gps::GpsDevice;
namespace tt::service::gps {
extern ServiceManifest manifest;
static std::shared_ptr<GpsService> findGpsService() {
auto service = findServiceById(manifest.id);
assert(service != nullptr);
return std::static_pointer_cast<GpsService>(service);
}
void addGpsDevice(const std::shared_ptr<GpsDevice>& device) {
return findGpsService()->addGpsDevice(device);
}
void removeGpsDevice(const std::shared_ptr<GpsDevice>& device) {
return findGpsService()->removeGpsDevice(device);
}
bool startReceiving() {
return findGpsService()->startReceiving();
}
void stopReceiving() {
findGpsService()->stopReceiving();
}
bool hasCoordinates() {
return findGpsService()->hasCoordinates();
}
bool getCoordinates(minmea_sentence_rmc& rmc) {
return findGpsService()->getCoordinates(rmc);
}
State getState() {
return findGpsService()->getState();
}
std::shared_ptr<PubSub> getStatePubsub() {
return findGpsService()->getStatePubsub();
}
} // namespace tt::service::gps
@@ -0,0 +1,108 @@
#include "Tactility/service/gps/GpsService.h"
#include <Tactility/file/ObjectFile.h>
#include <cstring>
using tt::hal::gps::GpsDevice;
namespace tt::service::gps {
constexpr const char* TAG = "GpsService";
bool GpsService::getConfigurationFilePath(std::string& output) const {
if (paths == nullptr) {
TT_LOG_E(TAG, "Can't add configuration: service not started");
return false;
}
if (!file::findOrCreateDirectory(paths->getDataDirectory(), 0777)) {
TT_LOG_E(TAG, "Failed to find or create path %s", paths->getDataDirectory().c_str());
return false;
}
output = paths->getDataPath("config.bin");
return true;
}
bool GpsService::getGpsConfigurations(std::vector<hal::gps::GpsConfiguration>& configurations) const {
std::string path;
if (!getConfigurationFilePath(path)) {
return false;
}
auto reader = file::ObjectFileReader(path, sizeof(hal::gps::GpsConfiguration));
if (!reader.open()) {
TT_LOG_E(TAG, "Failed to open configuration file");
return false;
}
hal::gps::GpsConfiguration configuration;
while (reader.hasNext()) {
if (!reader.readNext(&configuration)) {
TT_LOG_E(TAG, "Failed to read configuration");
reader.close();
return false;
} else {
configurations.push_back(configuration);
}
}
return true;
}
bool GpsService::addGpsConfiguration(hal::gps::GpsConfiguration configuration) {
std::string path;
if (!getConfigurationFilePath(path)) {
return false;
}
auto appender = file::ObjectFileWriter(path, sizeof(hal::gps::GpsConfiguration), 1, true);
if (!appender.open()) {
TT_LOG_E(TAG, "Failed to open/create configuration file");
return false;
}
if (!appender.write(&configuration)) {
TT_LOG_E(TAG, "Failed to add configuration");
appender.close();
return false;
}
appender.close();
return true;
}
bool GpsService::removeGpsConfiguration(hal::gps::GpsConfiguration configuration) {
std::string path;
if (!getConfigurationFilePath(path)) {
return false;
}
std::vector<hal::gps::GpsConfiguration> configurations;
if (!getGpsConfigurations(configurations)) {
TT_LOG_E(TAG, "Failed to get gps configurations");
return false;
}
auto count = std::erase_if(configurations, [&configuration](auto& item) {
return strcmp(item.uartName, configuration.uartName) == 0 &&
item.baudRate == configuration.baudRate &&
item.model == configuration.model;
});
auto writer = file::ObjectFileWriter(path, sizeof(hal::gps::GpsConfiguration), 1, false);
if (!writer.open()) {
TT_LOG_E(TAG, "Failed to open configuration file");
return false;
}
for (auto& configuration : configurations) {
writer.write(&configuration);
}
writer.close();
return count > 0;
}
} // namespace tt::service::gps
@@ -1,12 +1,17 @@
#include "Tactility/service/gps/GpsService.h"
#include "Tactility/service/ServiceManifest.h"
#include "Tactility/service/ServiceRegistry.h"
#define TAG "gps_service"
#include <Tactility/Log.h>
#include <Tactility/file/File.h>
using tt::hal::gps::GpsDevice;
namespace tt::service::gps {
constexpr const char* TAG = "GpsService";
extern const ServiceManifest manifest;
constexpr inline bool hasTimeElapsed(TickType_t now, TickType_t timeInThePast, TickType_t expireTimeInTicks) {
return (TickType_t)(now - timeInThePast) >= expireTimeInTicks;
}
@@ -15,7 +20,7 @@ GpsService::GpsDeviceRecord* _Nullable GpsService::findGpsRecord(const std::shar
auto lock = mutex.asScopedLock();
lock.lock();
auto result = std::views::filter(deviceRecords, [&device](auto& record){
auto result = std::views::filter(deviceRecords, [&device](auto& record) {
return record.device.get() == device.get();
});
if (!result.empty()) {
@@ -29,7 +34,7 @@ void GpsService::addGpsDevice(const std::shared_ptr<GpsDevice>& device) {
auto lock = mutex.asScopedLock();
lock.lock();
GpsDeviceRecord record = { .device = device };
GpsDeviceRecord record = {.device = device};
if (getState() == State::On) { // Ignore during OnPending due to risk of data corruptiohn
startGpsDevice(record);
@@ -48,25 +53,19 @@ void GpsService::removeGpsDevice(const std::shared_ptr<GpsDevice>& device) {
stopGpsDevice(*record);
}
std::erase_if(deviceRecords, [&device](auto& reference){
std::erase_if(deviceRecords, [&device](auto& reference) {
return reference.device.get() == device.get();
});
}
void GpsService::onStart(tt::service::ServiceContext &serviceContext) {
void GpsService::onStart(tt::service::ServiceContext& serviceContext) {
auto lock = mutex.asScopedLock();
lock.lock();
deviceRecords.clear();
auto devices = hal::findDevices<GpsDevice>(hal::Device::Type::Gps);
for (auto& device : devices) {
TT_LOG_I(TAG, "[device %lu] added", device->getId());
addGpsDevice(device);
}
paths = serviceContext.getPaths();
}
void GpsService::onStop(tt::service::ServiceContext &serviceContext) {
void GpsService::onStop(tt::service::ServiceContext& serviceContext) {
if (getState() == State::On) {
stopReceiving();
}
@@ -126,14 +125,39 @@ bool GpsService::stopGpsDevice(GpsDeviceRecord& record) {
bool GpsService::startReceiving() {
TT_LOG_I(TAG, "Start receiving");
if (getState() != State::Off) {
TT_LOG_E(TAG, "Already receiving");
return false;
}
setState(State::OnPending);
auto lock = mutex.asScopedLock();
lock.lock();
deviceRecords.clear();
std::vector<hal::gps::GpsConfiguration> configurations;
if (!getGpsConfigurations(configurations)) {
TT_LOG_E(TAG, "Failed to get GPS configurations");
setState(State::Off);
return false;
}
if (configurations.empty()) {
TT_LOG_E(TAG, "No GPS configurations");
setState(State::Off);
return false;
}
for (const auto& configuration: configurations) {
auto device = std::make_shared<GpsDevice>(configuration);
addGpsDevice(device);
}
bool started_one_or_more = false;
for (auto& record : deviceRecords) {
for (auto& record: deviceRecords) {
started_one_or_more |= startGpsDevice(record);
}
@@ -156,7 +180,7 @@ void GpsService::stopReceiving() {
auto lock = mutex.asScopedLock();
lock.lock();
for (auto& record : deviceRecords) {
for (auto& record: deviceRecords) {
stopGpsDevice(record);
}
@@ -202,9 +226,15 @@ bool GpsService::getCoordinates(minmea_sentence_rmc& rmc) const {
}
}
std::shared_ptr<GpsService> findGpsService() {
auto service = findServiceById(manifest.id);
assert(service != nullptr);
return std::static_pointer_cast<GpsService>(service);
}
extern const ServiceManifest manifest = {
.id = "Gps",
.createService = create<GpsService>
};
} // tt::hal::gps
} // namespace tt::service::gps