Skip to content
Snippets Groups Projects
Commit 88ac2fc5 authored by fxk8y's avatar fxk8y :spider:
Browse files

ST: Service starting order automation

parent 02d46a13
No related branches found
No related tags found
No related merge requests found
......@@ -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());
}
......
......@@ -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;
......
......@@ -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());
}
......
......@@ -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;
}
}
}
#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; };
};
......
......@@ -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;
......
......@@ -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;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment