From 3ce1e31e64a6baf6ef732f402c185fbf44491c9f Mon Sep 17 00:00:00 2001 From: Ulrich Meine Date: Sat, 13 Dec 2025 17:59:16 +0100 Subject: [PATCH] Initial change to history buffers taking any boat value --- lib/obp60task/OBPDataOperations.cpp | 189 +++++++++++++--------------- lib/obp60task/OBPDataOperations.h | 61 ++++----- lib/obp60task/PageWindPlot.cpp | 22 ++-- lib/obp60task/Pagedata.h | 2 +- lib/obp60task/obp60task.cpp | 14 +-- 5 files changed, 129 insertions(+), 159 deletions(-) diff --git a/lib/obp60task/OBPDataOperations.cpp b/lib/obp60task/OBPDataOperations.cpp index c68ef53..3eadde5 100644 --- a/lib/obp60task/OBPDataOperations.cpp +++ b/lib/obp60task/OBPDataOperations.cpp @@ -1,46 +1,81 @@ #include "OBPDataOperations.h" #include "BoatDataCalibration.h" // Functions lib for data instance calibration #include +#include +#include +#include // --- Class HstryBuf --------------- -// Init history buffers for selected boat data -void HstryBuf::init(BoatValueList* boatValues, GwLog *log) { +HstryBuf::HstryBuf(const String& name, int size, GwLog* log, BoatValueList* boatValues) + : logger(log), boatDataName(name) { + hstry.resize(size); + boatValue = boatValues->findValueOrCreate(name); +} - logger = log; +void HstryBuf::init(const String& format, int updFreq, int mltplr, double minVal, double maxVal) { + hstry.setMetaData(boatDataName, format, updFreq, mltplr, minVal, maxVal); + hstryMin = minVal; + hstryMax = maxVal; + if (!boatValue->valid) { + boatValue->setFormat(format); + boatValue->value = std::numeric_limits::max(); + } +} +void HstryBuf::add(double value) { + if (value >= hstryMin && value <= hstryMax) { + hstry.add(value); + } +} + +void HstryBuf::handle(bool useSimuData) { + GwApi::BoatValue *calBVal; + + if (boatValue->valid) { + calBVal = new GwApi::BoatValue(boatDataName.c_str()); + calBVal->setFormat(boatValue->getFormat()); + calBVal->value = boatValue->value; + calBVal->valid = boatValue->valid; + calibrationData.calibrateInstance(calBVal, logger); + add(calBVal->value); + delete calBVal; + calBVal = nullptr; + } else if (useSimuData) { + double simValue = hstry.getLast(); + if (boatDataName == "TWD" || boatDataName == "AWD") { + simValue += static_cast(random(-349, 349) / 1000.0); + simValue = WindUtils::to2PI(simValue); + } else if (boatDataName == "TWS" || boatDataName == "AWS") { + simValue += static_cast(random(-5000, 5000) / 1000.0); + simValue = constrain(simValue, 0, 40); + } + add(simValue); + } +} + +// --- Class HstryManager --------------- +HstryManager::HstryManager(int size, GwLog* log, BoatValueList* boatValues) { + // Create history buffers for each boat data type + hstryBufs["TWD"] = std::unique_ptr(new HstryBuf("TWD", size, log, boatValues)); + hstryBufs["TWS"] = std::unique_ptr(new HstryBuf("TWS", size, log, boatValues)); + hstryBufs["AWD"] = std::unique_ptr(new HstryBuf("AWD", size, log, boatValues)); + hstryBufs["AWS"] = std::unique_ptr(new HstryBuf("AWS", size, log, boatValues)); + + // Initialize metadata for each buffer int hstryUpdFreq = 1000; // Update frequency for history buffers in ms int mltplr = 1000; // Multiplier which transforms original value into buffer type format double hstryMinVal = 0; // Minimum value for these history buffers - twdHstryMax = 2 * M_PI; // Max value for wind direction (TWD, AWD) in rad [0...2*PI] - twsHstryMax = 65; // Max value for wind speed (TWS, AWS) in m/s [0..65] (limit due to type capacity of buffer - shifted by ) - awdHstryMax = twdHstryMax; - awsHstryMax = twsHstryMax; - twdHstryMin = hstryMinVal; - twsHstryMin = hstryMinVal; - awdHstryMin = hstryMinVal; - awsHstryMin = hstryMinVal; - const double DBL_MAX = std::numeric_limits::max(); + double courseMax = 2 * M_PI; + double speedMax = 65; - // Initialize history buffers with meta data mltplr = 10000; // Store 4 decimals for course data - hstryBufList.twdHstry->setMetaData("TWD", "formatCourse", hstryUpdFreq, mltplr, hstryMinVal, twdHstryMax); - hstryBufList.awdHstry->setMetaData("AWD", "formatCourse", hstryUpdFreq, mltplr, hstryMinVal, twdHstryMax); + hstryBufs["TWD"]->init("formatCourse", hstryUpdFreq, mltplr, hstryMinVal, courseMax); + hstryBufs["AWD"]->init("formatCourse", hstryUpdFreq, mltplr, hstryMinVal, courseMax); + mltplr = 1000; // Store 3 decimals for windspeed data - hstryBufList.twsHstry->setMetaData("TWS", "formatKnots", hstryUpdFreq, mltplr, hstryMinVal, twsHstryMax); - hstryBufList.awsHstry->setMetaData("AWS", "formatKnots", hstryUpdFreq, mltplr, hstryMinVal, twsHstryMax); - - // create boat values for history data types, if they don't exist yet - twdBVal = boatValues->findValueOrCreate(hstryBufList.twdHstry->getName()); - twsBVal = boatValues->findValueOrCreate(hstryBufList.twsHstry->getName()); - twaBVal = boatValues->findValueOrCreate("TWA"); - awdBVal = boatValues->findValueOrCreate(hstryBufList.awdHstry->getName()); - awsBVal = boatValues->findValueOrCreate(hstryBufList.awsHstry->getName()); - - if (!awdBVal->valid) { // AWD usually does not exist - awdBVal->setFormat(hstryBufList.awdHstry->getFormat()); - awdBVal->value = DBL_MAX; - } + hstryBufs["TWS"]->init("formatKnots", hstryUpdFreq, mltplr, hstryMinVal, speedMax); + hstryBufs["AWS"]->init("formatKnots", hstryUpdFreq, mltplr, hstryMinVal, speedMax); // collect boat values for true wind calculation awaBVal = boatValues->findValueOrCreate("AWA"); @@ -49,106 +84,58 @@ void HstryBuf::init(BoatValueList* boatValues, GwLog *log) { varBVal = boatValues->findValueOrCreate("VAR"); cogBVal = boatValues->findValueOrCreate("COG"); sogBVal = boatValues->findValueOrCreate("SOG"); + awdBVal = boatValues->findValueOrCreate("AWD"); } // Handle history buffers for TWD, TWS, AWD, AWS -//void HstryBuf::handleHstryBuf(GwApi* api, BoatValueList* boatValues, bool useSimuData) { -void HstryBuf::handleHstryBuf(bool useSimuData) { +void HstryManager::handleHstryBufs(bool useSimuData) { - static double twd, tws, awd, aws, hdt = 20; //initial value only relevant if we use simulation data + static double hdt = 20; //initial value only relevant if we use simulation data GwApi::BoatValue *calBVal; // temp variable just for data calibration -> we don't want to calibrate the original data here - LOG_DEBUG(GwLog::DEBUG,"obp60task handleHstryBuf: TWD_isValid? %d, twdBVal: %.1f, twaBVal: %.1f, twsBVal: %.1f", twdBVal->valid, twdBVal->value * RAD_TO_DEG, - twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852); - - if (twdBVal->valid) { -// if (!useSimuData) { - calBVal = new GwApi::BoatValue("TWD"); // temporary solution for calibration of history buffer values - calBVal->setFormat(twdBVal->getFormat()); - calBVal->value = twdBVal->value; - calBVal->valid = twdBVal->valid; - calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated - twd = calBVal->value; - if (twd >= twdHstryMin && twd <= twdHstryMax) { - hstryBufList.twdHstry->add(twd); - LOG_DEBUG(GwLog::DEBUG,"obp60task handleHstryBuf: calBVal.value %.2f, twd: %.2f, twdHstryMin: %.1f, twdHstryMax: %.2f", calBVal->value, twd, twdHstryMin, twdHstryMax); - } - delete calBVal; - calBVal = nullptr; - } else if (useSimuData) { -// } else { - twd += random(-20, 20); - twd += static_cast(random(-349, 349) / 1000.0); // add up to +/- 20 degree in RAD - twd = WindUtils::to2PI(twd); - hstryBufList.twdHstry->add(twd); - } - - if (twsBVal->valid) { - calBVal = new GwApi::BoatValue("TWS"); // temporary solution for calibration of history buffer values - calBVal->setFormat(twsBVal->getFormat()); - calBVal->value = twsBVal->value; - calBVal->valid = twsBVal->valid; - calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated - tws = calBVal->value; - if (tws >= twsHstryMin && tws <= twsHstryMax) { - hstryBufList.twsHstry->add(tws); - } - delete calBVal; - calBVal = nullptr; - } else if (useSimuData) { - // tws += random(-5000, 5000); // TWS value in m/s; expands to 3 decimals - tws += static_cast(random(-5000, 5000) / 1000.0); // add up to +/- 5 m/s TWS speed - tws = constrain(tws, 0, 40); // Limit TWS to [0..40] m/s - hstryBufList.twsHstry->add(tws); + // Handle all registered history buffers + for (auto& pair : hstryBufs) { + auto& buf = pair.second; + buf->handle(useSimuData); } + // Special handling for AWD which is calculated if (awaBVal->valid) { if (hdtBVal->valid) { hdt = hdtBVal->value; // Use HDT if available } else { hdt = WindUtils::calcHDT(&hdmBVal->value, &varBVal->value, &cogBVal->value, &sogBVal->value); } - + double awd; awd = awaBVal->value + hdt; awd = WindUtils::to2PI(awd); calBVal = new GwApi::BoatValue("AWD"); // temporary solution for calibration of history buffer values calBVal->value = awd; calBVal->setFormat(awdBVal->getFormat()); calBVal->valid = true; - calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated + // We don't have a logger here, so we pass nullptr. This should be improved. + calibrationData.calibrateInstance(calBVal, nullptr); // Check if boat data value is to be calibrated awdBVal->value = calBVal->value; awdBVal->valid = true; - awd = calBVal->value; - if (awd >= awdHstryMin && awd <= awdHstryMax) { - hstryBufList.awdHstry->add(awd); + // Find the AWD buffer and add the value. + auto it = hstryBufs.find("AWD"); + if (it != hstryBufs.end()) { + it->second->add(calBVal->value); } + delete calBVal; calBVal = nullptr; } else if (useSimuData) { - awd += static_cast(random(-349, 349) / 1000.0); // add up to +/- 20 degree in RAD - awd = WindUtils::to2PI(awd); - hstryBufList.awdHstry->add(awd); + // Simulation for AWD is handled inside HstryBuf::handle } - - if (awsBVal->valid) { - calBVal = new GwApi::BoatValue("AWS"); // temporary solution for calibration of history buffer values - calBVal->setFormat(awsBVal->getFormat()); - calBVal->value = awsBVal->value; - calBVal->valid = awsBVal->valid; - calibrationData.calibrateInstance(calBVal, logger); // Check if boat data value is to be calibrated - aws = calBVal->value; - if (aws >= awsHstryMin && aws <= awsHstryMax) { - hstryBufList.awsHstry->add(aws); - } - delete calBVal; - calBVal = nullptr; - } else if (useSimuData) { - aws += static_cast(random(-5000, 5000) / 1000.0); // add up to +/- 5 m/s TWS speed - aws = constrain(aws, 0, 40); // Limit TWS to [0..40] m/s - hstryBufList.awsHstry->add(aws); +} + +RingBuffer* HstryManager::getBuffer(const String& name) { + auto it = hstryBufs.find(name); + if (it != hstryBufs.end()) { + return &it->second->hstry; } - LOG_DEBUG(GwLog::DEBUG,"obp60task handleHstryBuf-End: Buffer twdHstry: %.3f, twsHstry: %.3f, awdHstry: %.3f, awsHstry: %.3f", hstryBufList.twdHstry->getLast(), hstryBufList.twsHstry->getLast(), - hstryBufList.awdHstry->getLast(),hstryBufList.awsHstry->getLast()); + return nullptr; } // --- Class HstryBuf --------------- diff --git a/lib/obp60task/OBPDataOperations.h b/lib/obp60task/OBPDataOperations.h index 8422894..de33156 100644 --- a/lib/obp60task/OBPDataOperations.h +++ b/lib/obp60task/OBPDataOperations.h @@ -2,51 +2,38 @@ #pragma once #include "OBPRingBuffer.h" #include "obp60task.h" - -typedef struct { - RingBuffer* twdHstry; - RingBuffer* twsHstry; - RingBuffer* awdHstry; - RingBuffer* awsHstry; -} tBoatHstryData; // Holds pointers to all history buffers for boat data +#include +#include +#include class HstryBuf { private: GwLog *logger; + RingBuffer hstry; // Circular buffer to store history values + String boatDataName; + double hstryMin; + double hstryMax; + GwApi::BoatValue *boatValue; - RingBuffer twdHstry; // Circular buffer to store true wind direction values - RingBuffer twsHstry; // Circular buffer to store true wind speed values (TWS) - RingBuffer awdHstry; // Circular buffer to store apparent wind direction values - RingBuffer awsHstry; // Circular buffer to store apparent xwind speed values (AWS) - double twdHstryMin; // Min value for wind direction (TWD) in history buffer - double twdHstryMax; // Max value for wind direction (TWD) in history buffer - double twsHstryMin; - double twsHstryMax; - double awdHstryMin; - double awdHstryMax; - double awsHstryMin; - double awsHstryMax; - - // boat values for buffers and for true wind calculation - GwApi::BoatValue *twdBVal, *twsBVal, *twaBVal, *awdBVal, *awsBVal; - GwApi::BoatValue *awaBVal, *hdtBVal, *hdmBVal, *varBVal, *cogBVal, *sogBVal; + friend class HstryManager; + void handleHistory(bool useSimuData); public: - tBoatHstryData hstryBufList; + HstryBuf(const String& name, int size, GwLog* log, BoatValueList* boatValues); + void init(const String& format, int updFreq, int mltplr, double minVal, double maxVal); + void add(double value); + void handle(bool useSimuData); +}; - HstryBuf(){ - hstryBufList = {&twdHstry, &twsHstry, &awdHstry, &awsHstry}; // Generate history buffers of zero size - }; - - HstryBuf(int size) { - hstryBufList = {&twdHstry, &twsHstry, &awdHstry, &awsHstry}; - hstryBufList.twdHstry->resize(size); // store xWD values for /60 minutes history - hstryBufList.twsHstry->resize(size); - hstryBufList.awdHstry->resize(size); - hstryBufList.awsHstry->resize(size); - }; - void init(BoatValueList* boatValues, GwLog *log); - void handleHstryBuf(bool useSimuData); +class HstryManager { +private: + std::map> hstryBufs; + // boat values for true wind calculation + GwApi::BoatValue *awaBVal, *hdtBVal, *hdmBVal, *varBVal, *cogBVal, *sogBVal, *awdBVal; +public: + HstryManager(int size, GwLog* log, BoatValueList* boatValues); + void handleHstryBufs(bool useSimuData); + RingBuffer* getBuffer(const String& name); }; class WindUtils { diff --git a/lib/obp60task/PageWindPlot.cpp b/lib/obp60task/PageWindPlot.cpp index 0b4e884..a30c0c8 100644 --- a/lib/obp60task/PageWindPlot.cpp +++ b/lib/obp60task/PageWindPlot.cpp @@ -2,6 +2,7 @@ #include "Pagedata.h" #include "OBP60Extensions.h" +#include "OBPDataOperations.h" #include "OBPcharts.h" // **************************************************************** @@ -163,12 +164,9 @@ public: if (showTruW != oldShowTruW) { if (!twdFlChart) { // Create true wind charts if they don't exist - LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: Creating true wind charts"); - auto* twdHstry = pageData.boatHstry->hstryBufList.twdHstry; - auto* twsHstry = pageData.boatHstry->hstryBufList.twsHstry; - // LOG_DEBUG(GwLog::DEBUG,"History Buffer addresses PageWindPlot: twdBuf: %p, twsBuf: %p", (void*)pageData.boatHstry->hstryBufList.twdHstry, - // (void*)pageData.boatHstry->hstryBufList.twsHstry); - + LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: Creating true wind charts"); + auto* twdHstry = pageData.hstryManager->getBuffer("TWD"); + auto* twsHstry = pageData.hstryManager->getBuffer("TWS"); twdFlChart = std::unique_ptr>(new Chart(*twdHstry, 1, 0, dfltRngWd, *commonData, useSimuData)); twsFlChart = std::unique_ptr>(new Chart(*twsHstry, 0, 0, dfltRngWs, *commonData, useSimuData)); twdHfChart = std::unique_ptr>(new Chart(*twdHstry, 1, 1, dfltRngWd, *commonData, useSimuData)); @@ -180,8 +178,8 @@ public: if (!awdFlChart) { // Create apparent wind charts if they don't exist LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: Creating apparent wind charts"); - auto* awdHstry = pageData.boatHstry->hstryBufList.awdHstry; - auto* awsHstry = pageData.boatHstry->hstryBufList.awsHstry; + auto* awdHstry = pageData.hstryManager->getBuffer("AWD"); + auto* awsHstry = pageData.hstryManager->getBuffer("AWS"); awdFlChart = std::unique_ptr>(new Chart(*awdHstry, 1, 0, dfltRngWd, *commonData, useSimuData)); awsFlChart = std::unique_ptr>(new Chart(*awsHstry, 0, 0, dfltRngWs, *commonData, useSimuData)); @@ -191,15 +189,15 @@ public: // Switch active charts based on showTruW if (showTruW) { - wdHstry = pageData.boatHstry->hstryBufList.twdHstry; - wsHstry = pageData.boatHstry->hstryBufList.twsHstry; + wdHstry = pageData.hstryManager->getBuffer("TWD"); + wsHstry = pageData.hstryManager->getBuffer("TWS"); wdFlChart = twdFlChart.get(); wsFlChart = twsFlChart.get(); wdHfChart = twdHfChart.get(); wsHfChart = twsHfChart.get(); } else { - wdHstry = pageData.boatHstry->hstryBufList.awdHstry; - wsHstry = pageData.boatHstry->hstryBufList.awsHstry; + wdHstry = pageData.hstryManager->getBuffer("AWD"); + wsHstry = pageData.hstryManager->getBuffer("AWS"); wdFlChart = awdFlChart.get(); wsFlChart = awsFlChart.get(); wdHfChart = awdHfChart.get(); diff --git a/lib/obp60task/Pagedata.h b/lib/obp60task/Pagedata.h index 9c515b4..f40d187 100644 --- a/lib/obp60task/Pagedata.h +++ b/lib/obp60task/Pagedata.h @@ -16,7 +16,7 @@ typedef struct{ uint8_t pageNumber; // page number in sequence of visible pages //the values will always contain the user defined values first ValueList values; - HstryBuf* boatHstry; + HstryManager* hstryManager; } PageData; // Sensor data structure (only for extended sensors, not for NMEA bus sensors) diff --git a/lib/obp60task/obp60task.cpp b/lib/obp60task/obp60task.cpp index 17e586a..0626a90 100644 --- a/lib/obp60task/obp60task.cpp +++ b/lib/obp60task/obp60task.cpp @@ -433,7 +433,7 @@ void OBP60Task(GwApi *api){ int lastPage=pageNumber; BoatValueList boatValues; //all the boat values for the api query - HstryBuf hstryBufList(1920); // Create ring buffers for history storage of some boat data (1920 seconds = 32 minutes) + HstryManager hstryManager(1920, logger, &boatValues); // Create and manage history buffers WindUtils trueWind(&boatValues); // Create helper object for true wind calculation //commonData.distanceformat=config->getString(xxx); //add all necessary data to common data @@ -477,21 +477,19 @@ void OBP60Task(GwApi *api){ LOG_DEBUG(GwLog::DEBUG,"added fixed value %s to page %d",value->getName().c_str(),i); pages[i].parameters.values.push_back(value); } - // Add boat history data to page parameters - pages[i].parameters.boatHstry = &hstryBufList; + // Add history manager to page parameters + pages[i].parameters.hstryManager = &hstryManager; } // add out of band system page (always available) Page *syspage = allPages.pages[0]->creator(commonData); - // Read all calibration data settings from config - calibrationData.readConfig(config, logger); - // Check user settings for true wind calculation bool calcTrueWnds = api->getConfig()->getBool(api->getConfig()->calcTrueWnds, false); bool useSimuData = api->getConfig()->getBool(api->getConfig()->useSimuData, false); // Initialize history buffer for certain boat data - hstryBufList.init(&boatValues, logger); + // Read all calibration data settings from config + calibrationData.readConfig(config, logger); // Display screenshot handler for HTTP request // http://192.168.15.1/api/user/OBP60Task/screenshot @@ -809,7 +807,7 @@ void OBP60Task(GwApi *api){ trueWind.addTrueWind(api, &boatValues, logger); } // Handle history buffers for certain boat data for windplot page and other usage - hstryBufList.handleHstryBuf(useSimuData); + hstryManager.handleHstryBufs(useSimuData); // Clear display // getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);