1
0
mirror of https://github.com/thooge/esp32-nmea2000-obp60.git synced 2026-01-26 08:13:05 +01:00

PageTwoValues: added chart display option

This commit is contained in:
Ulrich Meine
2026-01-11 15:30:13 +01:00
parent 4747336a69
commit 86a078690a
3 changed files with 306 additions and 156 deletions

View File

@@ -12,8 +12,8 @@ private:
enum PageMode {
VALUE,
CHART,
BOTH
BOTH,
CHART
};
enum DisplayMode {
FULL,
@@ -54,6 +54,7 @@ private:
RingBuffer<uint16_t>* dataHstryBuf = nullptr;
std::unique_ptr<Chart> dataChart; // Chart object
// display data value in display <mode> [FULL|HALF]
void showData(GwApi::BoatValue* bValue1, DisplayMode mode)
{
int nameXoff, nameYoff, unitXoff, unitYoff, value1Xoff, value1Yoff;
@@ -158,17 +159,17 @@ public:
Page::setupKeys();
#if defined BOARD_OBP60S3
constexpr int ZOOM_IDX = 4;
constexpr int ZOOM_KEY = 4;
#elif defined BOARD_OBP40S3
constexpr int ZOOM_IDX = 1;
constexpr int ZOOM_KEY = 1;
#endif
if (dataHstryBuf) { // show "Mode" key only if chart supported boat data type is available
commonData->keydata[0].label = "MODE";
commonData->keydata[ZOOM_IDX].label = "ZOOM";
commonData->keydata[ZOOM_KEY].label = "ZOOM";
} else {
commonData->keydata[0].label = "";
commonData->keydata[ZOOM_IDX].label = "";
commonData->keydata[ZOOM_KEY].label = "";
}
}
@@ -177,7 +178,7 @@ public:
{
if (dataHstryBuf) { // if boat data type supports charts
// Set page mode value | value/half chart | full chart
// Set page mode: value | value/half chart | full chart
if (key == 1) {
switch (pageMode) {
case VALUE:

View File

@@ -3,60 +3,267 @@
#include "Pagedata.h"
#include "OBP60Extensions.h"
#include "BoatDataCalibration.h"
#include "OBPDataOperations.h"
#include "OBPcharts.h"
class PageTwoValues : public Page
class PageTwoValues : public Page {
private:
GwLog* logger;
enum PageMode {
VALUES,
VAL1_CHART,
VAL2_CHART,
CHARTS
};
enum DisplayMode {
FULL,
HALF
};
static constexpr char HORIZONTAL = 'H';
static constexpr char VERTICAL = 'V';
static constexpr int8_t FULL_SIZE = 0;
static constexpr int8_t HALF_SIZE_TOP = 1;
static constexpr int8_t HALF_SIZE_BOTTOM = 2;
static constexpr bool PRNT_NAME = true;
static constexpr bool NO_PRNT_NAME = false;
static constexpr bool PRNT_VALUE = true;
static constexpr bool NO_PRNT_VALUE = false;
static constexpr int YOFFSET = 130; // y offset for display of 2nd boat value
int width; // Screen width
int height; // Screen height
bool keylock = false; // Keylock
PageMode pageMode = VALUES; // Page display mode
int8_t dataIntv = 1; // Update interval for wind history chart:
// (1)|(2)|(3)|(4)|(8) x 240 seconds for 4, 8, 12, 16, 32 min. history chart
// String lengthformat;
bool useSimuData;
bool holdValues;
String flashLED;
String backlightMode;
String tempFormat;
// Data buffer pointer (owned by HstryBuffers)
static constexpr int NUMVALUES = 2; // two data values in this page
RingBuffer<uint16_t>* dataHstryBuf[NUMVALUES] = { nullptr };
std::unique_ptr<Chart> dataChart[NUMVALUES]; // Chart object
// Old values for hold function
String sValueOld[NUMVALUES] = { "", "" };
String unitOld[NUMVALUES] = { "", "" };
// display data values in display <mode> [FULL|HALF]
void showData(const std::vector<GwApi::BoatValue*>& bValue, DisplayMode mode)
{
public:
PageTwoValues(CommonData &common){
commonData = &common;
common.logger->logDebug(GwLog::LOG,"Instantiate PageTwoValue");
getdisplay().setTextColor(commonData->fgcolor);
int numValues = bValue.size(); // do we have to handle 1 or 2 values?
for (int i = 0; i < numValues; i++) {
int yOffset = YOFFSET * i;
String name = xdrDelete(bValue[i]->getName()); // Value name
name = name.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bValue[i], logger); // Check if boat data value is to be calibrated
double value = bValue[i]->value; // Value as double in SI unit
bool valid = bValue[i]->valid; // Valid information
String sValue = formatValue(bValue[i], *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit = formatValue(bValue[i], *commonData).unit; // Unit of value
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 75 + yOffset);
getdisplay().print(name); // name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 125 + yOffset);
if (holdValues) {
getdisplay().print(unitOld[i]); // name
} else {
getdisplay().print(unit); // name
}
virtual int handleKey(int key){
// Code for keylock
if(key == 11){
// Switch font depending on value format and adjust position
if (bValue[i]->getFormat() == "formatLatitude" || bValue[i]->getFormat() == "formatLongitude") {
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 125 + yOffset);
} else if (bValue[i]->getFormat() == "formatTime" || bValue[i]->getFormat() == "formatDate") {
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 105 + yOffset);
} else { // Default font for other formats
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b);
getdisplay().setCursor(180, 125 + yOffset);
}
// Show bus data
if (!holdValues || useSimuData) {
getdisplay().print(sValue); // Real value as formated string
} else {
getdisplay().print(sValueOld[i]); // Old value as formated string
}
if (valid == true) {
sValueOld[i] = sValue; // Save the old value
unitOld[i] = unit; // Save the old unit
}
}
if (numValues == 2 && mode == FULL) { // print line only, if we want to show 2 data values
getdisplay().fillRect(0, 145, width, 3, commonData->fgcolor); // Horizontal line 3 pix
}
}
public:
PageTwoValues(CommonData& common)
{
commonData = &common;
logger = commonData->logger;
LOG_DEBUG(GwLog::LOG, "Instantiate PageTwoValues");
width = getdisplay().width(); // Screen width
height = getdisplay().height(); // Screen height
// Get config data
// lengthformat = commonData->config->getString(commonData->config->lengthFormat);
useSimuData = commonData->config->getBool(commonData->config->useSimuData);
holdValues = commonData->config->getBool(commonData->config->holdvalues);
flashLED = commonData->config->getString(commonData->config->flashLED);
backlightMode = commonData->config->getString(commonData->config->backlight);
tempFormat = commonData->config->getString(commonData->config->tempFormat); // [K|°C|°F]
}
virtual void setupKeys()
{
Page::setupKeys();
#if defined BOARD_OBP60S3
constexpr int ZOOM_KEY = 4;
#elif defined BOARD_OBP40S3
constexpr int ZOOM_KEY = 1;
#endif
if (dataHstryBuf[0] || dataHstryBuf[1]) { // show "Mode" key only if at least 1 chart supported boat data type is available
commonData->keydata[0].label = "MODE";
commonData->keydata[ZOOM_KEY].label = "ZOOM";
} else {
commonData->keydata[0].label = "";
commonData->keydata[ZOOM_KEY].label = "";
}
}
// Key functions
virtual int handleKey(int key)
{
if (dataHstryBuf[0] || dataHstryBuf[1]) { // if at least 1 boat data type supports charts
// Set page mode: value | value/half chart | full charts
if (key == 1) {
switch (pageMode) {
case VALUES:
if (dataHstryBuf[0]) {
pageMode = VAL1_CHART;
} else if (dataHstryBuf[1]) {
pageMode = VAL2_CHART;
}
break;
case VAL1_CHART:
if (dataHstryBuf[1]) {
pageMode = VAL2_CHART;
} else {
pageMode = CHARTS;
}
break;
case VAL2_CHART:
pageMode = CHARTS;
break;
case CHARTS:
pageMode = VALUES;
break;
}
return 0; // Commit the key
}
// Set time frame to show for history chart
#if defined BOARD_OBP60S3
if (key == 5) {
#elif defined BOARD_OBP40S3
if (key == 2) {
#endif
if (dataIntv == 1) {
dataIntv = 2;
} else if (dataIntv == 2) {
dataIntv = 3;
} else if (dataIntv == 3) {
dataIntv = 4;
} else if (dataIntv == 4) {
dataIntv = 8;
} else {
dataIntv = 1;
}
return 0; // Commit the key
}
}
// Keylock function
if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock;
return 0; // Commit the key
}
return key;
}
int displayPage(PageData &pageData){
GwConfigHandler *config = commonData->config;
GwLog *logger = commonData->logger;
virtual void displayNew(PageData& pageData)
{
#ifdef BOARD_OBP60S3
// Clear optical warning
if (flashLED == "Limit Violation") {
setBlinkingLED(false);
setFlashLED(false);
}
#endif
// buffer initialization will fail, if page is default page, because <displayNew> is not executed at system start for default page
for (int i = 0; i < NUMVALUES; i++) {
if (!dataChart[i]) { // Create chart objects if they don't exist
// Old values for hold function
static String svalue1old = "";
static String unit1old = "";
static String svalue2old = "";
static String unit2old = "";
GwApi::BoatValue* bValue = pageData.values[i]; // Page boat data element
String bValName = bValue->getName(); // Value name
String bValFormat = bValue->getFormat(); // Value format
// Get config data
String lengthformat = config->getString(config->lengthFormat);
// bool simulation = config->getBool(config->useSimuData);
bool holdvalues = config->getBool(config->holdvalues);
String flashLED = config->getString(config->flashLED);
String backlightMode = config->getString(config->backlight);
dataHstryBuf[i] = pageData.hstryBuffers->getBuffer(bValName);
// Get boat values #1
GwApi::BoatValue *bvalue1 = pageData.values[0]; // First element in list (only one value by PageOneValue)
String name1 = xdrDelete(bvalue1->getName()); // Value name
name1 = name1.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue1, logger); // Check if boat data value is to be calibrated
double value1 = bvalue1->value; // Value as double in SI unit
bool valid1 = bvalue1->valid; // Valid information
String svalue1 = formatValue(bvalue1, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit1 = formatValue(bvalue1, *commonData).unit; // Unit of value
if (dataHstryBuf[i]) {
dataChart[i].reset(new Chart(*dataHstryBuf[i], Chart::dfltChrtDta[bValFormat].range, *commonData, useSimuData));
LOG_DEBUG(GwLog::DEBUG, "PageTwoValues: Created chart object%d for %s", i, bValName.c_str());
} else {
LOG_DEBUG(GwLog::DEBUG, "PageTwoValues: No chart object available for %s", bValName.c_str());
}
}
}
// Get boat values #2
GwApi::BoatValue *bvalue2 = pageData.values[1]; // Second element in list
String name2 = xdrDelete(bvalue2->getName()); // Value name
name2 = name2.substring(0, 6); // String length limit for value name
calibrationData.calibrateInstance(bvalue2, logger); // Check if boat data value is to be calibrated
double value2 = bvalue2->value; // Value as double in SI unit
bool valid2 = bvalue2->valid; // Valid information
String svalue2 = formatValue(bvalue2, *commonData).svalue; // Formatted value as string including unit conversion and switching decimal places
String unit2 = formatValue(bvalue2, *commonData).unit; // Unit of value
setupKeys(); // adjust <mode> key depending on chart supported boat data type
}
int displayPage(PageData& pageData)
{
LOG_DEBUG(GwLog::LOG, "Display PageTwoValues");
// Get boat values for page
std::vector<GwApi::BoatValue*> bValue;
bValue.push_back(pageData.values[0]); // Page boat data element 1
bValue.push_back(pageData.values[1]); // Page boat data element 2
// Optical warning by limit violation (unused)
if (String(flashLED) == "Limit Violation") {
@@ -64,115 +271,59 @@ class PageTwoValues : public Page
setFlashLED(false);
}
// Logging boat values
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
LOG_DEBUG(GwLog::LOG,"Drawing at PageTwoValues, %s: %f, %s: %f", name1.c_str(), value1, name2.c_str(), value2);
if (bValue[0] == NULL && bValue[1] == NULL)
return PAGE_OK; // no data, no page to display
LOG_DEBUG(GwLog::DEBUG, "PageTwoValues: printing #1: %s, %.3f, #2: %s, %.3f",
bValue[0]->getName().c_str(), bValue[0]->value, bValue[1]->getName().c_str(), bValue[1]->value);
// Draw page
//***********************************************************
// Set display in partial refresh mode
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
// ############### Value 1 ################
if (pageMode == VALUES || (dataHstryBuf[0] == nullptr && dataHstryBuf[1] == nullptr)) {
// show only data value; ignore other pageMode options if no chart supported boat data history buffer is available
showData(bValue, FULL);
// Show name
getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 80);
getdisplay().print(name1); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 130);
if(holdvalues == false){
getdisplay().print(unit1); // Unit
}
else{
getdisplay().print(unit1old);
} else if (pageMode == VAL1_CHART) { // show data value 1 and chart
showData({bValue[0]}, HALF);
if (dataChart[0]) {
dataChart[0]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue[0]);
}
// Switch font if format for any values
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 130);
}
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 105);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b);
getdisplay().setCursor(180, 130);
} else if (pageMode == VAL2_CHART) { // show data value 2 and chart
showData({bValue[1]}, HALF);
if (dataChart[1]) {
dataChart[1]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue[1]);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue1); // Real value as formated string
} else if (pageMode == CHARTS) { // show both data charts
if (dataChart[0]) {
if (dataChart[1]) {
dataChart[0]->showChrt(HORIZONTAL, HALF_SIZE_TOP, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[0]);
} else {
dataChart[0]->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[0]);
}
else{
getdisplay().print(svalue1old); // Old value as formated string
}
if(valid1 == true){
svalue1old = svalue1; // Save the old value
unit1old = unit1; // Save the old unit
if (dataChart[1]) {
if (dataChart[0]) {
dataChart[1]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[1]);
} else {
dataChart[1]->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[1]);
}
// ############### Horizontal Line ################
// Horizontal line 3 pix
getdisplay().fillRect(0, 145, 400, 3, commonData->fgcolor);
// ############### Value 2 ################
// Show name
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(20, 190);
getdisplay().print(name2); // Page name
// Show unit
getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(20, 240);
if(holdvalues == false){
getdisplay().print(unit2); // Unit
}
else{
getdisplay().print(unit2old);
}
// Switch font if format for any values
if(bvalue2->getFormat() == "formatLatitude" || bvalue2->getFormat() == "formatLongitude"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(50, 240);
}
else if(bvalue2->getFormat() == "formatTime" || bvalue2->getFormat() == "formatDate"){
getdisplay().setFont(&Ubuntu_Bold20pt8b);
getdisplay().setCursor(170, 215);
}
else{
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b);
getdisplay().setCursor(180, 240);
}
// Show bus data
if(holdvalues == false){
getdisplay().print(svalue2); // Real value as formated string
}
else{
getdisplay().print(svalue2old); // Old value as formated string
}
if(valid2 == true){
svalue2old = svalue2; // Save the old value
unit2old = unit2; // Save the old unit
}
return PAGE_UPDATE;
};
};
static Page *createPage(CommonData &common){
static Page* createPage(CommonData& common)
{
return new PageTwoValues(common);
}
/**
* with the code below we make this page known to the PageTask
* we give it a type (name) that can be selected in the config

View File

@@ -163,8 +163,6 @@ public:
oldShowTruW = !showTruW; // Force chart update in displayPage
#endif
// With chart object initialization being performed here, PageWindPlot won't properly work as default page,
// because <displayNew> is not executed at system start for default page
if (!twdChart) { // Create true wind charts if they don't exist
twdHstry = pageData.hstryBuffers->getBuffer("TWD");
twsHstry = pageData.hstryBuffers->getBuffer("TWS");