mirror of
https://github.com/usatiuk/cardboy.git
synced 2025-10-28 23:27:49 +01:00
some refactoring
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
namespace cardboy::backend::esp {
|
||||
|
||||
@@ -16,12 +16,12 @@ public:
|
||||
void signal(std::uint32_t bits) override;
|
||||
void signalFromISR(std::uint32_t bits) override;
|
||||
std::uint32_t wait(std::uint32_t mask, std::uint32_t timeout_ms) override;
|
||||
void scheduleTimerSignal(std::uint32_t delay_ms) override;
|
||||
void cancelTimerSignal() override;
|
||||
void postEvent(const cardboy::sdk::AppEvent& event) override;
|
||||
bool popEvent(cardboy::sdk::AppEvent& outEvent) override;
|
||||
|
||||
private:
|
||||
EventGroupHandle_t group;
|
||||
TimerHandle_t timer;
|
||||
QueueHandle_t eventQueue;
|
||||
};
|
||||
|
||||
} // namespace cardboy::backend::esp
|
||||
|
||||
@@ -62,6 +62,7 @@ private:
|
||||
class FilesystemService;
|
||||
class LoopHooksService;
|
||||
class NotificationService;
|
||||
class TimerService;
|
||||
|
||||
std::unique_ptr<BuzzerService> buzzerService;
|
||||
std::unique_ptr<BatteryService> batteryService;
|
||||
@@ -70,6 +71,7 @@ private:
|
||||
std::unique_ptr<HighResClockService> highResClockService;
|
||||
std::unique_ptr<FilesystemService> filesystemService;
|
||||
std::unique_ptr<EventBus> eventBus;
|
||||
std::unique_ptr<TimerService> timerService;
|
||||
std::unique_ptr<LoopHooksService> loopHooksService;
|
||||
std::unique_ptr<NotificationService> notificationService;
|
||||
|
||||
|
||||
@@ -19,12 +19,15 @@
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -131,6 +134,63 @@ public:
|
||||
void onLoopIteration() override { vTaskDelay(1); }
|
||||
};
|
||||
|
||||
class EspRuntime::TimerService final : public cardboy::sdk::ITimerService {
|
||||
public:
|
||||
explicit TimerService(cardboy::sdk::IEventBus& bus);
|
||||
~TimerService() override;
|
||||
|
||||
cardboy::sdk::TimerClientId acquireClient() override;
|
||||
void releaseClient(cardboy::sdk::TimerClientId clientId) override;
|
||||
cardboy::sdk::AppTimerHandle scheduleTimer(cardboy::sdk::TimerClientId clientId, std::uint32_t delay_ms,
|
||||
bool repeat) override;
|
||||
void cancelTimer(cardboy::sdk::TimerClientId clientId, cardboy::sdk::AppTimerHandle handle) override;
|
||||
void cancelAllTimers(cardboy::sdk::TimerClientId clientId) override;
|
||||
|
||||
private:
|
||||
struct TimerRecord {
|
||||
TimerService* owner = nullptr;
|
||||
TimerHandle_t timer = nullptr;
|
||||
cardboy::sdk::TimerClientId clientId = cardboy::sdk::kInvalidTimerClient;
|
||||
cardboy::sdk::AppTimerHandle handle = cardboy::sdk::kInvalidAppTimer;
|
||||
bool repeat = false;
|
||||
bool active = true;
|
||||
};
|
||||
|
||||
class RecursiveLock {
|
||||
public:
|
||||
explicit RecursiveLock(SemaphoreHandle_t m) : mutex(m) { lock(); }
|
||||
~RecursiveLock() { unlock(); }
|
||||
|
||||
void lock() {
|
||||
if (mutex && !locked) {
|
||||
if (xSemaphoreTakeRecursive(mutex, portMAX_DELAY) == pdTRUE)
|
||||
locked = true;
|
||||
}
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
if (mutex && locked) {
|
||||
xSemaphoreGiveRecursive(mutex);
|
||||
locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SemaphoreHandle_t mutex;
|
||||
bool locked = false;
|
||||
};
|
||||
|
||||
static void timerCallback(TimerHandle_t timer);
|
||||
void handleTimer(TimerRecord* record);
|
||||
void shutdown();
|
||||
|
||||
cardboy::sdk::IEventBus& eventBus;
|
||||
SemaphoreHandle_t mutex = nullptr;
|
||||
std::list<TimerRecord> timers;
|
||||
cardboy::sdk::AppTimerHandle nextTimerHandle = 1;
|
||||
cardboy::sdk::TimerClientId nextClientId = 1;
|
||||
};
|
||||
|
||||
class EspRuntime::NotificationService final : public cardboy::sdk::INotificationCenter {
|
||||
public:
|
||||
void pushNotification(Notification notification) override {
|
||||
@@ -247,6 +307,216 @@ private:
|
||||
std::uint32_t revisionCounter = 0;
|
||||
};
|
||||
|
||||
EspRuntime::TimerService::TimerService(cardboy::sdk::IEventBus& bus) : eventBus(bus) {
|
||||
mutex = xSemaphoreCreateRecursiveMutex();
|
||||
}
|
||||
|
||||
EspRuntime::TimerService::~TimerService() {
|
||||
shutdown();
|
||||
if (mutex)
|
||||
vSemaphoreDelete(mutex);
|
||||
}
|
||||
|
||||
cardboy::sdk::TimerClientId EspRuntime::TimerService::acquireClient() {
|
||||
if (!mutex)
|
||||
return cardboy::sdk::kInvalidTimerClient;
|
||||
RecursiveLock lock(mutex);
|
||||
cardboy::sdk::TimerClientId id = cardboy::sdk::kInvalidTimerClient;
|
||||
do {
|
||||
id = nextClientId++;
|
||||
} while (id == cardboy::sdk::kInvalidTimerClient);
|
||||
if (nextClientId == cardboy::sdk::kInvalidTimerClient)
|
||||
++nextClientId;
|
||||
return id;
|
||||
}
|
||||
|
||||
void EspRuntime::TimerService::releaseClient(cardboy::sdk::TimerClientId clientId) { cancelAllTimers(clientId); }
|
||||
|
||||
cardboy::sdk::AppTimerHandle EspRuntime::TimerService::scheduleTimer(cardboy::sdk::TimerClientId clientId,
|
||||
std::uint32_t delay_ms, bool repeat) {
|
||||
if (!mutex || clientId == cardboy::sdk::kInvalidTimerClient)
|
||||
return cardboy::sdk::kInvalidAppTimer;
|
||||
|
||||
TimerRecord* storedRecord = nullptr;
|
||||
{
|
||||
RecursiveLock lock(mutex);
|
||||
TimerRecord record{};
|
||||
record.owner = this;
|
||||
record.clientId = clientId;
|
||||
record.repeat = repeat;
|
||||
record.active = true;
|
||||
cardboy::sdk::AppTimerHandle newHandle = cardboy::sdk::kInvalidAppTimer;
|
||||
do {
|
||||
newHandle = nextTimerHandle++;
|
||||
} while (newHandle == cardboy::sdk::kInvalidAppTimer);
|
||||
if (nextTimerHandle == cardboy::sdk::kInvalidAppTimer)
|
||||
++nextTimerHandle;
|
||||
record.handle = newHandle;
|
||||
timers.emplace_back(record);
|
||||
storedRecord = &timers.back();
|
||||
}
|
||||
|
||||
const std::uint32_t effectiveDelay = std::max<std::uint32_t>(1, delay_ms);
|
||||
const TickType_t ticks = std::max<TickType_t>(pdMS_TO_TICKS(effectiveDelay), 1);
|
||||
|
||||
TimerHandle_t timerHandle =
|
||||
xTimerCreate("AppSvcTimer", ticks, repeat ? pdTRUE : pdFALSE, storedRecord, &TimerService::timerCallback);
|
||||
if (!timerHandle) {
|
||||
RecursiveLock lock(mutex);
|
||||
for (auto it = timers.begin(); it != timers.end(); ++it) {
|
||||
if (&(*it) == storedRecord) {
|
||||
timers.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
|
||||
{
|
||||
RecursiveLock lock(mutex);
|
||||
storedRecord->timer = timerHandle;
|
||||
}
|
||||
|
||||
if (xTimerStart(timerHandle, 0) != pdPASS) {
|
||||
cancelTimer(clientId, storedRecord->handle);
|
||||
return cardboy::sdk::kInvalidAppTimer;
|
||||
}
|
||||
|
||||
return storedRecord->handle;
|
||||
}
|
||||
|
||||
void EspRuntime::TimerService::cancelTimer(cardboy::sdk::TimerClientId clientId,
|
||||
cardboy::sdk::AppTimerHandle handle) {
|
||||
if (!mutex || clientId == cardboy::sdk::kInvalidTimerClient || handle == cardboy::sdk::kInvalidAppTimer)
|
||||
return;
|
||||
|
||||
TimerHandle_t timerHandle = nullptr;
|
||||
{
|
||||
RecursiveLock lock(mutex);
|
||||
for (auto& record: timers) {
|
||||
if (record.clientId == clientId && record.handle == handle) {
|
||||
record.active = false;
|
||||
timerHandle = record.timer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!timerHandle)
|
||||
return;
|
||||
|
||||
xTimerStop(timerHandle, portMAX_DELAY);
|
||||
xTimerDelete(timerHandle, portMAX_DELAY);
|
||||
|
||||
{
|
||||
RecursiveLock lock(mutex);
|
||||
for (auto it = timers.begin(); it != timers.end();) {
|
||||
if (it->clientId == clientId && it->handle == handle)
|
||||
it = timers.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EspRuntime::TimerService::cancelAllTimers(cardboy::sdk::TimerClientId clientId) {
|
||||
if (!mutex || clientId == cardboy::sdk::kInvalidTimerClient)
|
||||
return;
|
||||
|
||||
std::vector<TimerHandle_t> handles;
|
||||
{
|
||||
RecursiveLock lock(mutex);
|
||||
for (auto& record: timers) {
|
||||
if (record.clientId == clientId) {
|
||||
record.active = false;
|
||||
if (record.timer)
|
||||
handles.push_back(record.timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto timerHandle: handles) {
|
||||
xTimerStop(timerHandle, portMAX_DELAY);
|
||||
xTimerDelete(timerHandle, portMAX_DELAY);
|
||||
}
|
||||
|
||||
{
|
||||
RecursiveLock lock(mutex);
|
||||
for (auto it = timers.begin(); it != timers.end();) {
|
||||
if (it->clientId == clientId)
|
||||
it = timers.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EspRuntime::TimerService::timerCallback(TimerHandle_t timer) {
|
||||
auto* record = static_cast<TimerRecord*>(pvTimerGetTimerID(timer));
|
||||
if (!record || !record->owner)
|
||||
return;
|
||||
record->owner->handleTimer(record);
|
||||
}
|
||||
|
||||
void EspRuntime::TimerService::handleTimer(TimerRecord* record) {
|
||||
if (!record || !record->active)
|
||||
return;
|
||||
|
||||
const cardboy::sdk::TimerClientId clientId = record->clientId;
|
||||
const cardboy::sdk::AppTimerHandle handle = record->handle;
|
||||
const bool isRepeating = record->repeat;
|
||||
TimerHandle_t timerHandle = record->timer;
|
||||
|
||||
if (!isRepeating) {
|
||||
record->active = false;
|
||||
{
|
||||
RecursiveLock lock(mutex);
|
||||
for (auto it = timers.begin(); it != timers.end();) {
|
||||
if (&(*it) == record)
|
||||
it = timers.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (timerHandle)
|
||||
xTimerDelete(timerHandle, 0);
|
||||
}
|
||||
|
||||
cardboy::sdk::AppTimerEvent timerEvent{};
|
||||
timerEvent.clientId = clientId;
|
||||
timerEvent.handle = handle;
|
||||
|
||||
cardboy::sdk::AppEvent event{};
|
||||
event.timestamp_ms = static_cast<std::uint32_t>(esp_timer_get_time() / 1000ULL);
|
||||
event.data = timerEvent;
|
||||
eventBus.postEvent(event);
|
||||
}
|
||||
|
||||
void EspRuntime::TimerService::shutdown() {
|
||||
if (!mutex)
|
||||
return;
|
||||
|
||||
std::vector<TimerHandle_t> handles;
|
||||
{
|
||||
RecursiveLock lock(mutex);
|
||||
for (auto& record: timers) {
|
||||
record.active = false;
|
||||
if (record.timer)
|
||||
handles.push_back(record.timer);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto timerHandle: handles) {
|
||||
xTimerStop(timerHandle, portMAX_DELAY);
|
||||
xTimerDelete(timerHandle, portMAX_DELAY);
|
||||
}
|
||||
|
||||
{
|
||||
RecursiveLock lock(mutex);
|
||||
timers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
EspRuntime::EspRuntime() : framebuffer(), input(), clock() {
|
||||
initializeHardware();
|
||||
|
||||
@@ -257,6 +527,7 @@ EspRuntime::EspRuntime() : framebuffer(), input(), clock() {
|
||||
highResClockService = std::make_unique<HighResClockService>();
|
||||
filesystemService = std::make_unique<FilesystemService>();
|
||||
eventBus = std::make_unique<EventBus>();
|
||||
timerService = std::make_unique<TimerService>(*eventBus);
|
||||
loopHooksService = std::make_unique<LoopHooksService>();
|
||||
notificationService = std::make_unique<NotificationService>();
|
||||
|
||||
@@ -267,6 +538,7 @@ EspRuntime::EspRuntime() : framebuffer(), input(), clock() {
|
||||
services.highResClock = highResClockService.get();
|
||||
services.filesystem = filesystemService.get();
|
||||
services.eventBus = eventBus.get();
|
||||
services.timer = timerService.get();
|
||||
services.loopHooks = loopHooksService.get();
|
||||
services.notifications = notificationService.get();
|
||||
|
||||
|
||||
@@ -14,22 +14,19 @@ namespace {
|
||||
return portMAX_DELAY;
|
||||
return pdMS_TO_TICKS(timeout_ms);
|
||||
}
|
||||
|
||||
constexpr UBaseType_t kEventQueueLength = 16;
|
||||
} // namespace
|
||||
|
||||
static void timerCallback(TimerHandle_t handle) {
|
||||
auto* bus = static_cast<EventBus*>(pvTimerGetTimerID(handle));
|
||||
if (bus)
|
||||
bus->signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer));
|
||||
}
|
||||
|
||||
EventBus::EventBus() :
|
||||
group(xEventGroupCreate()), timer(xTimerCreate("EventBusTimer", pdMS_TO_TICKS(1), pdFALSE, this, timerCallback)) {}
|
||||
group(xEventGroupCreate()),
|
||||
eventQueue(xQueueCreate(kEventQueueLength, sizeof(cardboy::sdk::AppEvent))) {}
|
||||
|
||||
EventBus::~EventBus() {
|
||||
if (timer)
|
||||
xTimerDelete(timer, portMAX_DELAY);
|
||||
if (group)
|
||||
vEventGroupDelete(group);
|
||||
if (eventQueue)
|
||||
vQueueDelete(eventQueue);
|
||||
}
|
||||
|
||||
void EventBus::signal(std::uint32_t bits) {
|
||||
@@ -54,28 +51,23 @@ std::uint32_t EventBus::wait(std::uint32_t mask, std::uint32_t timeout_ms) {
|
||||
return static_cast<std::uint32_t>(bits & mask);
|
||||
}
|
||||
|
||||
void EventBus::scheduleTimerSignal(std::uint32_t delay_ms) {
|
||||
if (!timer)
|
||||
return;
|
||||
xTimerStop(timer, 0);
|
||||
|
||||
if (delay_ms == cardboy::sdk::IEventBus::kWaitForever)
|
||||
void EventBus::postEvent(const cardboy::sdk::AppEvent& event) {
|
||||
if (!eventQueue)
|
||||
return;
|
||||
|
||||
if (delay_ms == 0) {
|
||||
signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer));
|
||||
return;
|
||||
if (xQueueSend(eventQueue, &event, 0) == errQUEUE_FULL) {
|
||||
cardboy::sdk::AppEvent discarded{};
|
||||
if (xQueueReceive(eventQueue, &discarded, 0) == pdTRUE)
|
||||
xQueueSend(eventQueue, &event, 0);
|
||||
}
|
||||
|
||||
const TickType_t ticks = std::max<TickType_t>(pdMS_TO_TICKS(delay_ms), 1);
|
||||
if (xTimerChangePeriod(timer, ticks, 0) == pdPASS)
|
||||
xTimerStart(timer, 0);
|
||||
signal(cardboy::sdk::to_event_bits(cardboy::sdk::EventBusSignal::Timer));
|
||||
}
|
||||
|
||||
void EventBus::cancelTimerSignal() {
|
||||
if (!timer)
|
||||
return;
|
||||
xTimerStop(timer, 0);
|
||||
bool EventBus::popEvent(cardboy::sdk::AppEvent& outEvent) {
|
||||
if (!eventQueue)
|
||||
return false;
|
||||
return xQueueReceive(eventQueue, &outEvent, 0) == pdTRUE;
|
||||
}
|
||||
|
||||
} // namespace cardboy::backend::esp
|
||||
|
||||
Reference in New Issue
Block a user