From 88ac2fc59f82290cd3c4c0b66433a3b5b8a906dd Mon Sep 17 00:00:00 2001 From: Jochen Vothknecht <jochen3120@gmail.com> Date: Fri, 24 Jun 2022 08:53:23 +0200 Subject: [PATCH] ST: Service starting order automation --- .../SiliconTorch/Service/CyanStripe.cpp | 2 +- CLC-qthing/SiliconTorch/Service/FxCyanF.cpp | 12 +- CLC-qthing/SiliconTorch/Service/FxPublish.cpp | 2 +- CLC-qthing/SiliconTorch/Service/Service.cpp | 70 ++++++++-- CLC-qthing/SiliconTorch/Service/Service.hpp | 30 ++++- .../SiliconTorch/Service/ServiceManager.cpp | 123 +++++++++++++++++- .../SiliconTorch/Service/ServiceManager.hpp | 6 +- 7 files changed, 218 insertions(+), 27 deletions(-) diff --git a/CLC-qthing/SiliconTorch/Service/CyanStripe.cpp b/CLC-qthing/SiliconTorch/Service/CyanStripe.cpp index c499c0e..62ae821 100644 --- a/CLC-qthing/SiliconTorch/Service/CyanStripe.cpp +++ b/CLC-qthing/SiliconTorch/Service/CyanStripe.cpp @@ -54,7 +54,7 @@ namespace SiliconTorch { } void CyanStripe::start() { - ESP_LOGW(getName().c_str(), "Starting service[ %s ] with order[ %d ] *WHOOP* *WHOOP*", getName().c_str(), getStartOrder()); + ESP_LOGW(getName().c_str(), "Starting service[ %s ] *WHOOP* *WHOOP*", getName().c_str()); } diff --git a/CLC-qthing/SiliconTorch/Service/FxCyanF.cpp b/CLC-qthing/SiliconTorch/Service/FxCyanF.cpp index e85e890..e4fd215 100644 --- a/CLC-qthing/SiliconTorch/Service/FxCyanF.cpp +++ b/CLC-qthing/SiliconTorch/Service/FxCyanF.cpp @@ -26,6 +26,8 @@ namespace SiliconTorch { void FxCyanF::init() { setName("FxCyanF"); setNameSpace("fxCyan"); // TODO: "FxCyanF" + + addWantedService("CyanBus"); } @@ -44,8 +46,10 @@ namespace SiliconTorch { u32 frq = SiliconTorch::FxCyanF::DefaultFrequency; u8 res = SiliconTorch::FxCyanF::DefaultResolution; - frq = NVSExplorer::NVSExplorer::instance().getUnsignedInt(getNameSpace(), "frequency", frq); - res = NVSExplorer::NVSExplorer::instance().getUnsignedInt(getNameSpace(), "resolution", res); + NVSExplorer::NVSExplorer& nvs = NVSExplorer::NVSExplorer::instance(); + + frq = nvs.getUnsignedInt(getNameSpace(), "frequency", frq); + res = nvs.getUnsignedInt(getNameSpace(), "resolution", res); fxCyan->setFrqRes(frq, res); @@ -55,10 +59,10 @@ namespace SiliconTorch { char buffer[32]; snprintf(buffer, 32, "ch%d_gpio", ch); - u8 gpio = NVSExplorer::NVSExplorer::instance().getUnsignedInt(getNameSpace(), buffer, 0xFF) & 0xFF; + u8 gpio = nvs.getUnsignedInt(getNameSpace(), buffer, 0xFF) & 0xFF; snprintf(buffer, 32, "ch%d_pwm", ch); - f32 pwm = NVSExplorer::NVSExplorer::instance().getFloat(getNameSpace(), buffer); + f32 pwm = nvs.getFloat(getNameSpace(), buffer); // NaN == "no value stored" or any other reading error if (std::isnan(pwm)) pwm = 0.0f; diff --git a/CLC-qthing/SiliconTorch/Service/FxPublish.cpp b/CLC-qthing/SiliconTorch/Service/FxPublish.cpp index 69abef7..ad2820f 100644 --- a/CLC-qthing/SiliconTorch/Service/FxPublish.cpp +++ b/CLC-qthing/SiliconTorch/Service/FxPublish.cpp @@ -41,7 +41,7 @@ namespace SiliconTorch { } void FxPublish::start() { - ESP_LOGW(getName().c_str(), "Starting service[ %s ] with order[ %d ] *WHOOP* *WHOOP*", getName().c_str(), getStartOrder()); + ESP_LOGW(getName().c_str(), "Starting service[ %s ] *WHOOP* *WHOOP*", getName().c_str()); } diff --git a/CLC-qthing/SiliconTorch/Service/Service.cpp b/CLC-qthing/SiliconTorch/Service/Service.cpp index d42151a..6da3240 100644 --- a/CLC-qthing/SiliconTorch/Service/Service.cpp +++ b/CLC-qthing/SiliconTorch/Service/Service.cpp @@ -16,7 +16,7 @@ // #include <qthing> -const char* TAG = "ServiceManager"; +static const char* TAG = "ServiceManager"; namespace SiliconTorch { @@ -28,25 +28,32 @@ namespace SiliconTorch { Service::Service() { // u8 state = 0; // disabled -> 0 enabled -> 1 - // state = NVSExplorer::NVSExplorer::instance().getUnsignedInt(nameSpace(), NVSStateKey, state); + // state = nvs.getUnsignedInt(nameSpace(), NVSStateKey, state); // if (state >= (u8)Invalid) state = (u8)Disabled; // nvsState = (NVSState)state; } + void Service::preInit(ServiceLookup lookup) { + serviceLookup = lookup; + } + void Service::postInit() { attrsLocked = true; u8 state = 0; // disabled -> 0 enabled -> 1 - state = NVSExplorer::NVSExplorer::instance().getUnsignedInt(getNameSpace(), NVSStateKey, state); + state = SiliconTorch::NVSExplorer::NVSExplorer::instance().getUnsignedInt(getNameSpace(), NVSStateKey, state); if (state >= (u8)Invalid) state = (u8)Disabled; nvsState = (NVSState)state; + + ESP_LOGW(TAG, "Service[ %s ] may not start due to errorneous initialization", getName().c_str()); + } bool Service::isEnabled() const { - return nvsState == Enabled; + return nvsState == Enabled && !hasErrors; } void Service::setEnabled(bool enabled) { @@ -86,15 +93,62 @@ namespace SiliconTorch { } } - void Service::setStartOrder(u16 startOrder) { + + void Service::addWantedService(const str& name) { + if (!attrsLocked) { + Service* service = otherByName(name); + + if (service != NULL) + wantedServices.insert(service); + } else { + // TODO: report error somehow? + } + } + + void Service::addRequiredService(const str& name) { if (!attrsLocked) { - this->startOrder = startOrder; + Service* service = otherByName(name); + + if (service != NULL) { + requiredServices.insert(service); + } else { + hasErrors = true; + ESP_LOGW(TAG, "Service[ %s ] requires unknown otherService[ %s ]", getName().c_str(), name.c_str()); + } } else { // TODO: report error somehow? } } + ServiceSet Service::getWantedServices() { + ServiceSet out; + + // TODO: better method for set copy?! + + for (const auto& service : wantedServices) + out.insert(service); + + return out; + } + + ServiceSet Service::getRequiredServices() { + ServiceSet out; + + // TODO: better method for set copy?! + + for (const auto& service : requiredServices) + out.insert(service); + + return out; + } + + + Service* Service::otherByName(const str& name) { + return serviceLookup(name); + } + + const str& Service::getName() const { return name; } @@ -103,9 +157,5 @@ namespace SiliconTorch { return nameSpace; } - u16 Service::getStartOrder() const { - return startOrder; - } - } } diff --git a/CLC-qthing/SiliconTorch/Service/Service.hpp b/CLC-qthing/SiliconTorch/Service/Service.hpp index 0f7de7d..aaf4f8b 100644 --- a/CLC-qthing/SiliconTorch/Service/Service.hpp +++ b/CLC-qthing/SiliconTorch/Service/Service.hpp @@ -1,9 +1,10 @@ #pragma once // C++ system level +#include <set> // #include <cstring> // memset, strncmp // #include <cstdlib> // TODO: is this for memcpy? -// #include <functional> +#include <functional> // ESP32 specific #include "esp_log.h" @@ -27,6 +28,11 @@ namespace SiliconTorch { Invalid }; + class Service; + + using ServiceSet = std::set<Service*>; + using ServiceLookup = std::function<Service*(const str&)>; + class Service { public: @@ -45,29 +51,43 @@ namespace SiliconTorch { const str& getName() const; const str& getNameSpace() const; - u16 getStartOrder() const; - bool isEnabled() const; void setEnabled(bool enabled); + Service* otherByName(const str& name); + // internal stuff, only to be called by ServiceManager! + void preInit(ServiceLookup lookup); void postInit(); + ServiceSet getWantedServices(); + ServiceSet getRequiredServices(); + protected: void setName(const str& name); void setNameSpace(const str& nameSpace); - void setStartOrder(u16 startOrder); + + // Service will be started after the wanted one (if wanted is disabled this limitation is waived) // TODO: (waived / raised / lowered?) + void addWantedService(const str& name); + + // Service will only be started after the required one + void addRequiredService(const str& name); private: NVSState nvsState = Disabled; + bool hasErrors = false; // Set if an error is detected. Prevents the service from being started + bool attrsLocked = false; str name = ""; str nameSpace = ""; - u16 startOrder = 1337; + ServiceSet wantedServices; + ServiceSet requiredServices; + + ServiceLookup serviceLookup = [](const str& ignored) { return (Service*)NULL; }; }; diff --git a/CLC-qthing/SiliconTorch/Service/ServiceManager.cpp b/CLC-qthing/SiliconTorch/Service/ServiceManager.cpp index a15fa1f..8c7a92f 100644 --- a/CLC-qthing/SiliconTorch/Service/ServiceManager.cpp +++ b/CLC-qthing/SiliconTorch/Service/ServiceManager.cpp @@ -14,6 +14,7 @@ // project specific #include <Types.hpp> +#include <SpiderLib/Util.hpp> // TODO: remove after startAlgo()-debuggin!! // #include "SiliconTorch/NVSExplorer.hpp" // qthing stuff @@ -37,6 +38,18 @@ namespace SiliconTorch { registrationLocked = true; + + auto sers = startAlgo(); + + ESP_LOGI(TAG, "===== Services starting order ====="); + + for (const auto& s : sers) ESP_LOGI(TAG, "Service: %s", s->getName().c_str()); + + ESP_LOGI(TAG, "======================================="); + + return; + + printServiceTable(); ESP_LOGI(TAG, "Starting services…"); @@ -44,15 +57,102 @@ namespace SiliconTorch { 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& [name, service] : serviceMap) { + + if (service->isEnabled()) { + + bool reqsOK = true; // TODO: requirements_"zufriedengestellt" …? (naming) + + 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 reqsOK = false; + + if (reqsOK) + 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->getName()); + + std::string sdeps = SpiderLib::Util::join(depsnames, " "); + + ESP_LOGI(TAG, "Service[ %s ] has deps{ %s }", service->getName().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& [ignored, 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(); - }); + // std::sort(services.begin(), services.end(), [](const Service* x, const Service* y) { + // return x->getStartOrder() < y->getStartOrder(); + // }); return services; } @@ -129,7 +229,7 @@ namespace SiliconTorch { ESP_LOGI(TAG, "│ %s %s│ % 5d %s│%s%s%s│", service->getName().c_str(), strmul(" ", nameColSize - 2 - nameSize).c_str(), - service->getStartOrder(), + 1337, // service->getStartOrder(), strmul(" ", startOrderColSize - 2 - 5).c_str(), strmul(" ", (enabledColSize - 2) / 2).c_str(), service->isEnabled() ? "✅" : "âŒ", @@ -167,10 +267,23 @@ namespace SiliconTorch { } + 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->init(); - s->postInit(); + s->postInit();//serviceLookup); serviceMap[s->getName()] = s; diff --git a/CLC-qthing/SiliconTorch/Service/ServiceManager.hpp b/CLC-qthing/SiliconTorch/Service/ServiceManager.hpp index 32a7dd0..ed43416 100644 --- a/CLC-qthing/SiliconTorch/Service/ServiceManager.hpp +++ b/CLC-qthing/SiliconTorch/Service/ServiceManager.hpp @@ -25,7 +25,8 @@ namespace SiliconTorch { extern const str NVSStateKey; - using ServiceMap = std::map<str, Service*>; + using ServiceSet = std::set<Service*>; + using ServiceMap = std::map<str, SiliconTorch::ServiceManager::Service*>; class ServiceManager { @@ -43,9 +44,12 @@ namespace SiliconTorch { std::vector<Service*> getServicesInStartOrder() const; + Service* byName(const str& name); void registerService(Service* s); + std::vector<Service*> startAlgo(); + private: bool registrationLocked = false; -- GitLab