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

Metrics basically working: counters, lifetime and uptime; fixing many bugs

parent d8fd0f7d
No related branches found
No related tags found
No related merge requests found
......@@ -3,6 +3,9 @@
#include "esp_err.h"
#include "esp_log.h"
#include "driver/ledc.h"
#include "tcpip_adapter.h"
#include <nlohmann/json.hpp>
#include <qthing>
#include <qthing/mqtt_common.hpp>
......@@ -17,6 +20,7 @@
using namespace qthing;
using json = nlohmann::json;
const char* SiliconTorch::TAG = "fxCyan"; // TODO: maybe suffix with exact protocol…?
......@@ -35,10 +39,13 @@ static float bytes2float(const char *bytes) {
}
SiliconTorch::FxCyanF::FxCyanF(uint32_t baseChannel) : metrics(Metrics::Metrics("fxCyan", 60*60)), baseChannel(baseChannel) {
SiliconTorch::FxCyanF::FxCyanF(uint32_t baseChannel) : metrics(Metrics::Metrics("fxCyan")), baseChannel(baseChannel) {
this->metrics.registerMetric("frames", "frameCounter");
this->metrics.registerMetric("errors", "errorCounter");
metrics.registerMetric("frames", "FrameCounter");
metrics.registerMetric("errors", "ErrorCounter");
this->frameCntInc = this->metrics.generateMetricIncrementer("frames");
this->errorCntInc = this->metrics.generateMetricIncrementer("errors");
// TODO: make vector!
......@@ -127,6 +134,10 @@ SiliconTorch::FxCyanF::FxCyanF(uint32_t baseChannel) : metrics(Metrics::Metrics(
this->publishFrqRes();
};
std::function<void(const std::string&)> getListener = [&](const std::string& ignored) {
this->publishListenerInfo();
};
// device-local setters
add_message_callback(this->genDeviceTopic("frqres/set"), setFrqRes);
......@@ -153,6 +164,11 @@ SiliconTorch::FxCyanF::FxCyanF(uint32_t baseChannel) : metrics(Metrics::Metrics(
add_message_callback(this->genServiceTopic("frqres/get"), getFrqRes);
add_message_callback(this->genServiceTopic("channel/get"), getBCh);
add_message_callback(this->genServiceTopic("channelCnt/get"), getChs);
// Listener info getters and auto-publish
add_message_callback(this->genDeviceTopic("listener/get"), getListener);
add_message_callback(this->genServiceTopic("listener/get"), getListener);
add_mqtt_connected_callback(std::bind(&SiliconTorch::FxCyanF::publishListenerInfo, this));
}
......@@ -186,6 +202,9 @@ bool SiliconTorch::FxCyanF::handleUnicast(const char *data, std::size_t length)
int32_t diff = length - size;
if (diff < 0) {
ESP_LOGE(TAG, "Invalid data length[ %i ]: Received ΔB = %i bytes too few", length, -diff);
this->errorCntInc();
return false;
}
......@@ -194,6 +213,7 @@ bool SiliconTorch::FxCyanF::handleUnicast(const char *data, std::size_t length)
this->setPWM(ch, f);
}
this->frameCntInc();
this->callPacketCallback();
return true;
......@@ -206,6 +226,9 @@ bool SiliconTorch::FxCyanF::handleBroadcast(const char *data, std::size_t length
int32_t diff = length - offset - size;
if (diff < 0) { // TODO: test thoroughly!
ESP_LOGE(TAG, "Invalid data length[ %i ]: Received ΔB = %i bytes too few", length, -diff);
this->errorCntInc();
return false;
}
......@@ -214,6 +237,7 @@ bool SiliconTorch::FxCyanF::handleBroadcast(const char *data, std::size_t length
this->setPWM(ch, f);
}
this->frameCntInc();
this->callPacketCallback();
return true;
......@@ -342,3 +366,26 @@ void SiliconTorch::FxCyanF::publishFrqRes() {
publish_message(this->genDeviceTopic("frqres"), tmp);
}
void SiliconTorch::FxCyanF::publishListenerInfo() {
if (qthing::is_mqtt_connected()) {
// TODO: get IP Address of ethernet adapter
tcpip_adapter_ip_info_t ipInfo;
esp_err_t err;
err = tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo);
if (err == ESP_OK) {
char ip[16];
snprintf(ip, 16, "%d.%d.%d.%d", ipInfo.ip.addr & 0xFF, (ipInfo.ip.addr >> 8) & 0xFF, (ipInfo.ip.addr >> 16) & 0xFF, ipInfo.ip.addr >> 24);
json j;
j["IPv4"] = ip;
j["port"] = "4213"; // TODO: get from qthing (currently not possible)
publish_message(this->genDeviceTopic("listener"), j.dump().c_str());
} else {
ESP_LOGW(TAG, "Can't determine IP");
}
}
}
......@@ -54,6 +54,7 @@ namespace SiliconTorch {
void publishResolution();
void publishBaseChannel();
void publishChannelCount();
void publishListenerInfo();
private:
......@@ -73,6 +74,8 @@ namespace SiliconTorch {
void callPacketCallback();
Metrics::Metrics metrics;
Metrics::Incrementer frameCntInc;
Metrics::Incrementer errorCntInc;
};
}
......@@ -9,11 +9,16 @@
#include "esp_err.h"
#include "esp_log.h"
#include <ctime>
#include <qthing>
#include <climits>
#include <cinttypes>
using namespace qthing;
using json = nlohmann::json;
static const char* TAG = "Metrics";
......@@ -28,11 +33,17 @@ namespace SiliconTorch {
namespace Metrics {
Metrics::Metrics(const std::string& nameSpace, uint32_t saveInterval) : nameSpace(nameSpace), saveInterval(saveInterval) {
uint32_t uptime_ms() {
return (uint32_t)(esp_timer_get_time() / 1000L);
}
Metrics::Metrics(const std::string& nameSpace) : nameSpace(nameSpace) {
nvs_flash_init();
nvs_flash_init(); // TODO: WTF does it crash????
nvs_open(nameSpace.c_str(), NVS_READWRITE, &this->nvs);
nvs_get_u64(this->nvs, "lifetime", &this->lifeTime); // read lifetime value from NVS
lifeTimeSaved = uptime_ms();
char taskName[64];
......@@ -40,8 +51,16 @@ namespace SiliconTorch {
std::function<void()> f = std::bind(&SiliconTorch::Metrics::Metrics::tickTask, this);
std::function<void()> *_f = new std::function<void()>(f);
xTaskCreate(methodTaskWrapper, taskName, 8192, (void*)_f, 1, NULL);
xTaskCreatePinnedToCore(methodTaskWrapper, taskName, 8192, (void*)_f, 1, NULL, 0);
std::function<void(const std::string&)> getMetrics = [&](const std::string& ignored) {
publishData();
};
add_message_callback(genDeviceTopic("listener/get"), getMetrics);
add_message_callback(std::string("service/") + nameSpace + std::string("/listener/get"), getMetrics);
add_mqtt_connected_callback(std::bind(&SiliconTorch::Metrics::Metrics::publishData, this));
}
......@@ -51,36 +70,104 @@ namespace SiliconTorch {
if (result != counters.end()) {
ESP_LOGW(TAG, "Can't register Metric[ %s ]: Already exists", shortName.c_str());
} else {
counters[shortName] = new CounterMetric(shortName, fullName, 10, 10);
CounterMetric* counter = new CounterMetric(shortName, fullName);
counter->load(&this->nvs);
counters[shortName] = counter;
}
}
void Metrics::publishData() {
json j;
for (auto it = counters.begin(); it != counters.end(); ++it) {
CounterMetric* metric = it->second;
j[metric->fullName.c_str()] = metric->getValue();
}
j["uptime"] = uptime_ms() / 1000UL;
j["lifetime"] = (lifeTime + (uptime_ms() - lifeTimeSaved)) / 1000UL;
publish_message(genDeviceTopic("metrics"), j.dump().c_str());
lastPublished = uptime_ms();
}
void Metrics::saveData() {
// TODO: implementation!
lastSaved = std::time(NULL);
for (auto it = counters.begin(); it != counters.end(); ++it)
it->second->save(&this->nvs);
uint32_t now = uptime_ms();
lifeTime += now - lifeTimeSaved;
lifeTimeSaved = now;
esp_err_t err = nvs_set_u64(this->nvs, "lifetime", this->lifeTime);
if (true) {
// if (err != ESP_OK) { // !!!!!!
const char* errorName = esp_err_to_name(err);
ESP_LOGE(TAG, "Error[ %s ] saving LifeTimeMetric[ %lli ]", errorName, lifeTime);
}
nvs_commit(this->nvs);
lastSaved = uptime_ms();
}
void Metrics::tickTask() {
TickType_t lastWakeTime = xTaskGetTickCount();
while (true) {
std::time_t now = std::time(NULL);
if (now - lastSaved > saveInterval) saveData();
uint32_t now = uptime_ms();
if ((now - lastSaved ) / 1000 > saveInterval ) saveData();
if ((now - lastPublished) / 1000 > publishInterval) publishData();
ESP_LOGW(TAG, "TickTack… now = %i lastSaved = %i lastPublished = %i", now, lastSaved, lastPublished);
vTaskDelay((tickInterval * 1000) / portTICK_PERIOD_MS);
vTaskDelayUntil(&lastWakeTime, tickPeriod / portTICK_PERIOD_MS);
}
}
uint64_t getCounter(const std::string& shortName) {
return 0;
uint64_t Metrics::getCounterValue(const std::string& shortName) {
auto result = counters.find(shortName);
if (result != counters.end()) {
return result->second->getValue();
} else if (shortName.compare("uptime") == 0) {
return uptime_ms() / 1000UL;
} else if (shortName.compare("lifetime") == 0) {
return (lifeTime + (uptime_ms() - lifeTimeSaved)) / 1000UL;
} else {
return 0; // TODO: log warning…?
}
}
std::string Metrics::genDeviceTopic(const char *suffix) {
return std::string(DEVICE_NAMESPACE) + nameSpace + std::string("/") + std::string(suffix);
}
Incrementer Metrics::generateMetricIncrementer(const std::string& shortName) {
CounterMetric::CounterMetric(const std::string& shortName, const std::string& fullName, uint32_t saveInterval, uint32_t publishInterval) :
shortName(shortName), fullName(fullName), saveInterval(saveInterval), publishInterval(publishInterval) {
auto result = counters.find(shortName);
if (result != counters.end()) {
CounterMetric* counter = result->second;
return [counter]() { counter->inc(); };
} else {
return [](){}; // TODO: log warning…?
}
}
CounterMetric::CounterMetric(const std::string& shortName, const std::string& fullName) : shortName(shortName), fullName(fullName) {
// TODO: implement!
......@@ -89,18 +176,15 @@ namespace SiliconTorch {
void CounterMetric::load(nvs_handle_t* nvs) {
lastSaved = std::time(NULL);
nvs_get_u64(*nvs, shortName.c_str(), &this->value);
}
void CounterMetric::save(nvs_handle_t* nvs) {
if (std::time(NULL) - lastSaved > saveInterval) forceSave(nvs);
if (dirty) forceSave(nvs);
}
// TODO: synchronize!
void CounterMetric::forceSave(nvs_handle_t* nvs) {
lastSaved = std::time(NULL);
dirty = false;
esp_err_t err = nvs_set_u64(*nvs, shortName.c_str(), this->value);
......@@ -112,6 +196,10 @@ namespace SiliconTorch {
}
}
uint64_t CounterMetric::getValue() {
return value;
}
void CounterMetric::inc() {
if (value < ULLONG_MAX) {
......
......@@ -15,17 +15,23 @@ namespace SiliconTorch {
typedef std::function<void()> Incrementer;
uint32_t uptime_ms(); // rolls over after about 136 years
class CounterMetric {
public:
CounterMetric(const std::string& shortName, const std::string& fullName, uint32_t saveInterval, uint32_t publishInterval);
CounterMetric(const std::string& shortName, const std::string& fullName);
const std::string fullName;
const std::string shortName;
bool dirty = false;
const std::string& fullName;
const std::string& shortName;
uint64_t getValue();
void inc();
void dec();
void load(nvs_handle_t* nvs); // load value from NVS
void load(nvs_handle_t* nvs); // load value from NVS
void save(nvs_handle_t* nvs); // saves only if enoughg time elapsed
void forceSave(nvs_handle_t* nvs); // saves value immediately
......@@ -36,39 +42,44 @@ namespace SiliconTorch {
uint64_t operator--(int); // post-decrement
private:
uint64_t value = 0;
bool dirty = false;
uint32_t saveInterval;
uint32_t publishInterval;
uint64_t value = 0;
std::time_t lastSaved;
std::time_t lastPublished;
};
class Metrics {
public:
Metrics(const std::string& nameSpace, uint32_t saveInterval = 60*60);
Metrics(const std::string& nameSpace);
void registerMetric(const std::string& shortName, const std::string& fullName);
uint64_t getCounter(const std::string& shortName); // returns the actual value or 0 for unknown counters
/*
* returns the actual value or 0 for unknown counters
* also supports getting the special uptime and lifetime metrics
*/
uint64_t getCounterValue(const std::string& shortName);
Incrementer generateMetricIncrementer(const std::string& shortName);
void saveData();
void publishData();
const std::string nameSpace;
const std::string& nameSpace;
std::string genDeviceTopic(const char *suffix);
private:
uint32_t saveInterval; // seconds
uint32_t tickInterval = 60; // seconds; internal tick task interval
uint32_t saveInterval = 60; // *60; // seconds
uint32_t publishInterval = 15; // *60; // seconds
uint32_t lastSaved = uptime_ms(); // unix timestamp
uint32_t lastPublished = uptime_ms(); // unix timestamp
uint32_t tickPeriod = 3000; // milli seconds
std::time_t lastSaved = std::time(NULL); // unix timestamp
uint64_t lifeTime = 0; // milli seconds
uint64_t lifeTimeSaved = 0; // milli seconds
void tickTask();
......
......@@ -13,7 +13,7 @@
qthing::Config cfg;
CyanLight::CyanLightControl ctrl(3);
CyanLight::CyanLightControl* ctrl;
......@@ -21,23 +21,24 @@ void device_main() {
cfg.apply();
ctrl.setFrqRes(100, 19);
ctrl = new CyanLight::CyanLightControl(3);
ctrl->setFrqRes(100, 19);
for (uint8_t i = 0; i < 100; i++) {
bool _f = i % 2 == 0;
ctrl.setPWM(0, _f * 1.0f);
ctrl.setPWM(2, (_f ^ 1) * 1.0f);
ctrl->setPWM(0, _f * 1.0f);
ctrl->setPWM(2, (_f ^ 1) * 1.0f);
vTaskDelay(50 / portTICK_PERIOD_MS);
}
ctrl.setPWM(0, 0.0f);
ctrl.setPWM(1, 0.0f);
ctrl.setPWM(2, 1.0f);
ctrl->setPWM(0, 0.0f);
ctrl->setPWM(1, 0.0f);
ctrl->setPWM(2, 1.0f);
qthing::enable_wifi();
......@@ -51,13 +52,13 @@ void device_main() {
while (true) {
for (uint8_t i = 0; i < limit; i++) {
float pwm = i / limit;
ctrl.setPWM(0, pwm*pwm);
ctrl->setPWM(0, pwm*pwm);
vTaskDelay(delay);
}
for (uint8_t i = 0; i < limit; i++) {
float pwm = i / limit;
ctrl.setPWM(0, 1.0f - pwm*pwm);
ctrl->setPWM(0, 1.0f - pwm*pwm);
vTaskDelay(delay);
}
}
......
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