mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-03-30 14:55:46 +00:00
* simplified alertIf()
* refactored TimeSeriesData to top-level class
This commit is contained in:
@@ -237,8 +237,8 @@ void SensorMesh::sendAlert(const char* text) {
|
||||
}
|
||||
}
|
||||
|
||||
void SensorMesh::alertIfLow(Trigger& t, float value, float threshold, const char* text) {
|
||||
if (value < threshold) {
|
||||
void SensorMesh::alertIf(bool condition, Trigger& t, const char* text) {
|
||||
if (condition) {
|
||||
if (!t.triggered) {
|
||||
t.triggered = true;
|
||||
t.time = getRTCClock()->getCurrentTime();
|
||||
@@ -252,65 +252,6 @@ void SensorMesh::alertIfLow(Trigger& t, float value, float threshold, const char
|
||||
}
|
||||
}
|
||||
|
||||
void SensorMesh::alertIfHigh(Trigger& t, float value, float threshold, const char* text) {
|
||||
if (value > threshold) {
|
||||
if (!t.triggered) {
|
||||
t.triggered = true;
|
||||
t.time = getRTCClock()->getCurrentTime();
|
||||
sendAlert(text);
|
||||
}
|
||||
} else {
|
||||
if (t.triggered) {
|
||||
t.triggered = false;
|
||||
// TODO: apply debounce logic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SensorMesh::recordData(TimeSeriesData& data, float value) {
|
||||
uint32_t now = getRTCClock()->getCurrentTime();
|
||||
if (now >= data.last_timestamp + data.interval_secs) {
|
||||
data.last_timestamp = now;
|
||||
|
||||
data.data[data.next] = value; // append to cycle table
|
||||
data.next = (data.next + 1) % data.num_slots;
|
||||
}
|
||||
}
|
||||
|
||||
void SensorMesh::calcDataMinMaxAvg(const TimeSeriesData& data, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type) {
|
||||
int i = data.next, n = data.num_slots;
|
||||
uint32_t ago = data.interval_secs * data.num_slots;
|
||||
int num_values = 0;
|
||||
float total = 0.0f;
|
||||
|
||||
dest->_channel = channel;
|
||||
dest->_lpp_type = lpp_type;
|
||||
|
||||
// start at earliest recording, through to most recent
|
||||
while (n > 0) {
|
||||
n--;
|
||||
i = (i + 1) % data.num_slots;
|
||||
if (ago >= end_secs_ago && ago < start_secs_ago) {
|
||||
float v = data.data[i];
|
||||
num_values++;
|
||||
total += v;
|
||||
if (num_values == 1) {
|
||||
dest->_max = dest->_min = v;
|
||||
} else {
|
||||
if (v < dest->_min) dest->_min = v;
|
||||
if (v > dest->_max) dest->_max = v;
|
||||
}
|
||||
}
|
||||
ago -= data.interval_secs;
|
||||
}
|
||||
// calc average
|
||||
if (num_values > 0) {
|
||||
dest->_avg = total / num_values;
|
||||
} else {
|
||||
dest->_avg = NAN;
|
||||
}
|
||||
}
|
||||
|
||||
float SensorMesh::getAirtimeBudgetFactor() const {
|
||||
return _prefs.airtime_factor;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <Arduino.h> // needed for PlatformIO
|
||||
#include <Mesh.h>
|
||||
|
||||
#include "TimeSeriesData.h"
|
||||
|
||||
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
|
||||
#include <InternalFileSystem.h>
|
||||
#elif defined(RP2040_PLATFORM)
|
||||
@@ -90,33 +92,7 @@ protected:
|
||||
Trigger() { triggered = false; time = 0; }
|
||||
};
|
||||
|
||||
void alertIfLow(Trigger& t, float value, float threshold, const char* text);
|
||||
void alertIfHigh(Trigger& t, float value, float threshold, const char* text);
|
||||
|
||||
class TimeSeriesData {
|
||||
public:
|
||||
float* data;
|
||||
int num_slots, next;
|
||||
uint32_t last_timestamp;
|
||||
uint32_t interval_secs;
|
||||
|
||||
TimeSeriesData(float* array, int num, uint32_t secs) : num_slots(num), data(array), last_timestamp(0), next(0), interval_secs(secs) {
|
||||
memset(data, 0, sizeof(float)*num);
|
||||
}
|
||||
TimeSeriesData(int num, uint32_t secs) : num_slots(num), last_timestamp(0), next(0), interval_secs(secs) {
|
||||
data = new float[num];
|
||||
memset(data, 0, sizeof(float)*num);
|
||||
}
|
||||
};
|
||||
|
||||
void recordData(TimeSeriesData& data, float value);
|
||||
|
||||
struct MinMaxAvg {
|
||||
float _min, _max, _avg;
|
||||
uint8_t _lpp_type, _channel;
|
||||
};
|
||||
|
||||
void calcDataMinMaxAvg(const TimeSeriesData& data, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type);
|
||||
void alertIf(bool condition, Trigger& t, const char* text);
|
||||
|
||||
virtual void onSensorDataRead() = 0; // for app to implement
|
||||
virtual int querySeriesData(uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg dest[], int max_num) = 0; // for app to implement
|
||||
|
||||
45
examples/simple_sensor/TimeSeriesData.cpp
Normal file
45
examples/simple_sensor/TimeSeriesData.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "TimeSeriesData.h"
|
||||
|
||||
void TimeSeriesData::recordData(mesh::RTCClock* clock, float value) {
|
||||
uint32_t now = clock->getCurrentTime();
|
||||
if (now >= last_timestamp + interval_secs) {
|
||||
last_timestamp = now;
|
||||
|
||||
data[next] = value; // append to cycle table
|
||||
next = (next + 1) % num_slots;
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSeriesData::calcDataMinMaxAvg(mesh::RTCClock* clock, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type) const {
|
||||
int i = next, n = num_slots;
|
||||
uint32_t ago = clock->getCurrentTime() - last_timestamp;
|
||||
int num_values = 0;
|
||||
float total = 0.0f;
|
||||
|
||||
dest->_channel = channel;
|
||||
dest->_lpp_type = lpp_type;
|
||||
|
||||
// start at most recet recording, back-track through to oldest
|
||||
while (n > 0) {
|
||||
n--;
|
||||
i = (i + num_slots - 1) % num_slots; // go back by one
|
||||
if (ago >= end_secs_ago && ago < start_secs_ago) { // filter by the desired time range
|
||||
float v = data[i];
|
||||
num_values++;
|
||||
total += v;
|
||||
if (num_values == 1) {
|
||||
dest->_max = dest->_min = v;
|
||||
} else {
|
||||
if (v < dest->_min) dest->_min = v;
|
||||
if (v > dest->_max) dest->_max = v;
|
||||
}
|
||||
}
|
||||
ago += interval_secs;
|
||||
}
|
||||
// calc average
|
||||
if (num_values > 0) {
|
||||
dest->_avg = total / num_values;
|
||||
} else {
|
||||
dest->_avg = NAN;
|
||||
}
|
||||
}
|
||||
29
examples/simple_sensor/TimeSeriesData.h
Normal file
29
examples/simple_sensor/TimeSeriesData.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Mesh.h>
|
||||
|
||||
struct MinMaxAvg {
|
||||
float _min, _max, _avg;
|
||||
uint8_t _lpp_type, _channel;
|
||||
};
|
||||
|
||||
class TimeSeriesData {
|
||||
float* data;
|
||||
int num_slots, next;
|
||||
uint32_t last_timestamp;
|
||||
uint32_t interval_secs;
|
||||
|
||||
public:
|
||||
TimeSeriesData(float* array, int num, uint32_t secs) : num_slots(num), data(array), last_timestamp(0), next(0), interval_secs(secs) {
|
||||
memset(data, 0, sizeof(float)*num);
|
||||
}
|
||||
TimeSeriesData(int num, uint32_t secs) : num_slots(num), last_timestamp(0), next(0), interval_secs(secs) {
|
||||
data = new float[num];
|
||||
memset(data, 0, sizeof(float)*num);
|
||||
}
|
||||
|
||||
void recordData(mesh::RTCClock* clock, float value);
|
||||
void calcDataMinMaxAvg(mesh::RTCClock* clock, uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg* dest, uint8_t channel, uint8_t lpp_type) const;
|
||||
};
|
||||
|
||||
@@ -21,12 +21,12 @@ protected:
|
||||
void onSensorDataRead() override {
|
||||
float batt_voltage = getVoltage(TELEM_CHANNEL_SELF);
|
||||
|
||||
recordData(battery_data, batt_voltage); // record battery
|
||||
alertIfLow(low_batt, batt_voltage, 3.4f, "Battery low!");
|
||||
battery_data.recordData(getRTCClock(), batt_voltage); // record battery
|
||||
alertIf(batt_voltage < 3.4f, low_batt, "Battery low!");
|
||||
}
|
||||
|
||||
int querySeriesData(uint32_t start_secs_ago, uint32_t end_secs_ago, MinMaxAvg dest[], int max_num) override {
|
||||
calcDataMinMaxAvg(battery_data, start_secs_ago, end_secs_ago, &dest[0], TELEM_CHANNEL_SELF, LPP_VOLTAGE);
|
||||
battery_data.calcDataMinMaxAvg(getRTCClock(), start_secs_ago, end_secs_ago, &dest[0], TELEM_CHANNEL_SELF, LPP_VOLTAGE);
|
||||
return 1;
|
||||
}
|
||||
/* ======================================================================= */
|
||||
|
||||
Reference in New Issue
Block a user