summaryrefslogtreecommitdiff
path: root/src/system_fsm
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-08-30 16:48:10 +1000
committerjacqueline <me@jacqueline.id.au>2023-08-30 16:48:10 +1000
commit320fdeb9d8355d3c361d5c6d60de8afc64501af9 (patch)
treef0d5a2ab82199c78ad6768c6b18ba1239a0b7ee4 /src/system_fsm
parent4247c9fe7d25c921fbfc73fc50e849c8780e7ad6 (diff)
downloadtangara-fw-320fdeb9d8355d3c361d5c6d60de8afc64501af9.tar.gz
Use a service locator instead of passing around subsets of drivers between FSMs
Diffstat (limited to 'src/system_fsm')
-rw-r--r--src/system_fsm/CMakeLists.txt2
-rw-r--r--src/system_fsm/booting.cpp57
-rw-r--r--src/system_fsm/idle.cpp26
-rw-r--r--src/system_fsm/include/service_locator.hpp113
-rw-r--r--src/system_fsm/include/system_events.hpp9
-rw-r--r--src/system_fsm/include/system_fsm.hpp19
-rw-r--r--src/system_fsm/running.cpp15
-rw-r--r--src/system_fsm/service_locator.cpp21
-rw-r--r--src/system_fsm/system_fsm.cpp53
9 files changed, 211 insertions, 104 deletions
diff --git a/src/system_fsm/CMakeLists.txt b/src/system_fsm/CMakeLists.txt
index fced4093..449e14cc 100644
--- a/src/system_fsm/CMakeLists.txt
+++ b/src/system_fsm/CMakeLists.txt
@@ -3,7 +3,7 @@
# SPDX-License-Identifier: GPL-3.0-only
idf_component_register(
- SRCS "system_fsm.cpp" "running.cpp" "booting.cpp" "idle.cpp"
+ SRCS "system_fsm.cpp" "running.cpp" "booting.cpp" "idle.cpp" "service_locator.cpp"
INCLUDE_DIRS "include"
REQUIRES "tinyfsm" "drivers" "database" "ui" "result" "events" "audio" "app_console" "battery")
target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS})
diff --git a/src/system_fsm/booting.cpp b/src/system_fsm/booting.cpp
index a988c622..006ed395 100644
--- a/src/system_fsm/booting.cpp
+++ b/src/system_fsm/booting.cpp
@@ -6,8 +6,10 @@
#include <stdint.h>
+#include "adc.hpp"
#include "assert.h"
#include "audio_fsm.hpp"
+#include "battery.hpp"
#include "bluetooth.hpp"
#include "core/lv_obj.h"
#include "display_init.hpp"
@@ -23,6 +25,7 @@
#include "nvs.hpp"
#include "relative_wheel.hpp"
#include "samd.hpp"
+#include "service_locator.hpp"
#include "spi.hpp"
#include "system_events.hpp"
#include "system_fsm.hpp"
@@ -40,65 +43,55 @@ static const char kTag[] = "BOOT";
auto Booting::entry() -> void {
ESP_LOGI(kTag, "beginning tangara boot");
- ESP_LOGI(kTag, "installing early drivers");
+ sServices.reset(new ServiceLocator());
+ ESP_LOGI(kTag, "installing early drivers");
// I2C and SPI are both always needed. We can't even power down or show an
// error without these.
ESP_ERROR_CHECK(drivers::init_spi());
- sGpios.reset(drivers::Gpios::Create());
-
- sSamd.reset(drivers::Samd::Create());
- sAdc.reset(drivers::AdcBattery::Create());
- sNvs.reset(drivers::NvsStorage::OpenSync());
- assert(sSamd.get() && sAdc.get() && sNvs.get());
+ sServices->gpios(std::unique_ptr<drivers::Gpios>(drivers::Gpios::Create()));
- sBattery.reset(new battery::Battery(sSamd.get(), sAdc.get()));
-
- // Start bringing up LVGL now, since we have all of its prerequisites.
- sTrackQueue.reset(new audio::TrackQueue());
ESP_LOGI(kTag, "starting ui");
- if (!ui::UiState::Init(sGpios.get(), sNvs, sTrackQueue.get(), sBattery)) {
+ if (!ui::UiState::InitBootSplash(sServices->gpios())) {
events::System().Dispatch(FatalError{});
return;
}
- // Install everything else that is certain to be needed.
ESP_LOGI(kTag, "installing remaining drivers");
- sTagParser.reset(new database::TagParserImpl());
+ sServices->samd(std::unique_ptr<drivers::Samd>(drivers::Samd::Create()));
+ sServices->nvs(
+ std::unique_ptr<drivers::NvsStorage>(drivers::NvsStorage::OpenSync()));
+ sServices->touchwheel(
+ std::unique_ptr<drivers::TouchWheel>{drivers::TouchWheel::Create()});
+
+ auto adc = drivers::AdcBattery::Create();
+ sServices->battery(std::make_unique<battery::Battery>(
+ sServices->samd(), std::unique_ptr<drivers::AdcBattery>(adc)));
+
+ sServices->track_queue(std::make_unique<audio::TrackQueue>());
+ sServices->tag_parser(std::make_unique<database::TagParserImpl>());
// ESP_LOGI(kTag, "starting bluetooth");
// sBluetooth.reset(new drivers::Bluetooth(sNvs.get()));
// sBluetooth->Enable();
- // At this point we've done all of the essential boot tasks. Start remaining
- // state machines and inform them that the system is ready.
-
- ESP_LOGI(kTag, "starting audio");
- if (!audio::AudioState::Init(sGpios.get(), sDatabase, sTagParser,
- sBluetooth.get(), sTrackQueue.get())) {
- events::System().Dispatch(FatalError{});
- events::Ui().Dispatch(FatalError{});
- return;
- }
-
- events::System().Dispatch(BootComplete{});
- events::Audio().Dispatch(BootComplete{});
- events::Ui().Dispatch(BootComplete{});
+ BootComplete ev{.services = sServices};
+ events::Audio().Dispatch(ev);
+ events::Ui().Dispatch(ev);
+ events::System().Dispatch(ev);
}
auto Booting::exit() -> void {
// TODO(jacqueline): Gate this on something. Debug flag? Flashing mode?
sAppConsole = new console::AppConsole();
- sAppConsole->sTrackQueue = sTrackQueue.get();
- sAppConsole->sBluetooth = sBluetooth.get();
- sAppConsole->sSamd = sSamd.get();
+ sAppConsole->sServices = sServices;
sAppConsole->Launch();
}
auto Booting::react(const BootComplete& ev) -> void {
ESP_LOGI(kTag, "bootup completely successfully");
- if (sGpios->Get(drivers::Gpios::Pin::kKeyLock)) {
+ if (sServices->gpios().Get(drivers::Gpios::Pin::kKeyLock)) {
transit<Running>();
} else {
transit<Idle>();
diff --git a/src/system_fsm/idle.cpp b/src/system_fsm/idle.cpp
index 7cc1fa39..bd327134 100644
--- a/src/system_fsm/idle.cpp
+++ b/src/system_fsm/idle.cpp
@@ -64,30 +64,32 @@ void Idle::react(const internal::IdleTimeout& ev) {
// FIXME: It would be neater to just free a bunch of our pointers, deinit the
// other state machines, etc.
- if (sTouch) {
- sTouch->PowerDown();
+ auto touchwheel = sServices->touchwheel();
+ if (touchwheel) {
+ touchwheel.value()->PowerDown();
}
+ auto& gpios = sServices->gpios();
// Pull down to turn things off
- sGpios->WriteBuffered(drivers::IGpios::Pin::kAmplifierEnable, false);
- sGpios->WriteBuffered(drivers::IGpios::Pin::kSdPowerEnable, false);
- sGpios->WriteBuffered(drivers::IGpios::Pin::kDisplayEnable, false);
+ gpios.WriteBuffered(drivers::IGpios::Pin::kAmplifierEnable, false);
+ gpios.WriteBuffered(drivers::IGpios::Pin::kSdPowerEnable, false);
+ gpios.WriteBuffered(drivers::IGpios::Pin::kDisplayEnable, false);
// Leave up to match the external pullups
- sGpios->WriteBuffered(drivers::IGpios::Pin::kSdMuxSwitch, true);
- sGpios->WriteBuffered(drivers::IGpios::Pin::kSdMuxDisable, true);
+ gpios.WriteBuffered(drivers::IGpios::Pin::kSdMuxSwitch, true);
+ gpios.WriteBuffered(drivers::IGpios::Pin::kSdMuxDisable, true);
// Pull down to prevent sourcing current uselessly from input pins.
- sGpios->WriteBuffered(drivers::IGpios::Pin::kSdCardDetect, false);
- sGpios->WriteBuffered(drivers::IGpios::Pin::kKeyUp, false);
- sGpios->WriteBuffered(drivers::IGpios::Pin::kKeyDown, false);
+ gpios.WriteBuffered(drivers::IGpios::Pin::kSdCardDetect, false);
+ gpios.WriteBuffered(drivers::IGpios::Pin::kKeyUp, false);
+ gpios.WriteBuffered(drivers::IGpios::Pin::kKeyDown, false);
- sGpios->Flush();
+ gpios.Flush();
// Retry shutting down in case of a transient failure with the SAMD. e.g. i2c
// timeouts. This guards against a buggy SAMD firmware preventing idle.
for (;;) {
- sSamd->PowerDown();
+ sServices->samd().PowerDown();
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
diff --git a/src/system_fsm/include/service_locator.hpp b/src/system_fsm/include/service_locator.hpp
new file mode 100644
index 00000000..00285ed5
--- /dev/null
+++ b/src/system_fsm/include/service_locator.hpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#pragma once
+
+#include <memory>
+
+#include "battery.hpp"
+#include "bluetooth.hpp"
+#include "database.hpp"
+#include "gpios.hpp"
+#include "nvs.hpp"
+#include "samd.hpp"
+#include "tag_parser.hpp"
+#include "touchwheel.hpp"
+#include "track_queue.hpp"
+
+namespace system_fsm {
+
+class ServiceLocator {
+ public:
+ static auto instance() -> ServiceLocator&;
+
+ auto gpios() -> drivers::Gpios& {
+ assert(gpios_ != nullptr);
+ return *gpios_;
+ }
+
+ auto gpios(std::unique_ptr<drivers::Gpios> i) { gpios_ = std::move(i); }
+
+ auto samd() -> drivers::Samd& {
+ assert(samd_ != nullptr);
+ return *samd_;
+ }
+
+ auto samd(std::unique_ptr<drivers::Samd> i) { samd_ = std::move(i); }
+
+ auto nvs() -> drivers::NvsStorage& {
+ assert(nvs_ != nullptr);
+ return *nvs_;
+ }
+
+ auto nvs(std::unique_ptr<drivers::NvsStorage> i) { nvs_ = std::move(i); }
+
+ auto bluetooth() -> drivers::Bluetooth& {
+ assert(bluetooth_ != nullptr);
+ return *bluetooth_;
+ }
+
+ auto bluetooth(std::unique_ptr<drivers::Bluetooth> i) {
+ bluetooth_ = std::move(i);
+ }
+
+ auto battery() -> battery::Battery& {
+ assert(battery_ != nullptr);
+ return *battery_;
+ }
+
+ auto battery(std::unique_ptr<battery::Battery> i) { battery_ = std::move(i); }
+
+ auto touchwheel() -> std::optional<drivers::TouchWheel*> {
+ if (!touchwheel_) {
+ return {};
+ }
+ return touchwheel_.get();
+ }
+
+ auto touchwheel(std::unique_ptr<drivers::TouchWheel> i) {
+ touchwheel_ = std::move(i);
+ }
+
+ auto database() -> std::weak_ptr<database::Database> { return database_; }
+
+ auto database(std::unique_ptr<database::Database> i) {
+ database_ = std::move(i);
+ }
+
+ auto tag_parser() -> database::ITagParser& {
+ assert(tag_parser_ != nullptr);
+ return *tag_parser_;
+ }
+
+ auto tag_parser(std::unique_ptr<database::ITagParser> i) {
+ tag_parser_ = std::move(i);
+ }
+
+ auto track_queue() -> audio::TrackQueue& {
+ assert(queue_ != nullptr);
+ return *queue_;
+ }
+
+ auto track_queue(std::unique_ptr<audio::TrackQueue> i) {
+ queue_ = std::move(i);
+ }
+
+ private:
+ std::unique_ptr<drivers::Gpios> gpios_;
+ std::unique_ptr<drivers::Samd> samd_;
+ std::unique_ptr<drivers::NvsStorage> nvs_;
+ std::unique_ptr<drivers::TouchWheel> touchwheel_;
+ std::unique_ptr<drivers::Bluetooth> bluetooth_;
+
+ std::unique_ptr<audio::TrackQueue> queue_;
+ std::unique_ptr<battery::Battery> battery_;
+
+ std::shared_ptr<database::Database> database_;
+ std::unique_ptr<database::ITagParser> tag_parser_;
+};
+
+} // namespace system_fsm
diff --git a/src/system_fsm/include/system_events.hpp b/src/system_fsm/include/system_events.hpp
index 64cbd393..e22fe2ae 100644
--- a/src/system_fsm/include/system_events.hpp
+++ b/src/system_fsm/include/system_events.hpp
@@ -9,6 +9,7 @@
#include <memory>
#include "database.hpp"
+#include "service_locator.hpp"
#include "tinyfsm.hpp"
namespace system_fsm {
@@ -19,7 +20,9 @@ struct DisplayReady : tinyfsm::Event {};
* Sent by SysState when the system has finished with its boot and self-test,
* and is now ready to run normally.
*/
-struct BootComplete : tinyfsm::Event {};
+struct BootComplete : tinyfsm::Event {
+ std::shared_ptr<ServiceLocator> services;
+};
/*
* May be sent by any component to indicate that the system has experienced an
@@ -33,9 +36,7 @@ struct OnIdle : tinyfsm::Event {};
/*
* Sent by SysState when the system storage has been successfully mounted.
*/
-struct StorageMounted : tinyfsm::Event {
- std::weak_ptr<database::Database> db;
-};
+struct StorageMounted : tinyfsm::Event {};
struct StorageError : tinyfsm::Event {};
diff --git a/src/system_fsm/include/system_fsm.hpp b/src/system_fsm/include/system_fsm.hpp
index 371e5527..28448e5a 100644
--- a/src/system_fsm/include/system_fsm.hpp
+++ b/src/system_fsm/include/system_fsm.hpp
@@ -18,6 +18,7 @@
#include "nvs.hpp"
#include "relative_wheel.hpp"
#include "samd.hpp"
+#include "service_locator.hpp"
#include "storage.hpp"
#include "tag_parser.hpp"
#include "tinyfsm.hpp"
@@ -60,22 +61,8 @@ class SystemState : public tinyfsm::Fsm<SystemState> {
protected:
auto IdleCondition() -> bool;
- static std::shared_ptr<drivers::Gpios> sGpios;
- static std::shared_ptr<drivers::Samd> sSamd;
- static std::shared_ptr<drivers::NvsStorage> sNvs;
-
- static std::shared_ptr<drivers::TouchWheel> sTouch;
- static std::shared_ptr<drivers::RelativeWheel> sRelativeTouch;
- static std::shared_ptr<drivers::AdcBattery> sAdc;
- static std::shared_ptr<battery::Battery> sBattery;
- static std::shared_ptr<drivers::SdStorage> sStorage;
- static std::shared_ptr<drivers::Display> sDisplay;
- static std::shared_ptr<drivers::Bluetooth> sBluetooth;
-
- static std::shared_ptr<database::Database> sDatabase;
- static std::shared_ptr<database::TagParserImpl> sTagParser;
-
- static std::shared_ptr<audio::TrackQueue> sTrackQueue;
+ static std::shared_ptr<ServiceLocator> sServices;
+ static std::unique_ptr<drivers::SdStorage> sStorage;
static console::AppConsole* sAppConsole;
};
diff --git a/src/system_fsm/running.cpp b/src/system_fsm/running.cpp
index 3fc5493f..e42429e7 100644
--- a/src/system_fsm/running.cpp
+++ b/src/system_fsm/running.cpp
@@ -6,6 +6,7 @@
#include "app_console.hpp"
#include "audio_events.hpp"
+#include "database.hpp"
#include "file_gatherer.hpp"
#include "freertos/projdefs.h"
#include "result.hpp"
@@ -31,7 +32,7 @@ static database::IFileGatherer* sFileGatherer;
void Running::entry() {
ESP_LOGI(kTag, "mounting sd card");
vTaskDelay(pdMS_TO_TICKS(250));
- auto storage_res = drivers::SdStorage::Create(sGpios.get());
+ auto storage_res = drivers::SdStorage::Create(sServices->gpios());
if (storage_res.has_error()) {
ESP_LOGW(kTag, "failed to mount!");
@@ -41,11 +42,11 @@ void Running::entry() {
return;
}
sStorage.reset(storage_res.value());
- vTaskDelay(pdMS_TO_TICKS(250));
ESP_LOGI(kTag, "opening database");
sFileGatherer = new database::FileGathererImpl();
- auto database_res = database::Database::Open(sFileGatherer, sTagParser.get());
+ auto database_res =
+ database::Database::Open(*sFileGatherer, sServices->tag_parser());
if (database_res.has_error()) {
ESP_LOGW(kTag, "failed to open!");
events::System().Dispatch(StorageError{});
@@ -53,18 +54,18 @@ void Running::entry() {
events::Ui().Dispatch(StorageError{});
return;
}
- sDatabase.reset(database_res.value());
- console::AppConsole::sDatabase = sDatabase;
+ sServices->database(
+ std::unique_ptr<database::Database>{database_res.value()});
ESP_LOGI(kTag, "storage loaded okay");
- StorageMounted ev{.db = sDatabase};
+ StorageMounted ev{};
events::System().Dispatch(ev);
events::Audio().Dispatch(ev);
events::Ui().Dispatch(ev);
}
void Running::exit() {
- sDatabase.reset();
+ sServices->database({});
sStorage.reset();
}
diff --git a/src/system_fsm/service_locator.cpp b/src/system_fsm/service_locator.cpp
new file mode 100644
index 00000000..1d4d8a65
--- /dev/null
+++ b/src/system_fsm/service_locator.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2023 jacqueline <me@jacqueline.id.au>
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "service_locator.hpp"
+
+#include <memory>
+
+#include "nvs.hpp"
+#include "touchwheel.hpp"
+
+namespace system_fsm {
+
+auto ServiceLocator::instance() -> ServiceLocator& {
+ static ServiceLocator sInstance{};
+ return sInstance;
+}
+
+} // namespace system_fsm
diff --git a/src/system_fsm/system_fsm.cpp b/src/system_fsm/system_fsm.cpp
index d21e8bcb..e048cec7 100644
--- a/src/system_fsm/system_fsm.cpp
+++ b/src/system_fsm/system_fsm.cpp
@@ -9,6 +9,7 @@
#include "event_queue.hpp"
#include "gpios.hpp"
#include "relative_wheel.hpp"
+#include "service_locator.hpp"
#include "system_events.hpp"
#include "tag_parser.hpp"
#include "track_queue.hpp"
@@ -17,22 +18,8 @@ static const char kTag[] = "system";
namespace system_fsm {
-std::shared_ptr<drivers::Gpios> SystemState::sGpios;
-std::shared_ptr<drivers::Samd> SystemState::sSamd;
-std::shared_ptr<drivers::NvsStorage> SystemState::sNvs;
-
-std::shared_ptr<drivers::TouchWheel> SystemState::sTouch;
-std::shared_ptr<drivers::RelativeWheel> SystemState::sRelativeTouch;
-std::shared_ptr<drivers::AdcBattery> SystemState::sAdc;
-std::shared_ptr<battery::Battery> SystemState::sBattery;
-std::shared_ptr<drivers::SdStorage> SystemState::sStorage;
-std::shared_ptr<drivers::Display> SystemState::sDisplay;
-std::shared_ptr<drivers::Bluetooth> SystemState::sBluetooth;
-
-std::shared_ptr<database::Database> SystemState::sDatabase;
-std::shared_ptr<database::TagParserImpl> SystemState::sTagParser;
-
-std::shared_ptr<audio::TrackQueue> SystemState::sTrackQueue;
+std::shared_ptr<ServiceLocator> SystemState::sServices;
+std::unique_ptr<drivers::SdStorage> SystemState::sStorage;
console::AppConsole* SystemState::sAppConsole;
@@ -43,17 +30,18 @@ void SystemState::react(const FatalError& err) {
}
void SystemState::react(const internal::GpioInterrupt&) {
- bool prev_key_up = sGpios->Get(drivers::Gpios::Pin::kKeyUp);
- bool prev_key_down = sGpios->Get(drivers::Gpios::Pin::kKeyDown);
- bool prev_key_lock = sGpios->Get(drivers::Gpios::Pin::kKeyLock);
- bool prev_has_headphones = !sGpios->Get(drivers::Gpios::Pin::kPhoneDetect);
+ auto& gpios = sServices->gpios();
+ bool prev_key_up = gpios.Get(drivers::Gpios::Pin::kKeyUp);
+ bool prev_key_down = gpios.Get(drivers::Gpios::Pin::kKeyDown);
+ bool prev_key_lock = gpios.Get(drivers::Gpios::Pin::kKeyLock);
+ bool prev_has_headphones = !gpios.Get(drivers::Gpios::Pin::kPhoneDetect);
- sGpios->Read();
+ gpios.Read();
- bool key_up = sGpios->Get(drivers::Gpios::Pin::kKeyUp);
- bool key_down = sGpios->Get(drivers::Gpios::Pin::kKeyDown);
- bool key_lock = sGpios->Get(drivers::Gpios::Pin::kKeyLock);
- bool has_headphones = !sGpios->Get(drivers::Gpios::Pin::kPhoneDetect);
+ bool key_up = gpios.Get(drivers::Gpios::Pin::kKeyUp);
+ bool key_down = gpios.Get(drivers::Gpios::Pin::kKeyDown);
+ bool key_lock = gpios.Get(drivers::Gpios::Pin::kKeyLock);
+ bool has_headphones = !gpios.Get(drivers::Gpios::Pin::kPhoneDetect);
if (key_up != prev_key_up) {
KeyUpChanged ev{.falling = prev_key_up};
@@ -77,14 +65,15 @@ void SystemState::react(const internal::GpioInterrupt&) {
}
void SystemState::react(const internal::SamdInterrupt&) {
- auto prev_charge_status = sSamd->GetChargeStatus();
- auto prev_usb_status = sSamd->GetUsbStatus();
+ auto& samd = sServices->samd();
+ auto prev_charge_status = samd.GetChargeStatus();
+ auto prev_usb_status = samd.GetUsbStatus();
- sSamd->UpdateChargeStatus();
- sSamd->UpdateUsbStatus();
+ samd.UpdateChargeStatus();
+ samd.UpdateUsbStatus();
- auto charge_status = sSamd->GetChargeStatus();
- auto usb_status = sSamd->GetUsbStatus();
+ auto charge_status = samd.GetChargeStatus();
+ auto usb_status = samd.GetUsbStatus();
if (charge_status != prev_charge_status) {
ChargingStatusChanged ev{};
@@ -97,7 +86,7 @@ void SystemState::react(const internal::SamdInterrupt&) {
}
auto SystemState::IdleCondition() -> bool {
- return !sGpios->Get(drivers::IGpios::Pin::kKeyLock) &&
+ return !sServices->gpios().Get(drivers::IGpios::Pin::kKeyLock) &&
audio::AudioState::is_in_state<audio::states::Standby>();
}