#include "ServiceManager.hpp" // C++ system level #include <vector> #include <algorithm> // #include <cstring> // memset, strncmp // #include <cstdlib> // TODO: is this for memcpy? // #include <functional> // ESP32 specific #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" // project specific #include <Types.hpp> #include <SpiderLib/Util.hpp> // TODO: remove after startAlgo()-debuggin!! // #include "SiliconTorch/NVSExplorer.hpp" // qthing stuff // #include <qthing> static const char* TAG = "ServiceManager"; namespace SiliconTorch { namespace ServiceManager { void registerSiliconTorchServices(ServiceManager* mgr); ServiceManager::ServiceManager() { registerSiliconTorchServices(this); registrationLocked = true; for (auto& [name, service] : serviceMap) service->postInit(); auto sers = startAlgo(); ESP_LOGI(TAG, "===== Services starting order ====="); for (const auto& s : sers) ESP_LOGI(TAG, "Service: %s", s->getFancyName().c_str()); ESP_LOGI(TAG, "======================================="); return; printServiceTable(); ESP_LOGI(TAG, "Starting services…"); u32 started = startEnabledServices(); ESP_LOGI(TAG, "Starting services#[ %d ] done.", started); } std::vector<Service*> ServiceManager::startAlgo() { std::vector<Service*> startOrder; // our result std::map<Service*, ServiceSet> services; // prepare our data structures for (const auto& [_, service] : serviceMap) { if (service->isEnabled()) { bool reqsSatisfied = true; ServiceSet deps; ServiceSet wanted = service->getWantedServices(); ServiceSet required = service->getRequiredServices(); // copy wanted services if enabled for (const auto& sw : wanted) if (sw->isEnabled()) deps.insert(sw); // copy required services for (const auto& sr : required) if (sr->isEnabled()) deps.insert(sr); else reqsSatisfied = false; if (reqsSatisfied) services[service] = deps; } } // Debugging stuff: print it! ESP_LOGI(TAG, "===== Service structure ====="); for (const auto& [service, deps] : services) { std::set<std::string> depsnames; for (const auto& d : deps) depsnames.insert(d->getFancyName()); std::string sdeps = SpiderLib::Util::join(depsnames, " "); ESP_LOGI(TAG, "Service[ %s ] has deps{ %s }", service->getFancyName().c_str(), sdeps.c_str()); } ESP_LOGI(TAG, "======================================="); // end debugging stuff bool dslr = true; // dslr = "did something last round" -> used for cycle detection while (dslr) { bool serviceCopied = false; auto it = services.begin(); while (it != services.end()) { if (it->second.size() == 0) { startOrder.push_back(it->first); serviceCopied = true; it = services.erase(it); } else { it++; } } for (const auto& starting : startOrder) for (auto& [_, deps] : services) deps.erase(starting); dslr = serviceCopied; } return startOrder; } std::vector<Service*> ServiceManager::getServicesInStartOrder() const { std::vector<Service*> services; for (const auto& [_, service] : serviceMap) services.push_back(service); // std::sort(services.begin(), services.end(), [](const Service* x, const Service* y) { // return x->getStartOrder() < y->getStartOrder(); // }); return services; } u32 ServiceManager::startEnabledServices() { std::vector<Service*> services = getServicesInStartOrder(); u32 started = 0; for (const auto& service : services) { if (service->isEnabled()) { service->start(); started++; } } return started; } void ServiceManager::printServiceTable() const { // burn a few cycles because Espressivs logging is mehr :/ // while (xTaskGetTickCount() * portTICK_PERIOD_MS < 1000) { // vTaskDelay(42 / portTICK_PERIOD_MS); // } vTaskDelay(100 / portTICK_PERIOD_MS); // helper function, never used outside of this // TODO: should we move it to utils…? auto strmul = [](const str& target, u32 count) { str out = ""; for (u32 n = 0; n < count; n++) out += target; return out; }; u8 nameColSize = 4; // size of "name" for (const auto& [name, _] : serviceMap) if (name.length() > nameColSize) nameColSize = name.length(); nameColSize += 2; // padding u8 startOrderColSize = 10 + 2; // size of "StartOrder" + padding u8 enabledColSize = 8 + 2; // size of "Enabled " + padding (must be even) ESP_LOGI(TAG, "┌%s┬%s┬%s┐", strmul("─", nameColSize).c_str(), strmul("─", startOrderColSize).c_str(), strmul("─", enabledColSize).c_str() ); ESP_LOGI(TAG, "│ Name %s│ StartOrder %s│ Enabled %s│", strmul(" ", nameColSize - 4 - 2).c_str(), strmul(" ", startOrderColSize - 10 - 2).c_str(), strmul(" ", enabledColSize - 8 - 2).c_str() ); ESP_LOGI(TAG, "╞%s╪%s╪%s╡", strmul("═", nameColSize).c_str(), strmul("═", startOrderColSize).c_str(), strmul("═", enabledColSize).c_str() ); std::vector<Service*> services = getServicesInStartOrder(); for (const auto& service : services) { u8 nameSize = service->getFancyName().length(); ESP_LOGI(TAG, "│ %s %s│ % 5d %s│%s%s%s│", service->getFancyName().c_str(), strmul(" ", nameColSize - 2 - nameSize).c_str(), 1337, // service->getStartOrder(), strmul(" ", startOrderColSize - 2 - 5).c_str(), strmul(" ", (enabledColSize - 2) / 2).c_str(), service->isEnabled() ? "✅" : "❌", strmul(" ", (enabledColSize - 2) / 2).c_str() ); } ESP_LOGI(TAG, "└%s┴%s┴%s┘", strmul("─", nameColSize).c_str(), strmul("─", startOrderColSize).c_str(), strmul("─", enabledColSize).c_str() ); } void ServiceManager::setServiceEnabled(const str& name, bool enabled) { if (serviceMap.count(name) > 0) { serviceMap.at(name)->setEnabled(enabled); } else { // Service not found // TODO: logging…? } } bool ServiceManager::getServiceEnabled(const str& name) const { if (serviceMap.count(name) > 0) { return serviceMap.at(name)->isEnabled(); } else { // Service not found // TODO: logging…? return false; } } Service* ServiceManager::byName(const str& name) { if (serviceMap.count(name) > 0) return serviceMap.at(name); else return NULL; } void ServiceManager::registerService(Service* s) { if (!registrationLocked) { auto serviceLookup = [&](const str& name) { return byName(name); }; s->preInit(serviceLookup); s->init(); // only do init() here to avoid name-lookup cycles in postInit() ! serviceMap[s->getName()] = s; ESP_LOGI(TAG, "Registered service[ %s ]", s->getFancyName().c_str()); } else { ESP_LOGW(TAG, "Service registration is locked"); } } } }