Skip to content

Commit

Permalink
Merge pull request #772 from SignalK/instrumentation
Browse files Browse the repository at this point in the history
Improve instrumentation
  • Loading branch information
mairas authored Oct 11, 2024
2 parents 0bc68d3 + b301f77 commit 0b1d6d8
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 53 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@
leaks or crashes due to dangling pointers. It is also possible, but not
mandatory, to use smart pointers in your own code.

- Internal instrumentation has been improved. Web user interface status page
now shows memory usage and event loop statistics.

### Migrating Existing Projects

- Update your project's `platformio.ini` file to use the new version of SensESP:
Expand Down
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
{
"owner": "mairas",
"name": "ReactESP",
"version": "^3.0.1"
"version": "^3.1.0"
},
{
"owner": "bblanchon",
Expand Down
2 changes: 1 addition & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ upload_speed = 2000000
monitor_speed = 115200

lib_deps =
mairas/ReactESP @ ^3.0.0
mairas/ReactESP @ ^3.1.0
bblanchon/ArduinoJson @ ^7.0.0
pfeerick/elapsedMillis @ ^1.0.6
bxparks/AceButton @ ^1.10.1
Expand Down
3 changes: 2 additions & 1 deletion src/sensesp/sensors/digital_input.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ class DigitalInputState : public DigitalInput, public Sensor<bool> {
triggered_{false} {
load();

event_loop()->onRepeat(read_delay_, [this]() { emit(digitalRead(pin_)); });
event_loop()->onRepeat(read_delay_,
[this]() { this->emit(digitalRead(pin_)); });
}

private:
Expand Down
8 changes: 4 additions & 4 deletions src/sensesp/sensors/system_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@

namespace sensesp {

void SystemHz::tick() { tick_count_++; }

void SystemHz::update() {
// getting sporadic divide by 0 exceptions, no harm in skipping a loop.
if (elapsed_millis_ == 0) {
return;
}

output_ = (tick_count_ * 1000) / elapsed_millis_;
uint32_t current_tick_count_ = event_loop()->getTickCount();
output_ =
(current_tick_count_ - last_tick_count_) / (elapsed_millis_ / 1000.);

tick_count_ = 0;
last_tick_count_ = current_tick_count_;
elapsed_millis_ = 0;

this->notify();
Expand Down
5 changes: 1 addition & 4 deletions src/sensesp/sensors/system_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,13 @@ class SystemHz : public ValueProducer<float> {
public:
SystemHz() {
elapsed_millis_ = 0;

event_loop()->onTick([this]() { this->tick(); });
event_loop()->onRepeat(1000, [this]() { this->update(); });
}
String get_value_name() { return "systemhz"; }

protected:
uint32_t tick_count_ = 0;
uint32_t last_tick_count_ = 0;
elapsedMillis elapsed_millis_;
void tick();
void update();
};

Expand Down
2 changes: 1 addition & 1 deletion src/sensesp/system/semaphore_value.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

#include "sensesp.h"

#include <freeRTOS/semphr.h>
#include <freertos/semphr.h>

namespace sensesp {

Expand Down
2 changes: 1 addition & 1 deletion src/sensesp/transforms/curveinterpolator.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class CurveInterpolator : public FloatTransform {
friend const char* ConfigSchema(const CurveInterpolator& obj);
};

const char* ConfigSchema(const CurveInterpolator& obj) {
inline const char* ConfigSchema(const CurveInterpolator& obj) {
static const char schema[] = R"({"type":"object","properties":{"samples":{"title":"Sample values","type":"array","format":"table","items":{"type":"object","properties":{"input":{"type":"number","title":"%s"},"output":{"type":"number","title":"%s"}}}}}})";
static char buf[sizeof(schema) + 160];
snprintf(buf, sizeof(buf), schema, obj.input_title_.c_str(),
Expand Down
21 changes: 14 additions & 7 deletions src/sensesp/transforms/repeat.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

#include <elapsedMillis.h>

#include "sensesp_base_app.h"
#include "sensesp/types/nullable.h"
#include "sensesp_base_app.h"
#include "transform.h"

namespace sensesp {
Expand All @@ -27,16 +27,20 @@ namespace sensesp {
template <typename FROM, typename TO>
class Repeat : public Transform<FROM, TO> {
public:
Repeat(unsigned long interval) : Transform<FROM, TO>(), interval_{interval} {}
Repeat(unsigned long interval) : Transform<FROM, TO>(), interval_{interval} {
if (interval_ == 0) {
ESP_LOGW("Repeat", "Interval is 0. This will cause a busy loop.");
}
}

virtual void set(const FROM& input) override {
this->emit(input);
if (repeat_event_ != nullptr) {
// Delete the old repeat event
repeat_event_->remove(event_loop());
}
repeat_event_ = event_loop()->onRepeat(
interval_, [this]() { this->notify(); });
repeat_event_ =
event_loop()->onRepeat(interval_, [this]() { this->notify(); });
}

protected:
Expand Down Expand Up @@ -107,8 +111,11 @@ class RepeatStopping : public Repeat<T, T> {
template <typename T>
class RepeatExpiring : public Repeat<T, Nullable<T>> {
public:
RepeatExpiring(unsigned long interval, unsigned long max_age)
RepeatExpiring(unsigned long interval, unsigned long max_age)
: Repeat<T, Nullable<T>>(interval), max_age_{max_age} {
ESP_LOGD("RepeatExpiring", "interval: %lu, max_age: %lu", interval,
max_age);

age_ = max_age;

if (this->repeat_event_ != nullptr) {
Expand Down Expand Up @@ -167,8 +174,8 @@ class RepeatConstantRate : public RepeatExpiring<T> {
this->repeat_event_->remove();
}

this->repeat_event_ = event_loop()->onRepeat(
interval, [this]() { this->repeat_function(); });
this->repeat_event_ =
event_loop()->onRepeat(interval, [this]() { this->repeat_function(); });
}

void set(T input) override {
Expand Down
150 changes: 117 additions & 33 deletions src/sensesp_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,21 +219,74 @@ class SensESPApp : public SensESPBaseApp {
connect_status_page_items();
}

// Collect metrics for the status page
void connect_status_page_items() {
this->hostname_->connect_to(&this->hostname_ui_output_);
this->event_loop_.onRepeat(
4999, [this]() { wifi_ssid_ui_output_.set(WiFi.SSID()); });
this->event_loop_.onRepeat(
4998, [this]() { free_memory_ui_output_.set(ESP.getFreeHeap()); });
this->event_loop_.onRepeat(
4997, [this]() { wifi_rssi_ui_output_.set(WiFi.RSSI()); });
this->event_loop_.onRepeat(4996, [this]() {
this->event_loop_.onRepeat(4999, [this]() {
wifi_ssid_ui_output_.set(WiFi.SSID());
free_memory_ui_output_.set(ESP.getFreeHeap());
wifi_rssi_ui_output_.set(WiFi.RSSI());

// Uptime
uptime_ui_output_.set(millis() / 1000);

// Event loop queue sizes
int event_loop_queue_size = event_loop_.getEventQueueSize();
int event_loop_timed_queue_size = event_loop_.getTimedEventQueueSize();
int event_loop_untimed_queue_size =
event_loop_.getUntimedEventQueueSize();

// Total tick count
uint64_t current_tick_count = event_loop_.getTickCount();
total_tick_count_ui_output_.set(current_tick_count);

// Event counts
uint64_t current_event_count = event_loop_.getEventCount();
uint64_t current_timed_event_count = event_loop_.getTimedEventCount();
uint64_t current_untimed_event_count = event_loop_.getUntimedEventCount();
event_count_ui_output_.set(current_event_count);
timed_event_count_ui_output_.set(current_timed_event_count);
untimed_event_count_ui_output_.set(current_untimed_event_count);

// Ticks and events per second during last interval
static uint64_t last_tick_count = 0;
static uint64_t last_event_count = 0;
static uint64_t last_timed_event_count = 0;
static uint64_t last_untimed_event_count = 0;
static uint64_t last_millis = 0;
uint64_t current_millis = millis();
float interval_seconds = (current_millis - last_millis) / 1000.0;

uint64_t ticks_diff = current_tick_count - last_tick_count;
uint64_t events_diff = current_event_count - last_event_count;
uint64_t timed_events_diff =
current_timed_event_count - last_timed_event_count;
uint64_t untimed_events_diff =
current_untimed_event_count - last_untimed_event_count;

// Set outputs
event_loop_queue_size_ui_output_.set(event_loop_queue_size);
event_loop_timed_queue_ui_output_.set(event_loop_timed_queue_size);
event_loop_untimed_queue_ui_output_.set(event_loop_untimed_queue_size);
event_loop_interrupt_queue_ui_output_.set(
event_loop_.getISREventQueueSize());

ticks_per_second_ui_output_.set(int(ticks_diff / interval_seconds));
events_per_second_ui_output_.set(int(events_diff / interval_seconds));
timed_events_per_second_ui_output_.set(
int(timed_events_diff / interval_seconds));
untimed_events_per_second_ui_output_.set(
int(untimed_events_diff / interval_seconds));

// Update last values
last_tick_count = current_tick_count;
last_event_count = current_event_count;
last_timed_event_count = current_timed_event_count;
last_untimed_event_count = current_untimed_event_count;
last_millis = current_millis;

sk_server_address_ui_output_.set(ws_client_->get_server_address());
});
this->event_loop_.onRepeat(4995, [this]() {
sk_server_port_ui_output_.set(ws_client_->get_server_port());
});
this->event_loop_.onRepeat(4994, [this]() {
sk_server_connection_ui_output_.set(ws_client_->get_connection_status());
});
ws_client_->get_delta_tx_count_producer().connect_to(
Expand Down Expand Up @@ -265,35 +318,66 @@ class SensESPApp : public SensESPBaseApp {
std::shared_ptr<SKDeltaQueue> sk_delta_queue_;
std::shared_ptr<SKWSClient> ws_client_;

StatusPageItem<String> sensesp_version_ui_output_{
"SenseESP version", kSensESPVersion, "Software", 1900};
StatusPageItem<String> build_info_ui_output_{
"Build date", __DATE__ " " __TIME__, "Software", 2000};
StatusPageItem<String> hostname_ui_output_{"Hostname", "", "Network", 500};
StatusPageItem<String> mac_address_ui_output_{
"MAC Address", WiFi.macAddress(), "Network", 1100};
StatusPageItem<String> wifi_ssid_ui_output_{"SSID", "", "Network", 1200};
StatusPageItem<int> free_memory_ui_output_{"Free memory (bytes)", 0, "System",
1250};
1000};
StatusPageItem<int> uptime_ui_output_{"Uptime (s)", 0, "System", 1100};

StatusPageItem<String> hostname_ui_output_{"Hostname", "", "Network", 1200};
StatusPageItem<String> mac_address_ui_output_{
"MAC Address", WiFi.macAddress(), "Network", 1300};
StatusPageItem<String> wifi_ssid_ui_output_{"SSID", "", "Network", 1400};

StatusPageItem<int8_t> wifi_rssi_ui_output_{"WiFi signal strength (dB)", -128,
"Network", 1300};
"Network", 1500};

StatusPageItem<String> sk_server_address_ui_output_{"Signal K server address",
"", "Signal K", 1400};
"", "Signal K", 1600};
StatusPageItem<uint16_t> sk_server_port_ui_output_{"Signal K server port", 0,
"Signal K", 1500};
"Signal K", 1700};
StatusPageItem<String> sk_server_connection_ui_output_{"SK connection status",
"", "Signal K", 1600};
"", "Signal K", 1800};
StatusPageItem<int> delta_tx_count_ui_output_{"SK Delta TX count", 0,
"Signal K", 1700};
"Signal K", 1900};
StatusPageItem<int> delta_rx_count_ui_output_{"SK Delta RX count", 0,
"Signal K", 1800};

// Placeholders for system status sensors in case they are created
std::shared_ptr<ValueProducer<float>> system_hz_sensor_;
std::shared_ptr<ValueProducer<uint32_t>> free_mem_sensor_;
std::shared_ptr<ValueProducer<float>> uptime_sensor_;
std::shared_ptr<ValueProducer<String>> ip_address_sensor_;
std::shared_ptr<ValueProducer<int>> wifi_signal_sensor_;
"Signal K", 2000};

StatusPageItem<int> event_loop_queue_size_ui_output_{
"Event Loop queue size", 0, "Event Loop Queues", 2100};
StatusPageItem<int> event_loop_timed_queue_ui_output_{
"Event Loop timed queue size", 0, "Event Loop Queues", 2200};
StatusPageItem<int> event_loop_untimed_queue_ui_output_{
"Event Loop untimed queue size", 0, "Event Loop Queues", 2300};
StatusPageItem<int> event_loop_interrupt_queue_ui_output_{
"Event Loop interrupt queue size", 0, "Event Loop Queues", 2400};

StatusPageItem<uint64_t> total_tick_count_ui_output_{
"Total ticks processed", 0, "Event Loop Lifetime", 2500};
StatusPageItem<uint64_t> event_count_ui_output_{"Events processed", 0,
"Event Loop Lifetime", 2600};
StatusPageItem<uint64_t> timed_event_count_ui_output_{
"Timed events processed", 0, "Event Loop Lifetime", 2700};
StatusPageItem<uint64_t> untimed_event_count_ui_output_{
"Untimed events processed", 0, "Event Loop Lifetime", 2800};
StatusPageItem<float> ticks_per_second_ui_output_{
"Ticks per second", 0, "Event Loop Performance", 2900};
StatusPageItem<float> events_per_second_ui_output_{
"Events per second", 0, "Event Loop Performance", 3000};
StatusPageItem<float> timed_events_per_second_ui_output_{
"Timed events per second", 0, "Event Loop Performance", 3100};
StatusPageItem<float> untimed_events_per_second_ui_output_{
"Untimed events per second", 0, "Event Loop Performance", 3200};

StatusPageItem<String> sensesp_version_ui_output_{
"SenseESP version", kSensESPVersion, "Software", 3300};
StatusPageItem<String> build_info_ui_output_{
"Build date", __DATE__ " " __TIME__, "Software", 3400};

// Placeholders for system status sensors in case they are created
std::shared_ptr<ValueProducer<float>> system_hz_sensor_;
std::shared_ptr<ValueProducer<uint32_t>> free_mem_sensor_;
std::shared_ptr<ValueProducer<float>> uptime_sensor_;
std::shared_ptr<ValueProducer<String>> ip_address_sensor_;
std::shared_ptr<ValueProducer<int>> wifi_signal_sensor_;

friend class WebServer;
friend class SensESPAppBuilder;
Expand Down

0 comments on commit 0b1d6d8

Please sign in to comment.