mirror of
https://github.com/thooge/esp32-nmea2000-obp60.git
synced 2026-01-26 08:13:05 +01:00
Merge pull request #217 from Scorgan01/WindPlot-v3
Graphical charts v3 + PageOneValue/PageTwoValues with chart option + updated PageWindPlot
This commit is contained in:
@@ -442,7 +442,7 @@ void drawTextRalign(int16_t x, int16_t y, String text) {
|
|||||||
int16_t x1, y1;
|
int16_t x1, y1;
|
||||||
uint16_t w, h;
|
uint16_t w, h;
|
||||||
getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
|
getdisplay().getTextBounds(text, 0, 150, &x1, &y1, &w, &h);
|
||||||
getdisplay().setCursor(x - w, y);
|
getdisplay().setCursor(x - w - 1, y); // '-1' required since some strings wrap around w/o it
|
||||||
getdisplay().print(text);
|
getdisplay().print(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,15 @@ String formatLongitude(double lon) {
|
|||||||
return String(degree, 0) + "\x90 " + String(minute, 4) + "' " + ((lon > 0) ? "E" : "W");
|
return String(degree, 0) + "\x90 " + String(minute, 4) + "' " + ((lon > 0) ? "E" : "W");
|
||||||
}
|
}
|
||||||
|
|
||||||
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata){
|
// Convert and format boat value from SI to user defined format (definition for compatibility purposes)
|
||||||
|
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata) {
|
||||||
|
|
||||||
|
return formatValue(value, commondata, false); // call <formatValue> with standard handling of user setting for simulation data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert and format boat value from SI to user defined format
|
||||||
|
// generate random simulation data; can be deselected to use conversion+formatting function even in simulation mode
|
||||||
|
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool ignoreSimuDataSetting){
|
||||||
GwLog *logger = commondata.logger;
|
GwLog *logger = commondata.logger;
|
||||||
FormattedData result;
|
FormattedData result;
|
||||||
static int dayoffset = 0;
|
static int dayoffset = 0;
|
||||||
@@ -66,9 +74,15 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata){
|
|||||||
String windspeedFormat = commondata.config->getString(commondata.config->windspeedFormat); // [m/s|km/h|kn|bft]
|
String windspeedFormat = commondata.config->getString(commondata.config->windspeedFormat); // [m/s|km/h|kn|bft]
|
||||||
String tempFormat = commondata.config->getString(commondata.config->tempFormat); // [K|°C|°F]
|
String tempFormat = commondata.config->getString(commondata.config->tempFormat); // [K|°C|°F]
|
||||||
String dateFormat = commondata.config->getString(commondata.config->dateFormat); // [DE|GB|US]
|
String dateFormat = commondata.config->getString(commondata.config->dateFormat); // [DE|GB|US]
|
||||||
bool usesimudata = commondata.config->getBool(commondata.config->useSimuData); // [on|off]
|
|
||||||
String precision = commondata.config->getString(commondata.config->valueprecision); // [1|2]
|
String precision = commondata.config->getString(commondata.config->valueprecision); // [1|2]
|
||||||
|
|
||||||
|
bool usesimudata;
|
||||||
|
if (ignoreSimuDataSetting){
|
||||||
|
usesimudata = false; // ignore user setting for simulation data; we want to format the boat value passed to this function
|
||||||
|
} else {
|
||||||
|
usesimudata = commondata.config->getBool(commondata.config->useSimuData); // [on|off]
|
||||||
|
}
|
||||||
|
|
||||||
// If boat value not valid
|
// If boat value not valid
|
||||||
if (! value->valid && !usesimudata){
|
if (! value->valid && !usesimudata){
|
||||||
result.svalue = "---";
|
result.svalue = "---";
|
||||||
@@ -196,10 +210,10 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata){
|
|||||||
rawvalue = value->value;
|
rawvalue = value->value;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
course = 2.53 + float(random(0, 10) / 100.0);
|
course = M_PI_2 + float(random(-17, 17) / 100.0); // create random course/wind values with 90° +/- 10°
|
||||||
rawvalue = course;
|
rawvalue = course;
|
||||||
}
|
}
|
||||||
course = course * 57.2958; // Unit conversion form rad to deg
|
course = course * RAD_TO_DEG; // Unit conversion form rad to deg
|
||||||
|
|
||||||
// Format 3 numbers with prefix zero
|
// Format 3 numbers with prefix zero
|
||||||
snprintf(buffer,bsize,"%03.0f",course);
|
snprintf(buffer,bsize,"%03.0f",course);
|
||||||
@@ -214,7 +228,7 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata){
|
|||||||
rawvalue = value->value;
|
rawvalue = value->value;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
rawvalue = 4.0 + float(random(0, 40));
|
rawvalue = 4.0 + float(random(-30, 40) / 10.0); // create random speed values from [1..8] m/s
|
||||||
speed = rawvalue;
|
speed = rawvalue;
|
||||||
}
|
}
|
||||||
if (String(speedFormat) == "km/h"){
|
if (String(speedFormat) == "km/h"){
|
||||||
@@ -248,7 +262,7 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata){
|
|||||||
rawvalue = value->value;
|
rawvalue = value->value;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rawvalue = 4.0 + float(random(0, 40));
|
rawvalue = 4.0 + float(random(0, 40) / 10.0); // create random wind speed values from [4..8] m/s
|
||||||
speed = rawvalue;
|
speed = rawvalue;
|
||||||
}
|
}
|
||||||
if (String(windspeedFormat) == "km/h"){
|
if (String(windspeedFormat) == "km/h"){
|
||||||
@@ -433,7 +447,7 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata){
|
|||||||
rawvalue = value->value;
|
rawvalue = value->value;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rawvalue = 18.0 + float(random(0, 100)) / 10.0;
|
rawvalue = 18.0 + float(random(0, 100)) / 10.0; // create random depth values from [18..28] metres
|
||||||
depth = rawvalue;
|
depth = rawvalue;
|
||||||
}
|
}
|
||||||
if(String(lengthFormat) == "ft"){
|
if(String(lengthFormat) == "ft"){
|
||||||
@@ -881,4 +895,30 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata){
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper method for conversion of any data value from SI to user defined format
|
||||||
|
double convertValue(const double &value, const String &name, const String &format, CommonData &commondata)
|
||||||
|
{
|
||||||
|
std::unique_ptr<GwApi::BoatValue> tmpBValue; // Temp variable to get converted data value from <OBP60Formatter::formatValue>
|
||||||
|
double result; // data value converted to user defined target data format
|
||||||
|
constexpr bool NO_SIMUDATA = true; // switch off simulation feature of <formatValue> function
|
||||||
|
|
||||||
|
// prepare temporary BoatValue structure for use in <formatValue>
|
||||||
|
tmpBValue = std::unique_ptr<GwApi::BoatValue>(new GwApi::BoatValue(name)); // we don't need boat value name for pure value conversion
|
||||||
|
tmpBValue->setFormat(format);
|
||||||
|
tmpBValue->valid = true;
|
||||||
|
tmpBValue->value = value;
|
||||||
|
|
||||||
|
result = formatValue(tmpBValue.get(), commondata, NO_SIMUDATA).cvalue; // get value (converted); ignore any simulation data setting
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper method for conversion of any data value from SI to user defined format
|
||||||
|
double convertValue(const double &value, const String &format, CommonData &commondata)
|
||||||
|
{
|
||||||
|
double result; // data value converted to user defined target data format
|
||||||
|
|
||||||
|
result = convertValue(value, "dummy", format, commondata);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,156 +1,119 @@
|
|||||||
#include "OBPDataOperations.h"
|
#include "OBPDataOperations.h"
|
||||||
#include "BoatDataCalibration.h" // Functions lib for data instance calibration
|
#include "BoatDataCalibration.h" // Functions lib for data instance calibration
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
// --- Class HstryBuf ---------------
|
// --- Class HstryBuf ---------------
|
||||||
|
HstryBuf::HstryBuf(const String& name, int size, BoatValueList* boatValues, GwLog* log)
|
||||||
|
: logger(log)
|
||||||
|
, boatDataName(name)
|
||||||
|
{
|
||||||
|
hstryBuf.resize(size);
|
||||||
|
boatValue = boatValues->findValueOrCreate(name);
|
||||||
|
}
|
||||||
|
|
||||||
// Init history buffers for selected boat data
|
void HstryBuf::init(const String& format, int updFreq, int mltplr, double minVal, double maxVal)
|
||||||
void HstryBuf::init(BoatValueList* boatValues, GwLog *log) {
|
{
|
||||||
|
hstryBuf.setMetaData(boatDataName, format, updFreq, mltplr, minVal, maxVal);
|
||||||
logger = log;
|
hstryMin = minVal;
|
||||||
|
hstryMax = maxVal;
|
||||||
int hstryUpdFreq = 1000; // Update frequency for history buffers in ms
|
if (!boatValue->valid) {
|
||||||
int mltplr = 1000; // Multiplier which transforms original <double> value into buffer type format
|
boatValue->setFormat(format);
|
||||||
double hstryMinVal = 0; // Minimum value for these history buffers
|
boatValue->value = std::numeric_limits<double>::max(); // mark current value invalid
|
||||||
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 <mltplr>)
|
|
||||||
awdHstryMax = twdHstryMax;
|
|
||||||
awsHstryMax = twsHstryMax;
|
|
||||||
twdHstryMin = hstryMinVal;
|
|
||||||
twsHstryMin = hstryMinVal;
|
|
||||||
awdHstryMin = hstryMinVal;
|
|
||||||
awsHstryMin = hstryMinVal;
|
|
||||||
const double DBL_MAX = std::numeric_limits<double>::max();
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HstryBuf::add(double value)
|
||||||
|
{
|
||||||
|
if (value >= hstryMin && value <= hstryMax) {
|
||||||
|
hstryBuf.add(value);
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "HstryBuf::add: name: %s, value: %.3f", hstryBuf.getName(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HstryBuf::handle(bool useSimuData, CommonData& common)
|
||||||
|
{
|
||||||
|
// GwApi::BoatValue* tmpBVal;
|
||||||
|
std::unique_ptr<GwApi::BoatValue> tmpBVal; // Temp variable to get formatted and converted data value from OBP60Formatter
|
||||||
|
|
||||||
|
// create temporary boat value for calibration purposes and retrieval of simulation value
|
||||||
|
// tmpBVal = new GwApi::BoatValue(boatDataName.c_str());
|
||||||
|
tmpBVal = std::unique_ptr<GwApi::BoatValue>(new GwApi::BoatValue(boatDataName));
|
||||||
|
tmpBVal->setFormat(boatValue->getFormat());
|
||||||
|
tmpBVal->value = boatValue->value;
|
||||||
|
tmpBVal->valid = boatValue->valid;
|
||||||
|
|
||||||
|
if (boatValue->valid) {
|
||||||
|
// Calibrate boat value before adding it to history buffer
|
||||||
|
calibrationData.calibrateInstance(tmpBVal.get(), logger);
|
||||||
|
add(tmpBVal->value);
|
||||||
|
|
||||||
|
} else if (useSimuData) { // add simulated value to history buffer
|
||||||
|
double simValue = formatValue(tmpBVal.get(), common).value; // simulated value is generated at <formatValue>
|
||||||
|
add(simValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- End Class HstryBuf ---------------
|
||||||
|
|
||||||
|
// --- Class HstryBuffers ---------------
|
||||||
|
HstryBuffers::HstryBuffers(int size, BoatValueList* boatValues, GwLog* log)
|
||||||
|
: size(size)
|
||||||
|
, boatValueList(boatValues)
|
||||||
|
, logger(log)
|
||||||
|
{
|
||||||
|
|
||||||
// collect boat values for true wind calculation
|
// collect boat values for true wind calculation
|
||||||
awaBVal = boatValues->findValueOrCreate("AWA");
|
// should all have been already created at true wind object initialization
|
||||||
hdtBVal = boatValues->findValueOrCreate("HDT");
|
// potentially to be moved to history buffer handling
|
||||||
hdmBVal = boatValues->findValueOrCreate("HDM");
|
awaBVal = boatValueList->findValueOrCreate("AWA");
|
||||||
varBVal = boatValues->findValueOrCreate("VAR");
|
hdtBVal = boatValueList->findValueOrCreate("HDT");
|
||||||
cogBVal = boatValues->findValueOrCreate("COG");
|
hdmBVal = boatValueList->findValueOrCreate("HDM");
|
||||||
sogBVal = boatValues->findValueOrCreate("SOG");
|
varBVal = boatValueList->findValueOrCreate("VAR");
|
||||||
|
cogBVal = boatValueList->findValueOrCreate("COG");
|
||||||
|
sogBVal = boatValueList->findValueOrCreate("SOG");
|
||||||
|
awdBVal = boatValueList->findValueOrCreate("AWD");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle history buffers for TWD, TWS, AWD, AWS
|
// Create history buffer for boat data type
|
||||||
//void HstryBuf::handleHstryBuf(GwApi* api, BoatValueList* boatValues, bool useSimuData) {
|
void HstryBuffers::addBuffer(const String& name)
|
||||||
void HstryBuf::handleHstryBuf(bool useSimuData) {
|
{
|
||||||
|
if (HstryBuffers::getBuffer(name) != nullptr) { // buffer for this data type already exists
|
||||||
static double twd, tws, awd, aws, hdt = 20; //initial value only relevant if we use simulation data
|
return;
|
||||||
GwApi::BoatValue *calBVal; // temp variable just for data calibration -> we don't want to calibrate the original data here
|
}
|
||||||
|
if (bufferParams.find(name) == bufferParams.end()) { // requested boat data type is not supported in list of <bufferParams>
|
||||||
LOG_DEBUG(GwLog::DEBUG,"obp60task handleHstryBuf: TWD_isValid? %d, twdBVal: %.1f, twaBVal: %.1f, twsBVal: %.1f", twdBVal->valid, twdBVal->value * RAD_TO_DEG,
|
return;
|
||||||
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<double>(random(-349, 349) / 1000.0); // add up to +/- 20 degree in RAD
|
|
||||||
twd = WindUtils::to2PI(twd);
|
|
||||||
hstryBufList.twdHstry->add(twd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (twsBVal->valid) {
|
hstryBuffers[name] = std::unique_ptr<HstryBuf>(new HstryBuf(name, size, boatValueList, logger));
|
||||||
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<double>(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (awaBVal->valid) {
|
// Initialize metadata for buffer
|
||||||
if (hdtBVal->valid) {
|
String valueFormat = bufferParams[name].format; // Data format of boat data type
|
||||||
hdt = hdtBVal->value; // Use HDT if available
|
// String valueFormat = boatValueList->findValueOrCreate(name)->getFormat().c_str(); // Unfortunately, format is not yet available during system initialization
|
||||||
} else {
|
int hstryUpdFreq = bufferParams[name].hstryUpdFreq; // Update frequency for history buffers in ms
|
||||||
hdt = WindUtils::calcHDT(&hdmBVal->value, &varBVal->value, &cogBVal->value, &sogBVal->value);
|
int mltplr = bufferParams[name].mltplr; // default multiplier which transforms original <double> value into buffer type format
|
||||||
}
|
double bufferMinVal = bufferParams[name].bufferMinVal; // Min value for this history buffer
|
||||||
|
double bufferMaxVal = bufferParams[name].bufferMaxVal; // Max value for this history buffer
|
||||||
|
|
||||||
awd = awaBVal->value + hdt;
|
hstryBuffers[name]->init(valueFormat, hstryUpdFreq, mltplr, bufferMinVal, bufferMaxVal);
|
||||||
awd = WindUtils::to2PI(awd);
|
LOG_DEBUG(GwLog::DEBUG, "HstryBuffers: new buffer added: name: %s, format: %s, multiplier: %d, min value: %.2f, max value: %.2f", name, valueFormat, mltplr, bufferMinVal, bufferMaxVal);
|
||||||
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
|
|
||||||
awdBVal->value = calBVal->value;
|
|
||||||
awdBVal->valid = true;
|
|
||||||
awd = calBVal->value;
|
|
||||||
if (awd >= awdHstryMin && awd <= awdHstryMax) {
|
|
||||||
hstryBufList.awdHstry->add(awd);
|
|
||||||
}
|
|
||||||
delete calBVal;
|
|
||||||
calBVal = nullptr;
|
|
||||||
} else if (useSimuData) {
|
|
||||||
awd += static_cast<double>(random(-349, 349) / 1000.0); // add up to +/- 20 degree in RAD
|
|
||||||
awd = WindUtils::to2PI(awd);
|
|
||||||
hstryBufList.awdHstry->add(awd);
|
|
||||||
}
|
|
||||||
|
|
||||||
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<double>(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);
|
|
||||||
}
|
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
// --- Class HstryBuf ---------------
|
|
||||||
|
// Handle all registered history buffers
|
||||||
|
void HstryBuffers::handleHstryBufs(bool useSimuData, CommonData& common)
|
||||||
|
{
|
||||||
|
for (auto& bufMap : hstryBuffers) {
|
||||||
|
auto& buf = bufMap.second;
|
||||||
|
buf->handle(useSimuData, common);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RingBuffer<uint16_t>* HstryBuffers::getBuffer(const String& name)
|
||||||
|
{
|
||||||
|
auto it = hstryBuffers.find(name);
|
||||||
|
if (it != hstryBuffers.end()) {
|
||||||
|
return &it->second->hstryBuf;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// --- End Class HstryBuffers ---------------
|
||||||
|
|
||||||
// --- Class WindUtils --------------
|
// --- Class WindUtils --------------
|
||||||
double WindUtils::to2PI(double a)
|
double WindUtils::to2PI(double a)
|
||||||
@@ -216,14 +179,14 @@ void WindUtils::addPolar(const double* phi1, const double* r1,
|
|||||||
|
|
||||||
void WindUtils::calcTwdSA(const double* AWA, const double* AWS,
|
void WindUtils::calcTwdSA(const double* AWA, const double* AWS,
|
||||||
const double* CTW, const double* STW, const double* HDT,
|
const double* CTW, const double* STW, const double* HDT,
|
||||||
double* TWD, double* TWS, double* TWA)
|
double* TWD, double* TWS, double* TWA, double* AWD)
|
||||||
{
|
{
|
||||||
double awd = *AWA + *HDT;
|
*AWD = *AWA + *HDT;
|
||||||
awd = to2PI(awd);
|
*AWD = to2PI(*AWD);
|
||||||
double stw = -*STW;
|
double stw = -*STW;
|
||||||
addPolar(&awd, AWS, CTW, &stw, TWD, TWS);
|
addPolar(AWD, AWS, CTW, &stw, TWD, TWS);
|
||||||
|
|
||||||
// Normalize TWD and TWA to 0-360°
|
// Normalize TWD and TWA to 0-360°/2PI
|
||||||
*TWD = to2PI(*TWD);
|
*TWD = to2PI(*TWD);
|
||||||
*TWA = toPI(*TWD - *HDT);
|
*TWA = toPI(*TWD - *HDT);
|
||||||
}
|
}
|
||||||
@@ -245,12 +208,12 @@ double WindUtils::calcHDT(const double* hdmVal, const double* varVal, const doub
|
|||||||
return hdt;
|
return hdt;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
|
bool WindUtils::calcWinds(const double* awaVal, const double* awsVal,
|
||||||
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
|
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
|
||||||
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal)
|
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal, double* awdVal)
|
||||||
{
|
{
|
||||||
double stw, hdt, ctw;
|
double stw, hdt, ctw;
|
||||||
double twd, tws, twa;
|
double twd, tws, twa, awd;
|
||||||
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
|
double minSogVal = 0.1; // SOG below this value (m/s) is assumed to be data noise from GPS sensor
|
||||||
|
|
||||||
if (*hdtVal != DBL_MAX) {
|
if (*hdtVal != DBL_MAX) {
|
||||||
@@ -274,60 +237,80 @@ bool WindUtils::calcTrueWind(const double* awaVal, const double* awsVal,
|
|||||||
// If STW and SOG are not available, we cannot calculate true wind
|
// If STW and SOG are not available, we cannot calculate true wind
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Serial.println("\ncalcTrueWind: HDT: " + String(hdt) + ", CTW: " + String(ctw) + ", STW: " + String(stw));
|
// LOG_DEBUG(GwLog::DEBUG, "WindUtils:calcWinds: HDT: %.1f, CTW %.1f, STW %.1f", hdt, ctw, stw);
|
||||||
|
|
||||||
if ((*awaVal == DBL_MAX) || (*awsVal == DBL_MAX)) {
|
if ((*awaVal == DBL_MAX) || (*awsVal == DBL_MAX)) {
|
||||||
// Cannot calculate true wind without valid AWA, AWS; other checks are done earlier
|
// Cannot calculate true wind without valid AWA, AWS; other checks are done earlier
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
calcTwdSA(awaVal, awsVal, &ctw, &stw, &hdt, &twd, &tws, &twa);
|
calcTwdSA(awaVal, awsVal, &ctw, &stw, &hdt, &twd, &tws, &twa, &awd);
|
||||||
*twdVal = twd;
|
*twdVal = twd;
|
||||||
*twsVal = tws;
|
*twsVal = tws;
|
||||||
*twaVal = twa;
|
*twaVal = twa;
|
||||||
|
*awdVal = awd;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate true wind data and add to obp60task boat data list
|
// Calculate true wind data and add to obp60task boat data list
|
||||||
bool WindUtils::addTrueWind(GwApi* api, BoatValueList* boatValues, GwLog* log) {
|
bool WindUtils::addWinds()
|
||||||
|
{
|
||||||
|
double twd, tws, twa, awd, hdt;
|
||||||
|
bool twCalculated = false;
|
||||||
|
bool awdCalculated = false;
|
||||||
|
|
||||||
GwLog* logger = log;
|
double awaVal = awaBVal->valid ? awaBVal->value : DBL_MAX;
|
||||||
|
double awsVal = awsBVal->valid ? awsBVal->value : DBL_MAX;
|
||||||
|
double cogVal = cogBVal->valid ? cogBVal->value : DBL_MAX;
|
||||||
|
double stwVal = stwBVal->valid ? stwBVal->value : DBL_MAX;
|
||||||
|
double sogVal = sogBVal->valid ? sogBVal->value : DBL_MAX;
|
||||||
|
double hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MAX;
|
||||||
|
double hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MAX;
|
||||||
|
double varVal = varBVal->valid ? varBVal->value : DBL_MAX;
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "WindUtils:addWinds: AWA %.1f, AWS %.1f, COG %.1f, STW %.1f, SOG %.2f, HDT %.1f, HDM %.1f, VAR %.1f", awaBVal->value * RAD_TO_DEG, awsBVal->value * 3.6 / 1.852,
|
||||||
|
cogBVal->value * RAD_TO_DEG, stwBVal->value * 3.6 / 1.852, sogBVal->value * 3.6 / 1.852, hdtBVal->value * RAD_TO_DEG, hdmBVal->value * RAD_TO_DEG, varBVal->value * RAD_TO_DEG);
|
||||||
|
|
||||||
double awaVal, awsVal, cogVal, stwVal, sogVal, hdtVal, hdmVal, varVal;
|
// Check if TWD can be calculated from TWA and HDT/HDM
|
||||||
double twd, tws, twa;
|
if (twaBVal->valid) {
|
||||||
bool isCalculated = false;
|
|
||||||
|
|
||||||
awaVal = awaBVal->valid ? awaBVal->value : DBL_MAX;
|
|
||||||
awsVal = awsBVal->valid ? awsBVal->value : DBL_MAX;
|
|
||||||
cogVal = cogBVal->valid ? cogBVal->value : DBL_MAX;
|
|
||||||
stwVal = stwBVal->valid ? stwBVal->value : DBL_MAX;
|
|
||||||
sogVal = sogBVal->valid ? sogBVal->value : DBL_MAX;
|
|
||||||
hdtVal = hdtBVal->valid ? hdtBVal->value : DBL_MAX;
|
|
||||||
hdmVal = hdmBVal->valid ? hdmBVal->value : DBL_MAX;
|
|
||||||
varVal = varBVal->valid ? varBVal->value : DBL_MAX;
|
|
||||||
LOG_DEBUG(GwLog::DEBUG,"obp60task addTrueWind: AWA %.1f, AWS %.1f, COG %.1f, STW %.1f, SOG %.2f, HDT %.1f, HDM %.1f, VAR %.1f", awaBVal->value * RAD_TO_DEG, awsBVal->value * 3.6 / 1.852,
|
|
||||||
cogBVal->value * RAD_TO_DEG, stwBVal->value * 3.6 / 1.852, sogBVal->value * 3.6 / 1.852, hdtBVal->value * RAD_TO_DEG, hdmBVal->value * RAD_TO_DEG, varBVal->value * RAD_TO_DEG);
|
|
||||||
|
|
||||||
isCalculated = calcTrueWind(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twd, &tws, &twa);
|
|
||||||
|
|
||||||
if (isCalculated) { // Replace values only, if successfully calculated and not already available
|
|
||||||
if (!twdBVal->valid) {
|
if (!twdBVal->valid) {
|
||||||
|
if (hdtVal != DBL_MAX) {
|
||||||
|
hdt = hdtVal; // Use HDT if available
|
||||||
|
} else {
|
||||||
|
hdt = calcHDT(&hdmVal, &varVal, &cogVal, &sogVal);
|
||||||
|
}
|
||||||
|
twd = twaBVal->value + hdt;
|
||||||
|
twd = to2PI(twd);
|
||||||
twdBVal->value = twd;
|
twdBVal->value = twd;
|
||||||
twdBVal->valid = true;
|
twdBVal->valid = true;
|
||||||
}
|
}
|
||||||
if (!twsBVal->valid) {
|
|
||||||
twsBVal->value = tws;
|
} else {
|
||||||
twsBVal->valid = true;
|
// Calculate true winds and AWD; if true winds exist, use at least AWD calculation
|
||||||
}
|
twCalculated = calcWinds(&awaVal, &awsVal, &cogVal, &stwVal, &sogVal, &hdtVal, &hdmVal, &varVal, &twd, &tws, &twa, &awd);
|
||||||
if (!twaBVal->valid) {
|
|
||||||
twaBVal->value = twa;
|
if (twCalculated) { // Replace values only, if successfully calculated and not already available
|
||||||
twaBVal->valid = true;
|
if (!twdBVal->valid) {
|
||||||
|
twdBVal->value = twd;
|
||||||
|
twdBVal->valid = true;
|
||||||
|
}
|
||||||
|
if (!twsBVal->valid) {
|
||||||
|
twsBVal->value = tws;
|
||||||
|
twsBVal->valid = true;
|
||||||
|
}
|
||||||
|
if (!twaBVal->valid) {
|
||||||
|
twaBVal->value = twa;
|
||||||
|
twaBVal->valid = true;
|
||||||
|
}
|
||||||
|
if (!awdBVal->valid) {
|
||||||
|
awdBVal->value = awd;
|
||||||
|
awdBVal->valid = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_DEBUG(GwLog::DEBUG,"obp60task addTrueWind: isCalculated %d, TWD %.1f, TWA %.1f, TWS %.1f", isCalculated, twdBVal->value * RAD_TO_DEG,
|
LOG_DEBUG(GwLog::DEBUG, "WindUtils:addWinds: twCalculated %d, TWD %.1f, TWA %.1f, TWS %.2f kn, AWD: %.1f", twCalculated, twdBVal->value * RAD_TO_DEG,
|
||||||
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852);
|
twaBVal->value * RAD_TO_DEG, twsBVal->value * 3.6 / 1.852, awdBVal->value * RAD_TO_DEG);
|
||||||
|
|
||||||
return isCalculated;
|
return twCalculated;
|
||||||
}
|
}
|
||||||
// --- Class WindUtils --------------
|
// --- End Class WindUtils --------------
|
||||||
@@ -1,67 +1,89 @@
|
|||||||
// Function lib for history buffer handling, true wind calculation, and other operations on boat data
|
// Function lib for history buffer handling, true wind calculation, and other operations on boat data
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "OBPRingBuffer.h"
|
#include "OBPRingBuffer.h"
|
||||||
|
#include "Pagedata.h"
|
||||||
#include "obp60task.h"
|
#include "obp60task.h"
|
||||||
|
#include <map>
|
||||||
typedef struct {
|
|
||||||
RingBuffer<uint16_t>* twdHstry;
|
|
||||||
RingBuffer<uint16_t>* twsHstry;
|
|
||||||
RingBuffer<uint16_t>* awdHstry;
|
|
||||||
RingBuffer<uint16_t>* awsHstry;
|
|
||||||
} tBoatHstryData; // Holds pointers to all history buffers for boat data
|
|
||||||
|
|
||||||
class HstryBuf {
|
class HstryBuf {
|
||||||
private:
|
private:
|
||||||
GwLog *logger;
|
RingBuffer<uint16_t> hstryBuf; // Circular buffer to store history values
|
||||||
|
String boatDataName;
|
||||||
|
double hstryMin;
|
||||||
|
double hstryMax;
|
||||||
|
GwApi::BoatValue* boatValue;
|
||||||
|
GwLog* logger;
|
||||||
|
|
||||||
RingBuffer<uint16_t> twdHstry; // Circular buffer to store true wind direction values
|
friend class HstryBuffers;
|
||||||
RingBuffer<uint16_t> twsHstry; // Circular buffer to store true wind speed values (TWS)
|
|
||||||
RingBuffer<uint16_t> awdHstry; // Circular buffer to store apparent wind direction values
|
|
||||||
RingBuffer<uint16_t> 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;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
tBoatHstryData hstryBufList;
|
HstryBuf(const String& name, int size, BoatValueList* boatValues, GwLog* log);
|
||||||
|
void init(const String& format, int updFreq, int mltplr, double minVal, double maxVal);
|
||||||
|
void add(double value);
|
||||||
|
void handle(bool useSimuData, CommonData& common);
|
||||||
|
};
|
||||||
|
|
||||||
HstryBuf(){
|
class HstryBuffers {
|
||||||
hstryBufList = {&twdHstry, &twsHstry, &awdHstry, &awsHstry}; // Generate history buffers of zero size
|
private:
|
||||||
|
std::map<String, std::unique_ptr<HstryBuf>> hstryBuffers;
|
||||||
|
int size; // size of all history buffers
|
||||||
|
BoatValueList* boatValueList;
|
||||||
|
GwLog* logger;
|
||||||
|
GwApi::BoatValue *awaBVal, *hdtBVal, *hdmBVal, *varBVal, *cogBVal, *sogBVal, *awdBVal; // boat values for true wind calculation
|
||||||
|
|
||||||
|
struct HistoryParams {
|
||||||
|
int hstryUpdFreq; // update frequency of history buffer (documentation only)
|
||||||
|
int mltplr; // specifies actual value precision being storable:
|
||||||
|
// [10000: 0 - 6.5535 | 1000: 0 - 65.535 | 100: 0 - 650.35 | 10: 0 - 6503.5
|
||||||
|
double bufferMinVal; // minimum valid data value
|
||||||
|
double bufferMaxVal; // maximum valid data value
|
||||||
|
String format; // format of data type
|
||||||
};
|
};
|
||||||
|
|
||||||
HstryBuf(int size) {
|
// Define buffer parameters for supported boat data type
|
||||||
hstryBufList = {&twdHstry, &twsHstry, &awdHstry, &awsHstry};
|
std::map<String, HistoryParams> bufferParams = {
|
||||||
hstryBufList.twdHstry->resize(size); // store <size> xWD values for <size>/60 minutes history
|
{ "AWA", { 1000, 10000, 0.0, M_TWOPI, "formatWind" } },
|
||||||
hstryBufList.twsHstry->resize(size);
|
{ "AWD", { 1000, 10000, 0.0, M_TWOPI, "formatCourse" } },
|
||||||
hstryBufList.awdHstry->resize(size);
|
{ "AWS", { 1000, 1000, 0.0, 65.0, "formatKnots" } },
|
||||||
hstryBufList.awsHstry->resize(size);
|
{ "COG", { 1000, 10000, 0.0, M_TWOPI, "formatCourse" } },
|
||||||
|
{ "DBS", { 1000, 100, 0.0, 650.0, "formatDepth" } },
|
||||||
|
{ "DBT", { 1000, 100, 0.0, 650.0, "formatDepth" } },
|
||||||
|
{ "DPT", { 1000, 100, 0.0, 650.0, "formatDepth" } },
|
||||||
|
{ "HDM", { 1000, 10000, 0.0, M_TWOPI, "formatCourse" } },
|
||||||
|
{ "HDT", { 1000, 10000, 0.0, M_TWOPI, "formatCourse" } },
|
||||||
|
{ "ROT", { 1000, 10000, -M_PI / 180.0 * 99.0, M_PI / 180.0 * 99.0, "formatRot" } }, // min/max is -/+ 99 degrees for "rate of turn"
|
||||||
|
{ "SOG", { 1000, 1000, 0.0, 65.0, "formatKnots" } },
|
||||||
|
{ "STW", { 1000, 1000, 0.0, 65.0, "formatKnots" } },
|
||||||
|
{ "TWA", { 1000, 10000, 0.0, M_TWOPI, "formatWind" } },
|
||||||
|
{ "TWD", { 1000, 10000, 0.0, M_TWOPI, "formatCourse" } },
|
||||||
|
{ "TWS", { 1000, 1000, 0.0, 65.0, "formatKnots" } },
|
||||||
|
{ "WTemp", { 1000, 100, 233.0, 650.0, "kelvinToC" } } // [-50..376] °C
|
||||||
};
|
};
|
||||||
void init(BoatValueList* boatValues, GwLog *log);
|
|
||||||
void handleHstryBuf(bool useSimuData);
|
public:
|
||||||
|
HstryBuffers(int size, BoatValueList* boatValues, GwLog* log);
|
||||||
|
void addBuffer(const String& name);
|
||||||
|
void handleHstryBufs(bool useSimuData, CommonData& common);
|
||||||
|
RingBuffer<uint16_t>* getBuffer(const String& name);
|
||||||
};
|
};
|
||||||
|
|
||||||
class WindUtils {
|
class WindUtils {
|
||||||
private:
|
private:
|
||||||
GwApi::BoatValue *twdBVal, *twsBVal, *twaBVal;
|
GwApi::BoatValue *twaBVal, *twsBVal, *twdBVal;
|
||||||
GwApi::BoatValue *awaBVal, *awsBVal, *cogBVal, *stwBVal, *sogBVal, *hdtBVal, *hdmBVal, *varBVal;
|
GwApi::BoatValue *awaBVal, *awsBVal, *awdBVal, *cogBVal, *stwBVal, *sogBVal, *hdtBVal, *hdmBVal, *varBVal;
|
||||||
static constexpr double DBL_MAX = std::numeric_limits<double>::max();
|
static constexpr double DBL_MAX = std::numeric_limits<double>::max();
|
||||||
|
GwLog* logger;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WindUtils(BoatValueList* boatValues){
|
WindUtils(BoatValueList* boatValues, GwLog* log)
|
||||||
twdBVal = boatValues->findValueOrCreate("TWD");
|
: logger(log)
|
||||||
twsBVal = boatValues->findValueOrCreate("TWS");
|
{
|
||||||
twaBVal = boatValues->findValueOrCreate("TWA");
|
twaBVal = boatValues->findValueOrCreate("TWA");
|
||||||
|
twsBVal = boatValues->findValueOrCreate("TWS");
|
||||||
|
twdBVal = boatValues->findValueOrCreate("TWD");
|
||||||
awaBVal = boatValues->findValueOrCreate("AWA");
|
awaBVal = boatValues->findValueOrCreate("AWA");
|
||||||
awsBVal = boatValues->findValueOrCreate("AWS");
|
awsBVal = boatValues->findValueOrCreate("AWS");
|
||||||
|
awdBVal = boatValues->findValueOrCreate("AWD");
|
||||||
cogBVal = boatValues->findValueOrCreate("COG");
|
cogBVal = boatValues->findValueOrCreate("COG");
|
||||||
stwBVal = boatValues->findValueOrCreate("STW");
|
stwBVal = boatValues->findValueOrCreate("STW");
|
||||||
sogBVal = boatValues->findValueOrCreate("SOG");
|
sogBVal = boatValues->findValueOrCreate("SOG");
|
||||||
@@ -81,10 +103,10 @@ public:
|
|||||||
double* phi, double* r);
|
double* phi, double* r);
|
||||||
void calcTwdSA(const double* AWA, const double* AWS,
|
void calcTwdSA(const double* AWA, const double* AWS,
|
||||||
const double* CTW, const double* STW, const double* HDT,
|
const double* CTW, const double* STW, const double* HDT,
|
||||||
double* TWD, double* TWS, double* TWA);
|
double* TWD, double* TWS, double* TWA, double* AWD);
|
||||||
static double calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal);
|
static double calcHDT(const double* hdmVal, const double* varVal, const double* cogVal, const double* sogVal);
|
||||||
bool calcTrueWind(const double* awaVal, const double* awsVal,
|
bool calcWinds(const double* awaVal, const double* awsVal,
|
||||||
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
|
const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal,
|
||||||
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal);
|
const double* hdmVal, const double* varVal, double* twdVal, double* twsVal, double* twaVal, double* awdVal);
|
||||||
bool addTrueWind(GwApi* api, BoatValueList* boatValues, GwLog *log);
|
bool addWinds();
|
||||||
};
|
};
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,47 +1,75 @@
|
|||||||
// Function lib for display of boat data in various graphical chart formats
|
// Function lib for display of boat data in various graphical chart formats
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
|
#include "OBP60Extensions.h"
|
||||||
|
|
||||||
struct Pos {
|
struct Pos {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
};
|
};
|
||||||
template <typename T> class RingBuffer;
|
|
||||||
class GwLog;
|
struct ChartProps {
|
||||||
|
double range;
|
||||||
|
double step;
|
||||||
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
class RingBuffer;
|
||||||
|
class GwLog;
|
||||||
|
|
||||||
class Chart {
|
class Chart {
|
||||||
protected:
|
protected:
|
||||||
CommonData *commonData;
|
CommonData* commonData;
|
||||||
GwLog *logger;
|
GwLog* logger;
|
||||||
|
|
||||||
RingBuffer<T> &dataBuf; // Buffer to display
|
enum ChrtDataFormat {
|
||||||
int8_t chrtDir; // Chart timeline direction: [0] = horizontal, [1] = vertical
|
WIND,
|
||||||
int8_t chrtSz; // Chart size: [0] = full size, [1] = half size left/top, [2] half size right/bottom
|
ROTATION,
|
||||||
|
SPEED,
|
||||||
|
DEPTH,
|
||||||
|
TEMPERATURE,
|
||||||
|
OTHER
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr char HORIZONTAL = 'H';
|
||||||
|
static constexpr char VERTICAL = 'V';
|
||||||
|
static constexpr int8_t FULL_SIZE = 0;
|
||||||
|
static constexpr int8_t HALF_SIZE_LEFT = 1;
|
||||||
|
static constexpr int8_t HALF_SIZE_RIGHT = 2;
|
||||||
|
|
||||||
|
static constexpr int8_t MIN_FREE_VALUES = 60; // free 60 values when chart line reaches chart end
|
||||||
|
static constexpr int8_t THRESHOLD_NO_DATA = 3; // max. seconds of invalid values in a row
|
||||||
|
static constexpr int8_t VALAXIS_STEP = 60; // pixels between two chart value axis labels
|
||||||
|
|
||||||
|
static constexpr bool NO_SIMUDATA = true; // switch off simulation feature of <formatValue> function
|
||||||
|
|
||||||
|
RingBuffer<uint16_t>& dataBuf; // Buffer to display
|
||||||
|
//char chrtDir; // Chart timeline direction: 'H' = horizontal, 'V' = vertical
|
||||||
|
//int8_t chrtSz; // Chart size: [0] = full size, [1] = half size left/top, [2] half size right/bottom
|
||||||
double dfltRng; // Default range of chart, e.g. 30 = [0..30]
|
double dfltRng; // Default range of chart, e.g. 30 = [0..30]
|
||||||
uint16_t fgColor; // color code for any screen writing
|
uint16_t fgColor; // color code for any screen writing
|
||||||
uint16_t bgColor; // color code for screen background
|
uint16_t bgColor; // color code for screen background
|
||||||
bool useSimuData; // flag to indicate if simulation data is active
|
bool useSimuData; // flag to indicate if simulation data is active
|
||||||
|
String tempFormat; // user defined format for temperature
|
||||||
|
double zeroValue; // "0" SI value for temperature
|
||||||
|
|
||||||
int top = 48; // display top header lines
|
|
||||||
int bottom = 22; // display bottom lines
|
|
||||||
int hGap = 11; // gap between 2 horizontal charts; actual gap is 2x <gap>
|
|
||||||
int vGap = 20; // gap between 2 vertical charts; actual gap is 2x <gap>
|
|
||||||
int xOffset = 33; // offset for horizontal axis (time/value), because of space for left vertical axis labeling
|
|
||||||
int yOffset = 10; // offset for vertical axis (time/value), because of space for top horizontal axis labeling
|
|
||||||
int dWidth; // Display width
|
int dWidth; // Display width
|
||||||
int dHeight; // Display height
|
int dHeight; // Display height
|
||||||
|
int top = 44; // chart gap at top of display (25 lines for standard gap + 19 lines for axis labels)
|
||||||
|
int bottom = 25; // chart gap at bottom of display to keep space for status line
|
||||||
|
int hGap = 11; // gap between 2 horizontal charts; actual gap is 2x <gap>
|
||||||
|
int vGap = 17; // gap between 2 vertical charts; actual gap is 2x <gap>
|
||||||
int timAxis, valAxis; // size of time and value chart axis
|
int timAxis, valAxis; // size of time and value chart axis
|
||||||
Pos cStart; // start point of chart area
|
Pos cRoot; // start point of chart area
|
||||||
double chrtRng; // Range of buffer values from min to max value
|
double chrtRng; // Range of buffer values from min to max value
|
||||||
double chrtMin; // Range low end value
|
double chrtMin; // Range low end value
|
||||||
double chrtMax; // Range high end value
|
double chrtMax; // Range high end value
|
||||||
double chrtMid; // Range mid value
|
double chrtMid; // Range mid value
|
||||||
double rngStep; // Defines the step of adjustment (e.g. 10 m/s) for value axis range
|
double rngStep; // Defines the step of adjustment (e.g. 10 m/s) for value axis range
|
||||||
bool recalcRngCntr = false; // Flag for re-calculation of mid value of chart for wind data types
|
bool recalcRngMid = false; // Flag for re-calculation of mid value of chart for wind data types
|
||||||
|
|
||||||
String dbName, dbFormat; // Name and format of data buffer
|
String dbName, dbFormat; // Name and format of data buffer
|
||||||
int chrtDataFmt; // Data format of chart: [0] size values; [1] degree of course or wind; [2] rotational degrees
|
ChrtDataFormat chrtDataFmt; // Data format of chart boat data type
|
||||||
double dbMIN_VAL; // Lowest possible value of buffer of type <T>
|
double dbMIN_VAL; // Lowest possible value of buffer of type <T>
|
||||||
double dbMAX_VAL; // Highest possible value of buffer of type <T>; indicates invalid value in buffer
|
double dbMAX_VAL; // Highest possible value of buffer of type <T>; indicates invalid value in buffer
|
||||||
size_t bufSize; // History buffer size: 1.920 values for 32 min. history chart
|
size_t bufSize; // History buffer size: 1.920 values for 32 min. history chart
|
||||||
@@ -52,19 +80,37 @@ protected:
|
|||||||
size_t currIdx; // Current index in TWD history buffer
|
size_t currIdx; // Current index in TWD history buffer
|
||||||
size_t lastIdx; // Last index of TWD history buffer
|
size_t lastIdx; // Last index of TWD history buffer
|
||||||
size_t lastAddedIdx = 0; // Last index of TWD history buffer when new data was added
|
size_t lastAddedIdx = 0; // Last index of TWD history buffer when new data was added
|
||||||
|
int numNoData; // Counter for multiple invalid data values in a row
|
||||||
bool bufDataValid = false; // Flag to indicate if buffer data is valid
|
bool bufDataValid = false; // Flag to indicate if buffer data is valid
|
||||||
int oldChrtIntv = 0; // remember recent user selection of data interval
|
int oldChrtIntv = 0; // remember recent user selection of data interval
|
||||||
|
|
||||||
void drawChrt(int8_t chrtIntv, GwApi::BoatValue& currValue); // Draw chart line
|
double chrtPrevVal; // Last data value in chart area
|
||||||
double getRng(double center, size_t amount); // Calculate range between chart center and edges
|
int x, y; // x and y coordinates for drawing
|
||||||
void calcChrtBorders(double& rngMid, double& rngMin, double& rngMax, double& rng); // Calculate chart points for value axis and return range between <min> and <max>
|
int prevX, prevY; // Last x and y coordinates for drawing
|
||||||
void drawChrtTimeAxis(int8_t chrtIntv); // Draw time axis of chart, value and lines
|
|
||||||
void drawChrtValAxis(); // Draw value axis of chart, value and lines
|
bool setChartDimensions(const char direction, const int8_t size); //define dimensions and start points for chart
|
||||||
void prntCurrValue(GwApi::BoatValue& currValue); // Add current boat data value to chart
|
void drawChrt(const char chrtDir, const int8_t chrtIntv, GwApi::BoatValue& currValue); // Draw chart line
|
||||||
|
void getBufferStartNSize(const int8_t chrtIntv); // Identify buffer size and buffer start position for chart
|
||||||
|
void calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, double& rng); // Calculate chart points for value axis and return range between <min> and <max>
|
||||||
|
void drawChartLines(const char direction, const int8_t chrtIntv, const double chrtScale); // Draw chart graph
|
||||||
|
Pos setCurrentChartPoint(const int i, const char direction, const double chrtVal, const double chrtScale); // Set current chart point to draw
|
||||||
|
void drawChrtTimeAxis(const char chrtDir, const int8_t chrtSz, const int8_t chrtIntv); // Draw time axis of chart, value and lines
|
||||||
|
void drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntLabel); // Draw value axis of chart, value and lines
|
||||||
|
void prntCurrValue(const char chrtDir, GwApi::BoatValue& currValue); // Add current boat data value to chart
|
||||||
|
void prntNoValidData(const char chrtDir); // print message for no valid data available
|
||||||
|
double getAngleRng(const double center, size_t amount); // Calculate range between chart center and edges
|
||||||
|
void prntVerticChartThreeValueAxisLabel(const GFXfont* font); // print value axis label with only three values: top, mid, and bottom for vertical chart
|
||||||
|
void prntHorizChartThreeValueAxisLabel(const GFXfont* font); // print value axis label with only three values: top, mid, and bottom for horizontal chart
|
||||||
|
void prntHorizChartMultiValueAxisLabel(const GFXfont* font); // print value axis label with multiple axis lines for horizontal chart
|
||||||
|
void drawBoldLine(const int16_t x1, const int16_t y1, const int16_t x2, const int16_t y2); // Draw chart line with thickness of 2px
|
||||||
|
String convNformatLabel(const double& label); // Convert and format current axis label to user defined format; helper function for easier handling of OBP60Formatter
|
||||||
|
String formatLabel(const double& label); // Format current axis label for printing w/o data format conversion (has been done earlier)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Chart(RingBuffer<T>& dataBuf, int8_t chrtDir, int8_t chrtSz, double dfltRng, CommonData& common, bool useSimuData); // Chart object of data chart
|
// Define default chart range and range step for each boat data type
|
||||||
~Chart();
|
static std::map<String, ChartProps> dfltChrtDta;
|
||||||
void showChrt(int8_t chrtIntv, GwApi::BoatValue currValue); // Perform all actions to draw chart
|
|
||||||
|
|
||||||
};
|
Chart(RingBuffer<uint16_t>& dataBuf, double dfltRng, CommonData& common, bool useSimuData); // Chart object of data chart
|
||||||
|
~Chart();
|
||||||
|
void showChrt(char chrtDir, int8_t chrtSz, const int8_t chrtIntv, bool prntName, bool showCurrValue, GwApi::BoatValue currValue); // Perform all actions to draw chart
|
||||||
|
};
|
||||||
|
|||||||
@@ -3,112 +3,300 @@
|
|||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
#include "BoatDataCalibration.h"
|
#include "BoatDataCalibration.h"
|
||||||
|
#include "OBPDataOperations.h"
|
||||||
|
#include "OBPcharts.h"
|
||||||
|
|
||||||
class PageOneValue : public Page
|
class PageOneValue : public Page {
|
||||||
{
|
private:
|
||||||
public:
|
GwLog* logger;
|
||||||
PageOneValue(CommonData &common){
|
|
||||||
commonData = &common;
|
enum PageMode {
|
||||||
common.logger->logDebug(GwLog::LOG,"Instantiate PageOneValue");
|
VALUE,
|
||||||
|
BOTH,
|
||||||
|
CHART
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
|
||||||
|
int width; // Screen width
|
||||||
|
int height; // Screen height
|
||||||
|
|
||||||
|
bool keylock = false; // Keylock
|
||||||
|
PageMode pageMode = VALUE; // 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;
|
||||||
|
|
||||||
|
// Old values for hold function
|
||||||
|
String sValue1Old = "";
|
||||||
|
String unit1Old = "";
|
||||||
|
|
||||||
|
// Data buffer pointer (owned by HstryBuffers)
|
||||||
|
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;
|
||||||
|
const GFXfont *nameFnt, *unitFnt, *valueFnt1, *valueFnt2, *valueFnt3;
|
||||||
|
|
||||||
|
if (mode == FULL) { // full size data display
|
||||||
|
nameXoff = 0;
|
||||||
|
nameYoff = 0;
|
||||||
|
nameFnt = &Ubuntu_Bold32pt8b;
|
||||||
|
unitXoff = 0;
|
||||||
|
unitYoff = 0;
|
||||||
|
unitFnt = &Ubuntu_Bold20pt8b;
|
||||||
|
value1Xoff = 0;
|
||||||
|
value1Yoff = 0;
|
||||||
|
valueFnt1 = &Ubuntu_Bold20pt8b;
|
||||||
|
valueFnt2 = &Ubuntu_Bold32pt8b;
|
||||||
|
valueFnt3 = &DSEG7Classic_BoldItalic60pt7b;
|
||||||
|
} else { // half size data and chart display
|
||||||
|
nameXoff = -10;
|
||||||
|
nameYoff = -34;
|
||||||
|
nameFnt = &Ubuntu_Bold20pt8b;
|
||||||
|
unitXoff = -295;
|
||||||
|
unitYoff = -119;
|
||||||
|
unitFnt = &Ubuntu_Bold12pt8b;
|
||||||
|
valueFnt1 = &Ubuntu_Bold12pt8b;
|
||||||
|
value1Xoff = 153;
|
||||||
|
value1Yoff = -119;
|
||||||
|
valueFnt2 = &Ubuntu_Bold20pt8b;
|
||||||
|
valueFnt3 = &DSEG7Classic_BoldItalic42pt7b;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Show name
|
||||||
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
getdisplay().setFont(nameFnt);
|
||||||
|
getdisplay().setCursor(20 + nameXoff, 100 + nameYoff);
|
||||||
|
getdisplay().print(name1); // name
|
||||||
|
|
||||||
|
// Show unit
|
||||||
|
getdisplay().setFont(unitFnt);
|
||||||
|
getdisplay().setCursor(305 + unitXoff, 240 + unitYoff);
|
||||||
|
|
||||||
|
if (holdValues) {
|
||||||
|
getdisplay().print(unit1Old); // name
|
||||||
|
} else {
|
||||||
|
getdisplay().print(unit1); // name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch font depending on value format and adjust position
|
||||||
|
if (bValue1->getFormat() == "formatLatitude" || bValue1->getFormat() == "formatLongitude") {
|
||||||
|
getdisplay().setFont(valueFnt1);
|
||||||
|
getdisplay().setCursor(20 + value1Xoff, 180 + value1Yoff);
|
||||||
|
} else if (bValue1->getFormat() == "formatTime" || bValue1->getFormat() == "formatDate") {
|
||||||
|
getdisplay().setFont(valueFnt2);
|
||||||
|
getdisplay().setCursor(20 + value1Xoff, 200 + value1Yoff);
|
||||||
|
} else {
|
||||||
|
getdisplay().setFont(valueFnt3);
|
||||||
|
getdisplay().setCursor(20 + value1Xoff, 240 + value1Yoff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show bus data
|
||||||
|
if (!holdValues || useSimuData) {
|
||||||
|
getdisplay().print(sValue1); // Real value as formated string
|
||||||
|
} else {
|
||||||
|
getdisplay().print(sValue1Old); // Old value as formated string
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valid1 == true) {
|
||||||
|
sValue1Old = sValue1; // Save the old value
|
||||||
|
unit1Old = unit1; // Save the old unit
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int handleKey(int key){
|
public:
|
||||||
// Code for keylock
|
PageOneValue(CommonData& common)
|
||||||
if(key == 11){
|
{
|
||||||
|
commonData = &common;
|
||||||
|
logger = commonData->logger;
|
||||||
|
LOG_DEBUG(GwLog::LOG, "Instantiate PageOneValue");
|
||||||
|
|
||||||
|
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) { // show "Mode" key only if 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) { // if boat data type supports charts
|
||||||
|
|
||||||
|
// Set page mode: value | value/half chart | full chart
|
||||||
|
if (key == 1) {
|
||||||
|
switch (pageMode) {
|
||||||
|
case VALUE:
|
||||||
|
pageMode = BOTH;
|
||||||
|
break;
|
||||||
|
case BOTH:
|
||||||
|
pageMode = CHART;
|
||||||
|
break;
|
||||||
|
case CHART:
|
||||||
|
pageMode = VALUE;
|
||||||
|
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;
|
commonData->keylock = !commonData->keylock;
|
||||||
return 0; // Commit the key
|
return 0; // Commit the key
|
||||||
}
|
}
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
int displayPage(PageData &pageData){
|
virtual void displayNew(PageData& pageData)
|
||||||
GwConfigHandler *config = commonData->config;
|
{
|
||||||
GwLog *logger = commonData->logger;
|
#ifdef BOARD_OBP60S3
|
||||||
|
// Clear optical warning
|
||||||
// Old values for hold function
|
if (flashLED == "Limit Violation") {
|
||||||
static String svalue1old = "";
|
|
||||||
static String unit1old = "";
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Get boat values
|
|
||||||
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
|
|
||||||
|
|
||||||
// Optical warning by limit violation (unused)
|
|
||||||
if(String(flashLED) == "Limit Violation"){
|
|
||||||
setBlinkingLED(false);
|
setBlinkingLED(false);
|
||||||
setFlashLED(false);
|
setFlashLED(false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// buffer initialization will fail, if page is default page, because <displayNew> is not executed at system start for default page
|
||||||
|
if (!dataChart) { // Create chart objects if they don't exist
|
||||||
|
|
||||||
|
GwApi::BoatValue* bValue1 = pageData.values[0]; // Page boat data element
|
||||||
|
String bValName1 = bValue1->getName(); // Value name
|
||||||
|
String bValFormat = bValue1->getFormat(); // Value format
|
||||||
|
|
||||||
|
dataHstryBuf = pageData.hstryBuffers->getBuffer(bValName1);
|
||||||
|
|
||||||
|
if (dataHstryBuf) {
|
||||||
|
dataChart.reset(new Chart(*dataHstryBuf, Chart::dfltChrtDta[bValFormat].range, *commonData, useSimuData));
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageOneValue: Created chart objects for %s", bValName1);
|
||||||
|
} else {
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageOneValue: No chart objects available for %s", bValName1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logging boat values
|
setupKeys(); // adjust <mode> key depending on chart supported boat data type
|
||||||
if (bvalue1 == NULL) return PAGE_OK; // WTF why this statement?
|
}
|
||||||
LOG_DEBUG(GwLog::LOG,"Drawing at PageOneValue, %s: %f", name1.c_str(), value1);
|
|
||||||
|
int displayPage(PageData& pageData)
|
||||||
|
{
|
||||||
|
LOG_DEBUG(GwLog::LOG, "Display PageOneValue");
|
||||||
|
|
||||||
|
// Get boat value for page
|
||||||
|
GwApi::BoatValue* bValue1 = pageData.values[0]; // Page boat data element
|
||||||
|
|
||||||
|
// Optical warning by limit violation (unused)
|
||||||
|
if (String(flashLED) == "Limit Violation") {
|
||||||
|
setBlinkingLED(false);
|
||||||
|
setFlashLED(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bValue1 == NULL)
|
||||||
|
return PAGE_OK; // no data, no page to display
|
||||||
|
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageOneValue: printing %s, %.3f", bValue1->getName().c_str(), bValue1->value);
|
||||||
|
|
||||||
// Draw page
|
// Draw page
|
||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
/// Set display in partial refresh mode
|
getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().height()); // Set partial update
|
|
||||||
|
|
||||||
// Show name
|
if (pageMode == VALUE || dataHstryBuf == nullptr) {
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
// show only data value; ignore other pageMode options if no chart supported boat data history buffer is available
|
||||||
getdisplay().setFont(&Ubuntu_Bold32pt8b);
|
showData(bValue1, FULL);
|
||||||
getdisplay().setCursor(20, 100);
|
|
||||||
getdisplay().print(name1); // Page name
|
|
||||||
|
|
||||||
// Show unit
|
} else if (pageMode == CHART) { // show only data chart
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
if (dataChart) {
|
||||||
getdisplay().setCursor(270, 100);
|
dataChart->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue1);
|
||||||
if(holdvalues == false){
|
}
|
||||||
getdisplay().print(unit1); // Unit
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().print(unit1old);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Switch font if format for any values
|
} else if (pageMode == BOTH) { // show data value and chart
|
||||||
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
|
showData(bValue1, HALF);
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
if (dataChart) {
|
||||||
getdisplay().setCursor(20, 180);
|
dataChart->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue1);
|
||||||
}
|
}
|
||||||
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
|
|
||||||
getdisplay().setFont(&Ubuntu_Bold32pt8b);
|
|
||||||
getdisplay().setCursor(20, 200);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic60pt7b);
|
|
||||||
getdisplay().setCursor(20, 240);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show bus data
|
|
||||||
if(holdvalues == false){
|
|
||||||
getdisplay().print(svalue1); // Real value as formated string
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().print(svalue1old); // Old value as formated string
|
|
||||||
}
|
|
||||||
if(valid1 == true){
|
|
||||||
svalue1old = svalue1; // Save the old value
|
|
||||||
unit1old = unit1; // Save the old unit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return PAGE_UPDATE;
|
return PAGE_UPDATE;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
static Page* createPage(CommonData &common){
|
static Page* createPage(CommonData& common)
|
||||||
|
{
|
||||||
return new PageOneValue(common);
|
return new PageOneValue(common);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,10 +308,10 @@ static Page* createPage(CommonData &common){
|
|||||||
* this will be number of BoatValue pointers in pageData.values
|
* this will be number of BoatValue pointers in pageData.values
|
||||||
*/
|
*/
|
||||||
PageDescription registerPageOneValue(
|
PageDescription registerPageOneValue(
|
||||||
"OneValue", // Page name
|
"OneValue", // Page name
|
||||||
createPage, // Action
|
createPage, // Action
|
||||||
1, // Number of bus values depends on selection in Web configuration
|
1, // Number of bus values depends on selection in Web configuration
|
||||||
true // Show display header on/off
|
true // Show display header on/off
|
||||||
);
|
);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -3,176 +3,327 @@
|
|||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
#include "BoatDataCalibration.h"
|
#include "BoatDataCalibration.h"
|
||||||
|
#include "OBPDataOperations.h"
|
||||||
|
#include "OBPcharts.h"
|
||||||
|
|
||||||
class PageTwoValues : public Page
|
class PageTwoValues : public Page {
|
||||||
{
|
private:
|
||||||
public:
|
GwLog* logger;
|
||||||
PageTwoValues(CommonData &common){
|
|
||||||
commonData = &common;
|
enum PageMode {
|
||||||
common.logger->logDebug(GwLog::LOG,"Instantiate PageTwoValue");
|
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)
|
||||||
|
{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual int handleKey(int key){
|
public:
|
||||||
// Code for keylock
|
PageTwoValues(CommonData& common)
|
||||||
if(key == 11){
|
{
|
||||||
|
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;
|
commonData->keylock = !commonData->keylock;
|
||||||
return 0; // Commit the key
|
return 0; // Commit the key
|
||||||
}
|
}
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
int displayPage(PageData &pageData){
|
virtual void displayNew(PageData& pageData)
|
||||||
GwConfigHandler *config = commonData->config;
|
{
|
||||||
GwLog *logger = commonData->logger;
|
#ifdef BOARD_OBP60S3
|
||||||
|
// Clear optical warning
|
||||||
// Old values for hold function
|
if (flashLED == "Limit Violation") {
|
||||||
static String svalue1old = "";
|
|
||||||
static String unit1old = "";
|
|
||||||
static String svalue2old = "";
|
|
||||||
static String unit2old = "";
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// Optical warning by limit violation (unused)
|
|
||||||
if(String(flashLED) == "Limit Violation"){
|
|
||||||
setBlinkingLED(false);
|
setBlinkingLED(false);
|
||||||
setFlashLED(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
|
||||||
|
|
||||||
|
GwApi::BoatValue* bValue = pageData.values[i]; // Page boat data element
|
||||||
|
String bValName = bValue->getName(); // Value name
|
||||||
|
String bValFormat = bValue->getFormat(); // Value format
|
||||||
|
|
||||||
|
dataHstryBuf[i] = pageData.hstryBuffers->getBuffer(bValName);
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logging boat values
|
setupKeys(); // adjust <mode> key depending on chart supported boat data type
|
||||||
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);
|
|
||||||
|
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") {
|
||||||
|
setBlinkingLED(false);
|
||||||
|
setFlashLED(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// Draw page
|
||||||
//***********************************************************
|
//***********************************************************
|
||||||
|
|
||||||
// Set display in partial refresh mode
|
getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
|
||||||
getdisplay().setPartialWindow(0, 0, getdisplay().width(), getdisplay().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
|
} else if (pageMode == VAL1_CHART) { // show data value 1 and chart
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
showData({bValue[0]}, HALF);
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
if (dataChart[0]) {
|
||||||
getdisplay().setCursor(20, 80);
|
dataChart[0]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue[0]);
|
||||||
getdisplay().print(name1); // Page name
|
}
|
||||||
|
|
||||||
// Show unit
|
} else if (pageMode == VAL2_CHART) { // show data value 2 and chart
|
||||||
getdisplay().setFont(&Ubuntu_Bold12pt8b);
|
showData({bValue[1]}, HALF);
|
||||||
getdisplay().setCursor(20, 130);
|
if (dataChart[1]) {
|
||||||
if(holdvalues == false){
|
dataChart[1]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, NO_PRNT_NAME, NO_PRNT_VALUE, *bValue[1]);
|
||||||
getdisplay().print(unit1); // Unit
|
}
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().print(unit1old);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Switch font if format for any values
|
} else if (pageMode == CHARTS) { // show both data charts
|
||||||
if(bvalue1->getFormat() == "formatLatitude" || bvalue1->getFormat() == "formatLongitude"){
|
if (dataChart[0]) {
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
if (dataChart[1]) {
|
||||||
getdisplay().setCursor(50, 130);
|
dataChart[0]->showChrt(HORIZONTAL, HALF_SIZE_TOP, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[0]);
|
||||||
}
|
} else {
|
||||||
else if(bvalue1->getFormat() == "formatTime" || bvalue1->getFormat() == "formatDate"){
|
dataChart[0]->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[0]);
|
||||||
getdisplay().setFont(&Ubuntu_Bold20pt8b);
|
}
|
||||||
getdisplay().setCursor(170, 105);
|
}
|
||||||
}
|
if (dataChart[1]) {
|
||||||
else{
|
if (dataChart[0]) {
|
||||||
getdisplay().setFont(&DSEG7Classic_BoldItalic42pt7b);
|
dataChart[1]->showChrt(HORIZONTAL, HALF_SIZE_BOTTOM, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[1]);
|
||||||
getdisplay().setCursor(180, 130);
|
} else {
|
||||||
}
|
dataChart[1]->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[1]);
|
||||||
|
}
|
||||||
// Show bus data
|
}
|
||||||
if(holdvalues == false){
|
|
||||||
getdisplay().print(svalue1); // Real value as formated string
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
getdisplay().print(svalue1old); // Old value as formated string
|
|
||||||
}
|
|
||||||
if(valid1 == true){
|
|
||||||
svalue1old = svalue1; // Save the old value
|
|
||||||
unit1old = unit1; // Save the old unit
|
|
||||||
}
|
|
||||||
|
|
||||||
// ############### 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;
|
return PAGE_UPDATE;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
static Page *createPage(CommonData &common){
|
static Page* createPage(CommonData& common)
|
||||||
|
{
|
||||||
return new PageTwoValues(common);
|
return new PageTwoValues(common);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* with the code below we make this page known to the PageTask
|
* 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
|
* we give it a type (name) that can be selected in the config
|
||||||
@@ -181,10 +332,10 @@ static Page *createPage(CommonData &common){
|
|||||||
* this will be number of BoatValue pointers in pageData.values
|
* this will be number of BoatValue pointers in pageData.values
|
||||||
*/
|
*/
|
||||||
PageDescription registerPageTwoValues(
|
PageDescription registerPageTwoValues(
|
||||||
"TwoValues", // Page name
|
"TwoValues", // Page name
|
||||||
createPage, // Action
|
createPage, // Action
|
||||||
2, // Number of bus values depends on selection in Web configuration
|
2, // Number of bus values depends on selection in Web configuration
|
||||||
true // Show display header on/off
|
true // Show display header on/off
|
||||||
);
|
);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "Pagedata.h"
|
#include "Pagedata.h"
|
||||||
#include "OBP60Extensions.h"
|
#include "OBP60Extensions.h"
|
||||||
|
#include "OBPDataOperations.h"
|
||||||
#include "OBPcharts.h"
|
#include "OBPcharts.h"
|
||||||
|
|
||||||
// ****************************************************************
|
// ****************************************************************
|
||||||
@@ -10,20 +11,58 @@ class PageWindPlot : public Page {
|
|||||||
private:
|
private:
|
||||||
GwLog* logger;
|
GwLog* logger;
|
||||||
|
|
||||||
|
enum ChartMode {
|
||||||
|
DIRECTION,
|
||||||
|
SPEED,
|
||||||
|
BOTH
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr char HORIZONTAL = 'H';
|
||||||
|
static constexpr char VERTICAL = 'V';
|
||||||
|
static constexpr int8_t FULL_SIZE = 0;
|
||||||
|
static constexpr int8_t HALF_SIZE_LEFT = 1;
|
||||||
|
static constexpr int8_t HALF_SIZE_RIGHT = 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;
|
||||||
|
|
||||||
int width; // Screen width
|
int width; // Screen width
|
||||||
int height; // Screen height
|
int height; // Screen height
|
||||||
|
|
||||||
bool keylock = false; // Keylock
|
bool keylock = false; // Keylock
|
||||||
char chrtMode = 'D'; // Chart mode: 'D' for TWD, 'S' for TWS, 'B' for both
|
ChartMode chrtMode = DIRECTION;
|
||||||
bool showTruW = true; // Show true wind or apparent wind in chart area
|
bool showTruW = true; // Show true wind or apparent wind in chart area
|
||||||
bool oldShowTruW = false; // remember recent user selection of wind data type
|
bool oldShowTruW = false; // remember recent user selection of wind data type
|
||||||
|
|
||||||
int dataIntv = 1; // Update interval for wind history chart:
|
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
|
// (1)|(2)|(3)|(4)|(8) x 240 seconds for 4, 8, 12, 16, 32 min. history chart
|
||||||
bool useSimuData;
|
bool useSimuData;
|
||||||
|
// bool holdValues;
|
||||||
String flashLED;
|
String flashLED;
|
||||||
String backlightMode;
|
String backlightMode;
|
||||||
|
|
||||||
|
#ifdef BOARD_OBP40S3
|
||||||
|
String wndSrc; // Wind source true/apparent wind - preselection for OBP40
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Data buffers pointers (owned by HstryBuffers)
|
||||||
|
RingBuffer<uint16_t>* twdHstry = nullptr;
|
||||||
|
RingBuffer<uint16_t>* twsHstry = nullptr;
|
||||||
|
RingBuffer<uint16_t>* awdHstry = nullptr;
|
||||||
|
RingBuffer<uint16_t>* awsHstry = nullptr;
|
||||||
|
|
||||||
|
// Chart objects
|
||||||
|
std::unique_ptr<Chart> twdChart, awdChart; // Chart object for wind direction
|
||||||
|
std::unique_ptr<Chart> twsChart, awsChart; // Chart object for wind speed
|
||||||
|
|
||||||
|
// Active charts and values
|
||||||
|
Chart* wdChart = nullptr;
|
||||||
|
Chart* wsChart = nullptr;
|
||||||
|
GwApi::BoatValue* wdBVal = nullptr;
|
||||||
|
GwApi::BoatValue* wsBVal = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PageWindPlot(CommonData& common)
|
PageWindPlot(CommonData& common)
|
||||||
{
|
{
|
||||||
@@ -31,11 +70,16 @@ public:
|
|||||||
logger = commonData->logger;
|
logger = commonData->logger;
|
||||||
LOG_DEBUG(GwLog::LOG, "Instantiate PageWindPlot");
|
LOG_DEBUG(GwLog::LOG, "Instantiate PageWindPlot");
|
||||||
|
|
||||||
|
width = getdisplay().width(); // Screen width
|
||||||
|
height = getdisplay().height(); // Screen height
|
||||||
|
|
||||||
// Get config data
|
// Get config data
|
||||||
useSimuData = common.config->getBool(common.config->useSimuData);
|
useSimuData = common.config->getBool(common.config->useSimuData);
|
||||||
// holdValues = common.config->getBool(common.config->holdvalues);
|
// holdValues = common.config->getBool(common.config->holdvalues);
|
||||||
flashLED = common.config->getString(common.config->flashLED);
|
flashLED = common.config->getString(common.config->flashLED);
|
||||||
backlightMode = common.config->getString(common.config->backlight);
|
backlightMode = common.config->getString(common.config->backlight);
|
||||||
|
|
||||||
|
oldShowTruW = !showTruW; // makes wind source being initialized at initial page call
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void setupKeys()
|
virtual void setupKeys()
|
||||||
@@ -44,23 +88,23 @@ public:
|
|||||||
commonData->keydata[0].label = "MODE";
|
commonData->keydata[0].label = "MODE";
|
||||||
#if defined BOARD_OBP60S3
|
#if defined BOARD_OBP60S3
|
||||||
commonData->keydata[1].label = "SRC";
|
commonData->keydata[1].label = "SRC";
|
||||||
commonData->keydata[4].label = "INTV";
|
commonData->keydata[4].label = "ZOOM";
|
||||||
#elif defined BOARD_OBP40S3
|
#elif defined BOARD_OBP40S3
|
||||||
commonData->keydata[1].label = "INTV";
|
commonData->keydata[1].label = "ZOOM";
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key functions
|
// Key functions
|
||||||
virtual int handleKey(int key)
|
virtual int handleKey(int key)
|
||||||
{
|
{
|
||||||
// Set chart mode TWD | TWS
|
// Set chart mode
|
||||||
if (key == 1) {
|
if (key == 1) {
|
||||||
if (chrtMode == 'D') {
|
if (chrtMode == DIRECTION) {
|
||||||
chrtMode = 'S';
|
chrtMode = SPEED;
|
||||||
} else if (chrtMode == 'S') {
|
} else if (chrtMode == SPEED) {
|
||||||
chrtMode = 'B';
|
chrtMode = BOTH;
|
||||||
} else {
|
} else {
|
||||||
chrtMode = 'D';
|
chrtMode = DIRECTION;
|
||||||
}
|
}
|
||||||
return 0; // Commit the key
|
return 0; // Commit the key
|
||||||
}
|
}
|
||||||
@@ -101,116 +145,77 @@ public:
|
|||||||
|
|
||||||
virtual void displayNew(PageData& pageData)
|
virtual void displayNew(PageData& pageData)
|
||||||
{
|
{
|
||||||
|
#ifdef BOARD_OBP60S3
|
||||||
|
// Clear optical warning
|
||||||
|
if (flashLED == "Limit Violation") {
|
||||||
|
setBlinkingLED(false);
|
||||||
|
setFlashLED(false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef BOARD_OBP40S3
|
#ifdef BOARD_OBP40S3
|
||||||
String wndSrc; // Wind source true/apparent wind - preselection for OBP40
|
// we can only initialize user defined wind source here, because "pageData" is not available at object instantiation
|
||||||
|
|
||||||
wndSrc = commonData->config->getString("page" + String(pageData.pageNumber) + "wndsrc");
|
wndSrc = commonData->config->getString("page" + String(pageData.pageNumber) + "wndsrc");
|
||||||
if (wndSrc == "True wind") {
|
if (wndSrc == "True wind") {
|
||||||
showTruW = true;
|
showTruW = true;
|
||||||
} else {
|
} else {
|
||||||
showTruW = false; // Wind source is apparent wind
|
showTruW = false; // Wind source is apparent wind
|
||||||
}
|
}
|
||||||
LOG_DEBUG(GwLog::LOG, "New PageWindPlot; wind source=%s", wndSrc);
|
oldShowTruW = !showTruW; // Force chart update in displayPage
|
||||||
#endif
|
#endif
|
||||||
oldShowTruW = !showTruW; // makes wind source being initialized at initial page call
|
|
||||||
|
|
||||||
width = getdisplay().width(); // Screen width
|
if (!twdChart) { // Create true wind charts if they don't exist
|
||||||
height = getdisplay().height(); // Screen height
|
twdHstry = pageData.hstryBuffers->getBuffer("TWD");
|
||||||
|
twsHstry = pageData.hstryBuffers->getBuffer("TWS");
|
||||||
|
|
||||||
|
if (twdHstry) {
|
||||||
|
twdChart.reset(new Chart(*twdHstry, Chart::dfltChrtDta["formatCourse"].range, *commonData, useSimuData));
|
||||||
|
}
|
||||||
|
if (twsHstry) {
|
||||||
|
twsChart.reset(new Chart(*twsHstry, Chart::dfltChrtDta["formatKnots"].range, *commonData, useSimuData));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!awdChart) { // Create apparent wind charts if they don't exist
|
||||||
|
awdHstry = pageData.hstryBuffers->getBuffer("AWD");
|
||||||
|
awsHstry = pageData.hstryBuffers->getBuffer("AWS");
|
||||||
|
|
||||||
|
if (awdHstry) {
|
||||||
|
awdChart.reset(new Chart(*awdHstry, Chart::dfltChrtDta["formatCourse"].range, *commonData, useSimuData));
|
||||||
|
}
|
||||||
|
if (awsHstry) {
|
||||||
|
awsChart.reset(new Chart(*awsHstry, Chart::dfltChrtDta["formatKnots"].range, *commonData, useSimuData));
|
||||||
|
}
|
||||||
|
if (twdHstry && twsHstry && awdHstry && awsHstry) {
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: Created wind charts");
|
||||||
|
} else {
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: Some/all chart objects for wind data missing");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int displayPage(PageData& pageData)
|
int displayPage(PageData& pageData)
|
||||||
{
|
{
|
||||||
GwConfigHandler* config = commonData->config;
|
|
||||||
|
|
||||||
static RingBuffer<uint16_t>* wdHstry; // Wind direction data buffer
|
|
||||||
static RingBuffer<uint16_t>* wsHstry; // Wind speed data buffer
|
|
||||||
static String wdName, wdFormat; // Wind direction name and format
|
|
||||||
static String wsName, wsFormat; // Wind speed name and format
|
|
||||||
|
|
||||||
// Separate chart objects for true wind and apparent wind
|
|
||||||
static std::unique_ptr<Chart<uint16_t>> twdFlChart, awdFlChart; // chart object for wind direction chart, full size
|
|
||||||
static std::unique_ptr<Chart<uint16_t>> twsFlChart, awsFlChart; // chart object for wind speed chart, full size
|
|
||||||
static std::unique_ptr<Chart<uint16_t>> twdHfChart, awdHfChart; // chart object for wind direction chart, half size
|
|
||||||
static std::unique_ptr<Chart<uint16_t>> twsHfChart, awsHfChart; // chart object for wind speed chart, half size
|
|
||||||
// Pointers to the currently active charts
|
|
||||||
static Chart<uint16_t>* wdFlChart;
|
|
||||||
static Chart<uint16_t>* wsFlChart;
|
|
||||||
static Chart<uint16_t>* wdHfChart;
|
|
||||||
static Chart<uint16_t>* wsHfChart;
|
|
||||||
|
|
||||||
static GwApi::BoatValue* wdBVal = new GwApi::BoatValue("TWD"); // temp BoatValue for wind direction unit identification; required by OBP60Formater
|
|
||||||
static GwApi::BoatValue* wsBVal = new GwApi::BoatValue("TWS"); // temp BoatValue for wind speed unit identification; required by OBP60Formater */
|
|
||||||
double dfltRngWd = 60.0 * DEG_TO_RAD; // default range for course chart from min to max value in RAD
|
|
||||||
double dfltRngWs = 7.5; // default range for wind speed chart from min to max value in m/s
|
|
||||||
|
|
||||||
const int numBoatData = 4;
|
|
||||||
GwApi::BoatValue* bvalue[numBoatData]; // current boat data values
|
|
||||||
|
|
||||||
LOG_DEBUG(GwLog::LOG, "Display PageWindPlot");
|
LOG_DEBUG(GwLog::LOG, "Display PageWindPlot");
|
||||||
ulong pageTime = millis();
|
ulong pageTime = millis();
|
||||||
|
|
||||||
// read boat data values
|
|
||||||
for (int i = 0; i < numBoatData; i++) {
|
|
||||||
bvalue[i] = pageData.values[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optical warning by limit violation (unused)
|
|
||||||
if (String(flashLED) == "Limit Violation") {
|
|
||||||
setBlinkingLED(false);
|
|
||||||
setFlashLED(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showTruW != oldShowTruW) {
|
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);
|
|
||||||
|
|
||||||
twdFlChart = std::unique_ptr<Chart<uint16_t>>(new Chart<uint16_t>(*twdHstry, 1, 0, dfltRngWd, *commonData, useSimuData));
|
|
||||||
twsFlChart = std::unique_ptr<Chart<uint16_t>>(new Chart<uint16_t>(*twsHstry, 0, 0, dfltRngWs, *commonData, useSimuData));
|
|
||||||
twdHfChart = std::unique_ptr<Chart<uint16_t>>(new Chart<uint16_t>(*twdHstry, 1, 1, dfltRngWd, *commonData, useSimuData));
|
|
||||||
twsHfChart = std::unique_ptr<Chart<uint16_t>>(new Chart<uint16_t>(*twsHstry, 1, 2, dfltRngWs, *commonData, useSimuData));
|
|
||||||
// twdHfChart = std::unique_ptr<Chart<uint16_t>>(new Chart<uint16_t>(*twdHstry, 0, 1, dfltRngWd, *commonData, useSimuData));
|
|
||||||
// twsHfChart = std::unique_ptr<Chart<uint16_t>>(new Chart<uint16_t>(*twsHstry, 0, 2, dfltRngWs, *commonData, useSimuData));
|
|
||||||
// LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: twdHstry: %p, twsHstry: %p", (void*)twdHstry, (void*)twsHstry);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
awdFlChart = std::unique_ptr<Chart<uint16_t>>(new Chart<uint16_t>(*awdHstry, 1, 0, dfltRngWd, *commonData, useSimuData));
|
|
||||||
awsFlChart = std::unique_ptr<Chart<uint16_t>>(new Chart<uint16_t>(*awsHstry, 0, 0, dfltRngWs, *commonData, useSimuData));
|
|
||||||
awdHfChart = std::unique_ptr<Chart<uint16_t>>(new Chart<uint16_t>(*awdHstry, 1, 1, dfltRngWd, *commonData, useSimuData));
|
|
||||||
awsHfChart = std::unique_ptr<Chart<uint16_t>>(new Chart<uint16_t>(*awsHstry, 1, 2, dfltRngWs, *commonData, useSimuData));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Switch active charts based on showTruW
|
// Switch active charts based on showTruW
|
||||||
if (showTruW) {
|
if (showTruW) {
|
||||||
wdHstry = pageData.boatHstry->hstryBufList.twdHstry;
|
wdChart = twdChart.get();
|
||||||
wsHstry = pageData.boatHstry->hstryBufList.twsHstry;
|
wsChart = twsChart.get();
|
||||||
wdFlChart = twdFlChart.get();
|
wdBVal = pageData.values[0];
|
||||||
wsFlChart = twsFlChart.get();
|
wsBVal = pageData.values[1];
|
||||||
wdHfChart = twdHfChart.get();
|
|
||||||
wsHfChart = twsHfChart.get();
|
|
||||||
} else {
|
} else {
|
||||||
wdHstry = pageData.boatHstry->hstryBufList.awdHstry;
|
wdChart = awdChart.get();
|
||||||
wsHstry = pageData.boatHstry->hstryBufList.awsHstry;
|
wsChart = awsChart.get();
|
||||||
wdFlChart = awdFlChart.get();
|
wdBVal = pageData.values[2];
|
||||||
wsFlChart = awsFlChart.get();
|
wsBVal = pageData.values[3];
|
||||||
wdHfChart = awdHfChart.get();
|
|
||||||
wsHfChart = awsHfChart.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wdHstry->getMetaData(wdName, wdFormat);
|
|
||||||
wsHstry->getMetaData(wsName, wsFormat);
|
|
||||||
|
|
||||||
oldShowTruW = showTruW;
|
oldShowTruW = showTruW;
|
||||||
}
|
}
|
||||||
|
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: draw with data %s: %.2f, %s: %.2f", wdBVal->getName().c_str(), wdBVal->value, wsBVal->getName().c_str(), wsBVal->value);
|
||||||
|
|
||||||
// Draw page
|
// Draw page
|
||||||
//***********************************************************
|
//***********************************************************
|
||||||
@@ -219,28 +224,26 @@ public:
|
|||||||
getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
|
getdisplay().setPartialWindow(0, 0, width, height); // Set partial update
|
||||||
getdisplay().setTextColor(commonData->fgcolor);
|
getdisplay().setTextColor(commonData->fgcolor);
|
||||||
|
|
||||||
if (chrtMode == 'D') {
|
if (chrtMode == DIRECTION) {
|
||||||
wdBVal->value = wdHstry->getLast();
|
if (wdChart) {
|
||||||
wdBVal->valid = wdBVal->value != wdHstry->getMaxVal();
|
wdChart->showChrt(VERTICAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *wdBVal);
|
||||||
wdFlChart->showChrt(dataIntv, *bvalue[0]);
|
}
|
||||||
|
|
||||||
} else if (chrtMode == 'S') {
|
} else if (chrtMode == SPEED) {
|
||||||
wsBVal->value = wsHstry->getLast();
|
if (wsChart) {
|
||||||
wsBVal->valid = wsBVal->value != wsHstry->getMaxVal();
|
wsChart->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *wsBVal);
|
||||||
wsFlChart->showChrt(dataIntv, *bvalue[1]);
|
}
|
||||||
|
|
||||||
} else if (chrtMode == 'B') {
|
} else if (chrtMode == BOTH) {
|
||||||
wdBVal->value = wdHstry->getLast();
|
if (wdChart) {
|
||||||
wdBVal->valid = wdBVal->value != wdHstry->getMaxVal();
|
wdChart->showChrt(VERTICAL, HALF_SIZE_LEFT, dataIntv, PRNT_NAME, PRNT_VALUE, *wdBVal);
|
||||||
wsBVal->value = wsHstry->getLast();
|
}
|
||||||
wsBVal->valid = wsBVal->value != wsHstry->getMaxVal();
|
if (wsChart) {
|
||||||
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot showChrt: wsBVal.name: %s, format: %s, wsBVal.value: %.1f, valid: %d, address: %p", wsBVal->getName(), wsBVal->getFormat(), wsBVal->value,
|
wsChart->showChrt(VERTICAL, HALF_SIZE_RIGHT, dataIntv, PRNT_NAME, PRNT_VALUE, *wsBVal);
|
||||||
wsBVal->valid, wsBVal);
|
}
|
||||||
wdHfChart->showChrt(dataIntv, *bvalue[0]);
|
|
||||||
wsHfChart->showChrt(dataIntv, *bvalue[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DEBUG(GwLog::LOG, "PageWindPlot: page time %ldms", millis() - pageTime);
|
LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: page time %ldms", millis() - pageTime);
|
||||||
return PAGE_UPDATE;
|
return PAGE_UPDATE;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,19 +4,20 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "LedSpiTask.h"
|
#include "LedSpiTask.h"
|
||||||
#include "OBPDataOperations.h"
|
|
||||||
|
|
||||||
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data
|
#define MAX_PAGE_NUMBER 10 // Max number of pages for show data
|
||||||
|
|
||||||
typedef std::vector<GwApi::BoatValue *> ValueList;
|
typedef std::vector<GwApi::BoatValue *> ValueList;
|
||||||
|
|
||||||
|
class HstryBuffers;
|
||||||
|
|
||||||
typedef struct{
|
typedef struct{
|
||||||
GwApi *api;
|
GwApi *api;
|
||||||
String pageName;
|
String pageName;
|
||||||
uint8_t pageNumber; // page number in sequence of visible pages
|
uint8_t pageNumber; // page number in sequence of visible pages
|
||||||
//the values will always contain the user defined values first
|
//the values will always contain the user defined values first
|
||||||
ValueList values;
|
ValueList values;
|
||||||
HstryBuf* boatHstry;
|
HstryBuffers* hstryBuffers; // list of all boat history buffers
|
||||||
} PageData;
|
} PageData;
|
||||||
|
|
||||||
// Sensor data structure (only for extended sensors, not for NMEA bus sensors)
|
// Sensor data structure (only for extended sensors, not for NMEA bus sensors)
|
||||||
@@ -203,3 +204,8 @@ typedef struct{
|
|||||||
|
|
||||||
// Formatter for boat values
|
// Formatter for boat values
|
||||||
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata);
|
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata);
|
||||||
|
FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool ignoreSimuDataSetting);
|
||||||
|
|
||||||
|
// Helper method for conversion of any data value from SI to user defined format (defined in OBP60Formatter)
|
||||||
|
double convertValue(const double &value, const String &format, CommonData &commondata);
|
||||||
|
double convertValue(const double &value, const String &name, const String &format, CommonData &commondata);
|
||||||
|
|||||||
@@ -435,8 +435,8 @@ void OBP60Task(GwApi *api){
|
|||||||
int lastPage=-1; // initialize with an impiossible value, so we can detect wether we are during startup and no page has been displayed yet
|
int lastPage=-1; // initialize with an impiossible value, so we can detect wether we are during startup and no page has been displayed yet
|
||||||
|
|
||||||
BoatValueList boatValues; //all the boat values for the api query
|
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)
|
HstryBuffers hstryBufList(1920, &boatValues, logger); // Create empty list of boat data history buffers
|
||||||
WindUtils trueWind(&boatValues); // Create helper object for true wind calculation
|
WindUtils trueWind(&boatValues, logger); // Create helper object for true wind calculation
|
||||||
//commonData.distanceformat=config->getString(xxx);
|
//commonData.distanceformat=config->getString(xxx);
|
||||||
//add all necessary data to common data
|
//add all necessary data to common data
|
||||||
|
|
||||||
@@ -479,21 +479,27 @@ void OBP60Task(GwApi *api){
|
|||||||
LOG_DEBUG(GwLog::DEBUG,"added fixed value %s to page %d",value->getName().c_str(),i);
|
LOG_DEBUG(GwLog::DEBUG,"added fixed value %s to page %d",value->getName().c_str(),i);
|
||||||
pages[i].parameters.values.push_back(value);
|
pages[i].parameters.values.push_back(value);
|
||||||
}
|
}
|
||||||
// Add boat history data to page parameters
|
|
||||||
pages[i].parameters.boatHstry = &hstryBufList;
|
// Read the specified boat data type of relevant pages and create a history buffer for each type
|
||||||
|
if (pages[i].parameters.pageName == "OneValue" || pages[i].parameters.pageName == "TwoValues" || pages[i].parameters.pageName == "WindPlot") {
|
||||||
|
for (auto pVal : pages[i].parameters.values) {
|
||||||
|
hstryBufList.addBuffer(pVal->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add list of history buffers to page parameters
|
||||||
|
pages[i].parameters.hstryBuffers = &hstryBufList;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add out of band system page (always available)
|
// add out of band system page (always available)
|
||||||
Page *syspage = allPages.pages[0]->creator(commonData);
|
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
|
// Check user settings for true wind calculation
|
||||||
bool calcTrueWnds = api->getConfig()->getBool(api->getConfig()->calcTrueWnds, false);
|
bool calcTrueWnds = api->getConfig()->getBool(api->getConfig()->calcTrueWnds, false);
|
||||||
bool useSimuData = api->getConfig()->getBool(api->getConfig()->useSimuData, false);
|
bool useSimuData = api->getConfig()->getBool(api->getConfig()->useSimuData, false);
|
||||||
|
|
||||||
// Initialize history buffer for certain boat data
|
// Read all calibration data settings from config
|
||||||
hstryBufList.init(&boatValues, logger);
|
calibrationData.readConfig(config, logger);
|
||||||
|
|
||||||
// Display screenshot handler for HTTP request
|
// Display screenshot handler for HTTP request
|
||||||
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
// http://192.168.15.1/api/user/OBP60Task/screenshot
|
||||||
@@ -808,10 +814,10 @@ void OBP60Task(GwApi *api){
|
|||||||
api->getStatus(commonData.status);
|
api->getStatus(commonData.status);
|
||||||
|
|
||||||
if (calcTrueWnds) {
|
if (calcTrueWnds) {
|
||||||
trueWind.addTrueWind(api, &boatValues, logger);
|
trueWind.addWinds();
|
||||||
}
|
}
|
||||||
// Handle history buffers for certain boat data for windplot page and other usage
|
// Handle history buffers for certain boat data for windplot page and other usage
|
||||||
hstryBufList.handleHstryBuf(useSimuData);
|
hstryBufList.handleHstryBufs(useSimuData, commonData);
|
||||||
|
|
||||||
// Clear display
|
// Clear display
|
||||||
// getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
|
// getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);
|
||||||
|
|||||||
Reference in New Issue
Block a user