Implement v1 history data storage at OBPSensorTask

This commit is contained in:
Ulrich Meine 2025-06-25 23:14:09 +02:00
parent 1f90cefbd6
commit 2729ef9cb6
5 changed files with 157 additions and 21 deletions

View File

@ -4,5 +4,6 @@
"stdexcept": "cpp", "stdexcept": "cpp",
"limits": "cpp", "limits": "cpp",
"functional": "cpp" "functional": "cpp"
} },
"github.copilot.nextEditSuggestions.enabled": false
} }

View File

@ -3,6 +3,7 @@
#include <limits> #include <limits>
#include <stdexcept> #include <stdexcept>
#include <vector> #include <vector>
#include "WString.h"
template <typename T> template <typename T>
class RingBuffer { class RingBuffer {
@ -18,12 +19,16 @@ private:
T MAX_VAL; // highest possible value of buffer of type <T> T MAX_VAL; // highest possible value of buffer of type <T>
// metadata for buffer // metadata for buffer
String dataName; // Name of boat data in buffer
String dataFmt; // Format of boat data in buffer
int updFreq; // Update frequency in milliseconds int updFreq; // Update frequency in milliseconds
int smallest; // Value range of buffer: smallest value int smallest; // Value range of buffer: smallest value
int biggest; // Value range of buffer: biggest value int largest; // Value range of buffer: biggest value
public: public:
RingBuffer(size_t size); RingBuffer(size_t size);
void setMetaData(String name, String format, int updateFrequency, int minValue, int maxValue); // Set meta data for buffer
bool getMetaData(String& name, String& format, int& updateFrequency, int& minValue, int& maxValue); // Get meta data of buffer
void add(const T& value); // Add a new value to buffer void add(const T& value); // Add a new value to buffer
T get(size_t index) const; // Get value at specific position (0-based index from oldest to newest) T get(size_t index) const; // Get value at specific position (0-based index from oldest to newest)
T getFirst() const; // Get the first (oldest) value in buffer T getFirst() const; // Get the first (oldest) value in buffer
@ -39,12 +44,13 @@ public:
T getMedian(size_t amount) const; // Get the median value of the last <amount> values of buffer T getMedian(size_t amount) const; // Get the median value of the last <amount> values of buffer
size_t getCapacity() const; // Get the buffer capacity (maximum size) size_t getCapacity() const; // Get the buffer capacity (maximum size)
size_t getCurrentSize() const; // Get the current number of elements in buffer size_t getCurrentSize() const; // Get the current number of elements in buffer
size_t getLastIdx() const; // Get the last index of buffer
bool isEmpty() const; // Check if buffer is empty bool isEmpty() const; // Check if buffer is empty
bool isFull() const; // Check if buffer is full bool isFull() const; // Check if buffer is full
T getMinVal(); // Get lowest possible value for buffer; used for initialized buffer data T getMinVal() const; // Get lowest possible value for buffer; used for initialized buffer data
T getMaxVal(); // Get highest possible value for buffer T getMaxVal() const; // Get highest possible value for buffer
void clear(); // Clear buffer void clear(); // Clear buffer
T operator[](size_t index) const; T operator[](size_t index);
std::vector<T> getAllValues() const; // Operator[] for convenient access (same as get()) std::vector<T> getAllValues() const; // Operator[] for convenient access (same as get())
}; };

View File

@ -15,15 +15,50 @@ RingBuffer<T>::RingBuffer(size_t size)
MIN_VAL = std::numeric_limits<T>::lowest(); MIN_VAL = std::numeric_limits<T>::lowest();
MAX_VAL = std::numeric_limits<T>::max(); MAX_VAL = std::numeric_limits<T>::max();
dataName = "";
dataFmt = "";
updFreq = MIN_VAL;
smallest = MIN_VAL;
largest = MAX_VAL;
buffer.resize(size, MIN_VAL); buffer.resize(size, MIN_VAL);
// return true; // return true;
} }
// Add a new value to the buffer // Specify meta data of buffer content
template <typename T>
void RingBuffer<T>::setMetaData(String name, String format, int updateFrequency, int minValue, int maxValue)
{
dataName = name;
dataFmt = format;
updFreq = updateFrequency;
smallest = minValue;
largest = maxValue;
}
// Get meta data of buffer content
template <typename T>
bool RingBuffer<T>::getMetaData(String& name, String& format, int& updateFrequency, int& minValue, int& maxValue)
{
if (updFreq == MIN_VAL || smallest == MIN_VAL || largest == MAX_VAL) {
return false; // Meta data not set
}
name = dataName;
format = dataFmt;
updFreq = updFreq;
smallest = smallest;
largest = largest;
return true; // Meta data successfully retrieved
}
// Add a new value to buffer
template <typename T> template <typename T>
void RingBuffer<T>::add(const T& value) void RingBuffer<T>::add(const T& value)
{ {
if (value < smallest || value > largest) {
buffer[head] = MIN_VAL; // Store MIN_VAL if value is out of range
}
buffer[head] = value; buffer[head] = value;
last = head; last = head;
@ -57,7 +92,7 @@ T RingBuffer<T>::get(size_t index) const
// Operator[] for convenient access (same as get()) // Operator[] for convenient access (same as get())
template <typename T> template <typename T>
T RingBuffer<T>::operator[](size_t index) const T RingBuffer<T>::operator[](size_t index)
{ {
return get(index); return get(index);
} }
@ -302,6 +337,13 @@ size_t RingBuffer<T>::getCurrentSize() const
return count; return count;
} }
// Get the last index of buffer
template <typename T>
size_t RingBuffer<T>::getLastIdx() const
{
return last;
}
// Check if buffer is empty // Check if buffer is empty
template <typename T> template <typename T>
bool RingBuffer<T>::isEmpty() const bool RingBuffer<T>::isEmpty() const
@ -318,14 +360,14 @@ bool RingBuffer<T>::isFull() const
// Get lowest possible value for buffer; used for initialized buffer data // Get lowest possible value for buffer; used for initialized buffer data
template <typename T> template <typename T>
T RingBuffer<T>::getMinVal() T RingBuffer<T>::getMinVal() const
{ {
return MIN_VAL; return MIN_VAL;
} }
// Get highest possible value for buffer // Get highest possible value for buffer
template <typename T> template <typename T>
T RingBuffer<T>::getMaxVal() T RingBuffer<T>::getMaxVal() const
{ {
return MAX_VAL; return MAX_VAL;
} }

View File

@ -70,6 +70,12 @@ void sensorTask(void *param){
batV.begin(); batV.begin();
batC.begin(); batC.begin();
// Create ring buffers for history storage of some boat data
// later read data types from config and specify buffers accordingly
RingBuffer<int16_t> twdHstry(960); // Circular buffer to store wind direction values; store 960 TWD values for 16 minutes history
RingBuffer<int16_t> twsHstry(960); // Circular buffer to store wind speed values (TWS)
RingBuffer<int16_t> dbtHstry(960); // Circular buffer to store water depth values (DBT)
// Start timer // Start timer
Timer1.start(); // Start Timer1 for blinking LED Timer1.start(); // Start Timer1 for blinking LED
@ -361,6 +367,7 @@ void sensorTask(void *param){
long starttime11 = millis(); // Copy GPS data to RTC all 5min long starttime11 = millis(); // Copy GPS data to RTC all 5min
long starttime12 = millis(); // Get RTC data all 500ms long starttime12 = millis(); // Get RTC data all 500ms
long starttime13 = millis(); // Get 1Wire sensor data all 2s long starttime13 = millis(); // Get 1Wire sensor data all 2s
unsigned long starttime20 = millis(); // Get TWD and TWS data every 1000ms
tN2kMsg N2kMsg; tN2kMsg N2kMsg;
shared->setSensorData(sensors); //set initially read values shared->setSensorData(sensors); //set initially read values
@ -370,6 +377,21 @@ void sensorTask(void *param){
GwApi::BoatValue *hdop=new GwApi::BoatValue(GwBoatData::_HDOP); GwApi::BoatValue *hdop=new GwApi::BoatValue(GwBoatData::_HDOP);
GwApi::BoatValue *valueList[]={gpsdays, gpsseconds, hdop}; GwApi::BoatValue *valueList[]={gpsdays, gpsseconds, hdop};
// Prepare boat data values for history storage
// later read data types from config and specify hstryvalList accordingly
GwApi::BoatValue *twdBVal=new GwApi::BoatValue(GwBoatData::_TWD);
GwApi::BoatValue *twsBVal=new GwApi::BoatValue(GwBoatData::_TWS);
GwApi::BoatValue *dbtBVal=new GwApi::BoatValue(GwBoatData::_DBT);
GwApi::BoatValue *hstryValList[]={twdBVal, twsBVal, dbtBVal}; // List of boat values for history storage
int twdHstryMin = 0;
int twsHstryMin = 0;
int dbtHstryMin = 0;
// Initialize history buffers with meta data
api->getBoatDataValues(3,hstryValList);
twdHstry.setMetaData(twdBVal->getName(), twdBVal->getFormat(), 1000, twdHstryMin, 360); // Set meta data for TWD buffer: update frequency 1000ms, min value 0, max value 360
twsHstry.setMetaData(twsBVal->getName(), twsBVal->getFormat(), 1000, twsHstryMin, 100); // Set meta data for TWS buffer: update frequency 1000ms, min value 0, max value 100
dbtHstry.setMetaData(dbtBVal->getName(), dbtBVal->getFormat(), 1000, dbtHstryMin, 10928); // Set meta data for TWS buffer: update frequency 1000ms, min value 0, max value 10,928
// Internal RTC with NTP init // Internal RTC with NTP init
ESP32Time rtc(0); ESP32Time rtc(0);
if (api->getConfig()->getString(api->getConfig()->timeSource) == "iRTC") { if (api->getConfig()->getString(api->getConfig()->timeSource) == "iRTC") {
@ -775,6 +797,39 @@ void sensorTask(void *param){
} }
} }
// Read TWD, TWS, DBT data from boatData every 1000ms for history and windplot display
if(millis() > starttime20 + 1000){
starttime20 = millis();
api->getBoatDataValues(3,hstryValList);
int16_t bValue;
if (twdBVal->valid) {
bValue = int16_t(RadToDeg(twdBVal->value));
twdHstry.add(bValue);
} else {
twdHstry.add(INT16_MIN); // Add invalid value
}
if (twsBVal->valid) {
bValue = int16_t(twsBVal->value);
twsHstry.add(bValue);
} else {
twsHstry.add(INT16_MIN); // Add invalid value
}
if (dbtBVal->valid) {
bValue = int16_t(dbtBVal->value);
dbtHstry.add(bValue);
} else {
dbtHstry.add(INT16_MIN); // Add invalid value
}
String TmpName;
String TmpFormat;
int TmpUpdFreq;
int TmpSmallest;
int TmpBiggest;
twdHstry.getMetaData(TmpName, TmpFormat, TmpUpdFreq, TmpSmallest, TmpBiggest);
api->getLogger()->logDebug(GwLog::ERROR,"History buffer TWD: name:%s format:%s Freq:%d, Min: %d, Max: %d", TmpName.c_str(), TmpFormat.c_str(), TmpUpdFreq, TmpSmallest, TmpBiggest);
api->getLogger()->logDebug(GwLog::ERROR,"History buffers: TWD:%d TWS:%d DBT:%d", twdHstry.getLast(), twsHstry.getLast(), dbtHstry.getLast());
}
shared->setSensorData(sensors); shared->setSensorData(sensors);
} }
vTaskDelete(NULL); vTaskDelete(NULL);

View File

@ -2,8 +2,8 @@
#include "BoatDataCalibration.h" #include "BoatDataCalibration.h"
#include "OBP60Extensions.h" #include "OBP60Extensions.h"
#include "Pagedata.h"
#include "OBPRingBuffer.h" #include "OBPRingBuffer.h"
#include "Pagedata.h"
#include <vector> #include <vector>
@ -201,12 +201,44 @@ public:
} }
}; };
// Get maximum difference of last <amount> of TWD ringbuffer values to center chart
int getRng(const RingBuffer<int16_t>& windDirHstry, int center, size_t amount)
{
int minVal = windDirHstry.getMinVal();
size_t count = windDirHstry.getCurrentSize();
size_t capacity = windDirHstry.getCapacity();
size_t last = windDirHstry.getLastIdx();
if (windDirHstry.isEmpty() || amount <= 0) {
return minVal;
}
if (amount > count)
amount = count;
int value = 0;
int rng = 0;
int maxRng = minVal;
// Start from the newest value (last) and go backwards x times
for (size_t i = 0; i < amount; i++) {
value = windDirHstry.get((last + capacity - i) % capacity);
if (value == minVal) {
continue;
}
rng = abs(((value - center + 540) % 360) - 180);
if (rng > maxRng)
maxRng = rng;
}
if (maxRng > 180) {
maxRng = 180;
}
return maxRng;
}
// **************************************************************** // ****************************************************************
class PageWindPlot : public Page { class PageWindPlot : public Page {
bool keylock = false; // Keylock bool keylock = false; // Keylock
// int16_t lp = 80; // Pointer length char chrtMode = 'D'; // Chart mode: 'D' for TWD, 'S' for TWS, 'B' for both
char chrtMode = 'D'; // Chart mode: 'D' for TWD, 'S' for TWS, 'B' for both06121990
int dataIntv = 1; // Update interval for wind history chart: int dataIntv = 1; // Update interval for wind history chart:
// (1)|(2)|(3)|(4) seconds for approx. 4, 8, 12, 16 min. history chart // (1)|(2)|(3)|(4) seconds for approx. 4, 8, 12, 16 min. history chart
bool showTWS = true; // Show TWS value in chart area bool showTWS = true; // Show TWS value in chart area
@ -323,8 +355,8 @@ public:
int distVals; // helper to check wndCenter crossing int distVals; // helper to check wndCenter crossing
int distMid; // helper to check wndCenter crossing int distMid; // helper to check wndCenter crossing
static RingBuffer<int> windDirHstry(bufSize); // Circular buffer to store wind direction values static RingBuffer<int16_t> windDirHstry(bufSize); // Circular buffer to store wind direction values
static RingBuffer<int>windSpdHstry(bufSize); // Circular buffer to store wind speed values static RingBuffer<int16_t> windSpdHstry(bufSize); // Circular buffer to store wind speed values
LOG_DEBUG(GwLog::LOG, "Display page WindPlot"); LOG_DEBUG(GwLog::LOG, "Display page WindPlot");
unsigned long start = millis(); unsigned long start = millis();
@ -420,13 +452,13 @@ public:
// initialize chart range values // initialize chart range values
if (wndCenter == INT_MIN) { if (wndCenter == INT_MIN) {
wndCenter = max(0, windDirHstry.get(numWndValues - intvBufSize)); // get 1st value of current data interval wndCenter = max(0, int(windDirHstry.get(numWndValues - intvBufSize))); // get 1st value of current data interval
wndCenter = (int((wndCenter + (wndCenter >= 0 ? 5 : -5)) / 10) * 10) % 360; // Set new center value; round to nearest 10 degree value; 360° -> 0° wndCenter = (int((wndCenter + (wndCenter >= 0 ? 5 : -5)) / 10) * 10) % 360; // Set new center value; round to nearest 10 degree value; 360° -> 0°
diffRng = dfltRng; diffRng = dfltRng;
chrtRng = dfltRng; chrtRng = dfltRng;
} else { } else {
// check and adjust range between left, center, and right chart limit // check and adjust range between left, center, and right chart limit
diffRng = windDirHstry.getRng(wndCenter, numWndValues); diffRng = getRng(windDirHstry, wndCenter, numWndValues);
diffRng = (diffRng == INT_MIN ? 0 : diffRng); diffRng = (diffRng == INT_MIN ? 0 : diffRng);
if (diffRng > chrtRng) { if (diffRng > chrtRng) {
chrtRng = int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10; // Round up to next 10 degree value chrtRng = int((diffRng + (diffRng >= 0 ? 9 : -1)) / 10) * 10; // Round up to next 10 degree value