diff --git a/lib/obp60task/OBP60Formatter.cpp b/lib/obp60task/OBP60Formatter.cpp index 9eb3916..18cff2f 100644 --- a/lib/obp60task/OBP60Formatter.cpp +++ b/lib/obp60task/OBP60Formatter.cpp @@ -519,11 +519,11 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool } if (String(tempFormat) == "C") { temp = temp - 273.15; - result.unit = "C"; + result.unit = "Deg C"; } else if (String(tempFormat) == "F") { temp = (temp - 273.15) * 9 / 5 + 32; - result.unit = "F"; + result.unit = "Deg F"; } else{ result.unit = "K"; @@ -587,7 +587,11 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool rawvalue = 968 + float(random(0, 10)); pressure = rawvalue; } - snprintf(buffer, bsize, "%4.0f", pressure); + if (pressure < 999.5) { + snprintf(buffer, bsize, "!%3.0f", pressure); + } else { + snprintf(buffer, bsize, "%4.0f", pressure); + } result.unit = "hPa"; result.cvalue = pressure; } @@ -603,7 +607,11 @@ FormattedData formatValue(GwApi::BoatValue *value, CommonData &commondata, bool rawvalue = value->value; pressure = 968 + float(random(0, 10)); } - snprintf(buffer, bsize, "%4.0f", pressure); + if (pressure < 999.5) { + snprintf(buffer, bsize, "!%3.0f", pressure); + } else { + snprintf(buffer, bsize, "%4.0f", pressure); + } result.unit = "mBar"; result.cvalue = pressure; } diff --git a/lib/obp60task/OBPDataOperations.cpp b/lib/obp60task/OBPDataOperations.cpp index 1ed7662..1d84a70 100644 --- a/lib/obp60task/OBPDataOperations.cpp +++ b/lib/obp60task/OBPDataOperations.cpp @@ -223,13 +223,13 @@ HstryBuf::HstryBuf(const String& name, int size, BoatValueList* boatValues, GwLo boatValue = boatValues->findValueOrCreate(name); } -void HstryBuf::init(const String& format, int updFreq, int mltplr, double minVal, double maxVal) +void HstryBuf::init(const String& format, int updFreq, double mltplr, double minVal, double maxVal) { hstryBuf.setMetaData(boatDataName, format, updFreq, mltplr, minVal, maxVal); hstryMin = minVal; hstryMax = maxVal; + bufUpdateTime = 0; if (!boatValue->valid) { - boatValue->setFormat(format); boatValue->value = std::numeric_limits::max(); // mark current value invalid } } @@ -238,25 +238,32 @@ 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); + // LOG_DEBUG(GwLog::DEBUG, "HstryBuf::add: name: %s, value: %.3f, value buffer: %.3f", hstryBuf.getName(), value, hstryBuf.getLast()); } } void HstryBuf::handle(bool useSimuData, CommonData& common) { - std::unique_ptr tmpBVal; // Temp variable to get formatted and converted data value from OBP60Formatter + if ((millis() - bufUpdateTime) >= hstryBuf.getUpdFreq()) { - if (boatValue->valid) { - add(boatValue->value); - } else if (useSimuData) { // add simulated value to history buffer - tmpBVal = std::unique_ptr(new GwApi::BoatValue(boatDataName)); // create temporary boat value for retrieval of simulation value - tmpBVal->setFormat(boatValue->getFormat()); - tmpBVal->value = boatValue->value; - tmpBVal->valid = boatValue->valid; - double simSIValue = formatValue(tmpBVal.get(), common).value; // simulated value is generated at ; here: retreive SI value - add(simSIValue); - } else { - // here we will add invalid (DBL_MAX) value; this will mark periods of missing data in buffer together with a timestamp + bufUpdateTime = millis(); + // LOG_DEBUG(GwLog::DEBUG, "HstryBuf::handle: name: %s, frequency: %d, format: %s, value: %.3f", hstryBuf.getName(), hstryBuf.getUpdFreq(), + // boatValue->getFormat().c_str(), boatValue->value); + + if (boatValue->valid) { + add(boatValue->value); + + } else if (useSimuData) { // add simulated value to history buffer + std::unique_ptr tmpBVal; // Temp variable to get formatted and converted data value from OBP60Formatter + tmpBVal = std::unique_ptr(new GwApi::BoatValue(boatDataName)); // create temporary boat value for retrieval of simulation value + tmpBVal->setFormat(boatValue->getFormat()); + tmpBVal->value = boatValue->value; + tmpBVal->valid = boatValue->valid; + double simSIValue = formatValue(tmpBVal.get(), common).value; // simulated value is generated at ; here: retreive SI value + add(simSIValue); + } else { + // here we will add invalid (DBL_MAX) value; this will mark periods of missing data in buffer together with a timestamp + } } } // --- End Class HstryBuf --------------- @@ -273,21 +280,16 @@ void HstryBuffers::addBuffer(const String& name) if (HstryBuffers::getBuffer(name) != nullptr) { // buffer for this data type already exists return; } - if (bufferParams.find(name) == bufferParams.end()) { // requested boat data type is not supported in list of + + auto it = bufferParams.find(name); + if (it == bufferParams.end() && !name.startsWith("xdr")) { // requested boat data type is not supported in list of + // we take any "XDR" type, though return; } - // Initialize metadata for buffer - String valueFormat = bufferParams[name].format; // Data format of boat data type - // String valueFormat = boatValueList->findValueOrCreate(name)->getFormat().c_str(); // Unfortunately, format is not yet available during system initialization - int hstryUpdFreq = bufferParams[name].hstryUpdFreq; // Update frequency for history buffers in ms - int mltplr = bufferParams[name].mltplr; // default multiplier which transforms original 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 - + // create buffer only; initialization with metadata can only be done later after boat data have been updated first time hstryBuffers[name] = std::unique_ptr(new HstryBuf(name, size, boatValueList, logger)); - hstryBuffers[name]->init(valueFormat, hstryUpdFreq, mltplr, bufferMinVal, bufferMaxVal); - 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); + LOG_DEBUG(GwLog::DEBUG, "HstryBuffers: new buffer added: name: %s", name); } // Handle all registered history buffers @@ -295,6 +297,35 @@ void HstryBuffers::handleHstryBufs(bool useSimuData, CommonData& common) { for (auto& bufMap : hstryBuffers) { auto& buf = bufMap.second; + + if (!buf->hasMetaData()) { // meta data initialization for buffer has not been done before + + String valueFormat = boatValueList->findValueOrCreate(buf->boatDataName)->getFormat().c_str(); + LOG_DEBUG(GwLog::DEBUG, "HstryBuffers: value name: %s, format: %s", boatValueList->findValueOrCreate(buf->boatDataName)->getName().c_str(), valueFormat); + + if (!valueFormat.isEmpty()) { + String lookupKey = buf->boatDataName; + if (buf->boatDataName.startsWith("xdr")) { // current boat data is of type "XDR" + lookupKey = valueFormat; + } + auto it = bufferParams.find(lookupKey); + if (it == bufferParams.end()) { // requested boat data type is not supported in list of + return; + } + HistoryParams params = it->second; // this is the metadata for the new buffer + + int hstryUpdFreq = params.hstryUpdFreq; // Update frequency for history buffers in ms + double mltplr = params.mltplr; // default multiplier which transforms original value into buffer type format + double bufferMinVal = params.bufferMinVal; // Min value for this history buffer + double bufferMaxVal = params.bufferMaxVal; // Max value for this history buffer + + hstryBuffers[buf->boatDataName]->init(valueFormat, hstryUpdFreq, mltplr, bufferMinVal, bufferMaxVal); + buf->metaDataDefined = true; + LOG_DEBUG(GwLog::DEBUG, "HstryBuffers::handleBufs: metadata added: name: %s, format: %s, frequency: %d, multiplier: %f, min value: %.2f, max value: %.2f", buf->boatDataName, valueFormat, hstryUpdFreq, + mltplr, bufferMinVal, bufferMaxVal); + } + } + buf->handle(useSimuData, common); } } @@ -461,10 +492,24 @@ bool WindUtils::calcTrueWinds(const double* awaVal, const double* awsVal, const return true; } +// Set max wind speed +void WindUtils::setMaxWs(GwApi::BoatValue* wsMaxValue, const double* wsVal) +{ + static double maxWs = 0.0; // maintain own maxTWS value in obp user task; core gateway would reset MaxTWS obp boat value if true wind data is not available + + if (*wsVal != DBL_MAX && *wsVal > maxWs) { + maxWs = *wsVal; + } + + if ( maxWs > 0.0 && maxWs >= wsMaxValue->value) { + wsMaxValue->value = maxWs; // overwrite core gateway value each second again with own user task value if that value is larger + wsMaxValue->valid = true; + } +}; + // Calculate true wind data and add to obp60task boat data list bool WindUtils::handleWinds(bool calcWinds) { - double twd, tws, twa, awd; bool twCalculated = false; double awaVal = awaBVal->valid ? awaBVal->value : DBL_MAX; @@ -477,8 +522,6 @@ bool WindUtils::handleWinds(bool calcWinds) double varVal = varBVal->valid ? varBVal->value : DBL_MAX; double twaVal = twaBVal->valid ? twaBVal->value : DBL_MAX; double twsVal = twsBVal->valid ? twsBVal->value : DBL_MAX; - // LOG_DEBUG(GwLog::DEBUG, "WindUtils:handleWinds: AWA %.1f, AWS %.1f, AWD %.1f, COG %.1f, STW %.1f, SOG %.2f, HDT %.1f, HDM %.1f, VAR %.1f", awaVal * RAD_TO_DEG, awsVal * 3.6 / 1.852, - // awd * RAD_TO_DEG, cogVal * RAD_TO_DEG, stwVal * 3.6 / 1.852, sogVal * 3.6 / 1.852, hdtVal * RAD_TO_DEG, hdmVal * RAD_TO_DEG, varVal * RAD_TO_DEG); if (calcHDT(&hdmVal, &varVal, &cogVal, &sogVal, &hdtVal)) { hdtBVal->value = hdtVal; diff --git a/lib/obp60task/OBPDataOperations.h b/lib/obp60task/OBPDataOperations.h index 5631483..558a03b 100644 --- a/lib/obp60task/OBPDataOperations.h +++ b/lib/obp60task/OBPDataOperations.h @@ -39,6 +39,8 @@ private: String boatDataName; double hstryMin; double hstryMax; + bool metaDataDefined = false; + unsigned long bufUpdateTime; GwApi::BoatValue* boatValue; GwLog* logger; @@ -46,7 +48,8 @@ private: public: HstryBuf(const String& name, int size, BoatValueList* boatValues, GwLog* log); - void init(const String& format, int updFreq, int mltplr, double minVal, double maxVal); + bool hasMetaData() const { return metaDataDefined; }; + void init(const String& format, int updFreq, double mltplr, double minVal, double maxVal); void add(double value); void handle(bool useSimuData, CommonData& common); }; @@ -61,31 +64,39 @@ private: 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 mltplr; // specifies actual value precision being storable: + // [10000: 0 - 6.5535 | 1000: 0 - 65.535 | 100: 0 - 655.35 | 10: 0 - 6553.5 | 1: 0 - 65535 | 0.1: 0 - 655350] double bufferMinVal; // minimum valid data value double bufferMaxVal; // maximum valid data value - String format; // format of data type }; - // Define buffer parameters for supported boat data type std::map bufferParams = { - { "AWA", { 1000, 10000, 0.0, M_TWOPI, "formatWind" } }, - { "AWD", { 1000, 10000, 0.0, M_TWOPI, "formatCourse" } }, - { "AWS", { 1000, 1000, 0.0, 65.0, "formatKnots" } }, - { "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 + { "AWA", { 1000, 10000, 0.0, M_TWOPI } }, + { "AWD", { 1000, 10000, 0.0, M_TWOPI } }, + { "AWS", { 1000, 1000, 0.0, 65.0 } }, + { "COG", { 1000, 10000, 0.0, M_TWOPI } }, + { "DBK", { 1000, 100, 0.0, 650.0 } }, + { "DBS", { 1000, 100, 0.0, 650.0 } }, + { "DBT", { 1000, 100, 0.0, 650.0 } }, + { "DPT", { 1000, 100, 0.0, 650.0 } }, + { "HDM", { 1000, 10000, 0.0, M_TWOPI } }, + { "HDT", { 1000, 10000, 0.0, M_TWOPI } }, + { "ROT", { 1000, 10000, -M_PI / 180.0 * 99.0, M_PI / 180.0 * 99.0 } }, // min/max is -/+ 99 degrees for "rate of turn" + { "SOG", { 1000, 1000, 0.0, 65.0 } }, + { "STW", { 1000, 1000, 0.0, 65.0 } }, + { "TWA", { 1000, 10000, 0.0, M_TWOPI } }, + { "TWD", { 1000, 10000, 0.0, M_TWOPI } }, + { "TWS", { 1000, 1000, 0.0, 65.0 } }, + { "WTemp", { 1000, 100, 263.15, 403.15 } }, // water temp [-10..130] °C + { "formatXdr:C:K", { 1000, 100, 223.15, 423.15 } }, // temperature [-50..150] deg celsius + { "formatXdr:P:B", { 60000, 1000, 0, 65.0 } }, // pressure [0..65] bar + { "formatXdr:P:P", { 60000, 0.1, 0, 650000 } }, // pressure [0..6500] hPa + { "formatXdr:H:P", { 1000, 100, 0, 100 } }, // humidity [0..100] percent + { "formatXdr:I:A", { 1000, 100, 0, 650.0 } }, // current [0..650] amperes + { "formatXdr:U:V", { 1000, 1000, 0, 65.0 } }, // voltage [0..65] volts + { "formatXdr:T:R", { 1000, 1, 0, 30000 } }, // tachometer [0..30000] rpm + { "formatXdr:V:L", { 10000, 100, 0, 650 } }, // volume [0..650] litres + { "formatXdr:V:M", { 10000, 10000, 0, 6.50 } }, // volume [0..6.5] m^3 }; public: @@ -97,9 +108,10 @@ public: class WindUtils { private: - GwApi::BoatValue *twaBVal, *twsBVal, *twdBVal; + GwApi::BoatValue *twaBVal, *twsBVal, *twdBVal, *maxtwsBVal; GwApi::BoatValue *awaBVal, *awsBVal, *awdBVal; GwApi::BoatValue *cogBVal, *stwBVal, *sogBVal, *hdtBVal, *hdmBVal, *varBVal; + double twd, tws, twa, awd; static constexpr double DBL_MAX = std::numeric_limits::max(); GwLog* logger; @@ -109,6 +121,7 @@ public: { twaBVal = boatValues->findValueOrCreate("TWA"); twsBVal = boatValues->findValueOrCreate("TWS"); + maxtwsBVal = boatValues->findValueOrCreate("MaxTws"); twdBVal = boatValues->findValueOrCreate("TWD"); awaBVal = boatValues->findValueOrCreate("AWA"); awsBVal = boatValues->findValueOrCreate("AWS"); @@ -137,5 +150,7 @@ public: bool calcTrueWinds(const double* awaVal, const double* awsVal, const double* awd, const double* cogVal, const double* stwVal, const double* sogVal, const double* hdtVal, double* twdVal, double* twsVal, double* twaVal); + void setMaxWs(GwApi::BoatValue *wsMaxValue, const double * wsVal); + void setMaxWs() { setMaxWs(maxtwsBVal, &tws); }; bool handleWinds(bool calcWinds); }; \ No newline at end of file diff --git a/lib/obp60task/OBPRingBuffer.h b/lib/obp60task/OBPRingBuffer.h index 970245e..332dddc 100644 --- a/lib/obp60task/OBPRingBuffer.h +++ b/lib/obp60task/OBPRingBuffer.h @@ -45,9 +45,9 @@ private: size_t last; // Points to the last (newest) valid element size_t count; // Number of valid elements currently in buffer bool is_Full; // Indicates that all buffer elements are used and ringing is in use - T MIN_VAL; // lowest possible value of buffer of type - T MAX_VAL; // highest possible value of buffer of type -> indicates invalid value in buffer - double dblMIN_VAL, dblMAX_VAL; // MIN_VAL, MAX_VAL in double format + T NUMLIMIT_LOW; // internally lowest possible value of buffer of type + T NUMLIMIT_HIGH; // internally highest possible value of buffer of type + double BUFMIN_VAL, BUFMAX_VAL; // lowest/highest possible buffer value considering multiplier -> externally used mutable SemaphoreHandle_t bufLocker; // metadata for buffer @@ -55,8 +55,8 @@ private: String dataFmt; // Format of boat data in buffer int updFreq; // Update frequency in milliseconds double mltplr; // Multiplier which transforms original value into buffer type format - double smallest; // Value range of buffer: smallest value; needs to be => MIN_VAL - double largest; // Value range of buffer: biggest value; needs to be < MAX_VAL, since MAX_VAL indicates invalid entries + double lowest; // low value range for boat data in this buffer; needs to be => BUFMIN_VAL + double highest; // high value range for boat data in this buffer; needs to be < BUFMAX_VAL, since BUFMAX_VAL indicates invalid entries void initCommon(); @@ -64,10 +64,12 @@ public: RingBuffer(); RingBuffer(size_t size); void setMetaData(String name, String format, int updateFrequency, double multiplier, double minValue, double maxValue); // Set meta data for buffer + void setFormat(String format); // Specify format of buffer bool getMetaData(String& name, String& format, int& updateFrequency, double& multiplier, double& minValue, double& maxValue); // Get meta data of buffer bool getMetaData(String& name, String& format); String getName() const; // Get buffer name String getFormat() const; // Get buffer data format + int getUpdFreq() const; // Get buffer update frequency void add(const double& value); // Add a new value to buffer double get(size_t index) const; // Get value at specific position (0-based index from oldest to newest) double getFirst() const; // Get the first (oldest) value in buffer diff --git a/lib/obp60task/OBPRingBuffer.tpp b/lib/obp60task/OBPRingBuffer.tpp index 7d73f46..1b88498 100644 --- a/lib/obp60task/OBPRingBuffer.tpp +++ b/lib/obp60task/OBPRingBuffer.tpp @@ -6,16 +6,16 @@ template void RingBuffer::initCommon() { - MIN_VAL = std::numeric_limits::lowest(); - MAX_VAL = std::numeric_limits::max(); - dblMIN_VAL = static_cast(MIN_VAL); - dblMAX_VAL = static_cast(MAX_VAL); + NUMLIMIT_LOW = std::numeric_limits::lowest(); + NUMLIMIT_HIGH = std::numeric_limits::max(); dataName = ""; dataFmt = ""; updFreq = -1; mltplr = 1; - smallest = dblMIN_VAL; - largest = dblMAX_VAL; + BUFMIN_VAL = static_cast(NUMLIMIT_LOW); + BUFMAX_VAL = static_cast(NUMLIMIT_HIGH); + lowest = BUFMIN_VAL; + highest = BUFMAX_VAL; bufLocker = xSemaphoreCreateMutex(); } @@ -44,7 +44,7 @@ RingBuffer::RingBuffer(size_t size) initCommon(); buffer.reserve(size); - buffer.resize(size, MAX_VAL); // MAX_VAL indicate invalid values + buffer.resize(size, NUMLIMIT_HIGH); // NUMLIMIT_HIGH indicate invalid values } // Specify meta data of buffer content @@ -56,8 +56,18 @@ void RingBuffer::setMetaData(String name, String format, int updateFrequency, dataFmt = format; updFreq = updateFrequency; mltplr = multiplier; - smallest = std::max(dblMIN_VAL, minValue); - largest = std::min(dblMAX_VAL, maxValue); + BUFMIN_VAL = static_cast(NUMLIMIT_LOW) / mltplr; // lowest possible buffer value; converted to external view + BUFMAX_VAL = static_cast(NUMLIMIT_HIGH) / mltplr; // highest possible buffer value; converted to external view + lowest = std::max(BUFMIN_VAL, minValue); // low value range, set by user + highest = std::min(std::nextafter(BUFMAX_VAL, -std::numeric_limits::infinity()), maxValue); // high value range, set by user; max. is 1 tick smaller than BUFMAX_VAL +} + +// Specify format of buffer content +template +void RingBuffer::setFormat(String format) +{ + GWSYNCHRONIZED(&bufLocker); + dataFmt = format; } // Get meta data of buffer content @@ -73,8 +83,8 @@ bool RingBuffer::getMetaData(String& name, String& format, int& updateFrequen format = dataFmt; updateFrequency = updFreq; multiplier = mltplr; - minValue = smallest; - maxValue = largest; + minValue = lowest; + maxValue = highest; return true; } @@ -106,13 +116,20 @@ String RingBuffer::getFormat() const return dataFmt; } +// Get buffer update frequency +template +int RingBuffer::getUpdFreq() const +{ + return updFreq; +} + // Add a new value to buffer template void RingBuffer::add(const double& value) { GWSYNCHRONIZED(&bufLocker); - if (value < smallest || value > largest) { - buffer[head] = MAX_VAL; // Store MAX_VAL if value is out of range + if (value < lowest || value > highest) { + buffer[head] = NUMLIMIT_HIGH; // Store maximum buffer value if data value is out of range } else { buffer[head] = static_cast(std::round(value * mltplr)); } @@ -136,11 +153,11 @@ double RingBuffer::get(size_t index) const { GWSYNCHRONIZED(&bufLocker); if (isEmpty() || index < 0 || index >= count) { - return dblMAX_VAL; + return BUFMAX_VAL; } size_t realIndex = (first + index) % capacity; - return static_cast(buffer[realIndex] / mltplr); + return static_cast(buffer[realIndex] / mltplr); // is BUFMAX_VAL if value is invalid } // Operator[] for convenient access (same as get()) @@ -155,7 +172,7 @@ template double RingBuffer::getFirst() const { if (isEmpty()) { - return dblMAX_VAL; + return BUFMAX_VAL; } return get(0); } @@ -165,7 +182,7 @@ template double RingBuffer::getLast() const { if (isEmpty()) { - return dblMAX_VAL; + return BUFMAX_VAL; } return get(count - 1); } @@ -174,19 +191,7 @@ double RingBuffer::getLast() const template double RingBuffer::getMin() const { - if (isEmpty()) { - return dblMAX_VAL; - } - - double minVal = dblMAX_VAL; - double value; - for (size_t i = 0; i < count; i++) { - value = get(i); - if (value < minVal && value != dblMAX_VAL) { - minVal = value; - } - } - return minVal; + return getMin(getCurrentSize()); } // Get minimum value of the last values of buffer @@ -194,16 +199,16 @@ template double RingBuffer::getMin(size_t amount) const { if (isEmpty() || amount <= 0) { - return dblMAX_VAL; + return BUFMAX_VAL; } if (amount > count) amount = count; - double minVal = dblMAX_VAL; + double minVal = BUFMAX_VAL; double value; for (size_t i = 0; i < amount; i++) { value = get(count - 1 - i); - if (value < minVal && value != dblMAX_VAL) { + if (value < minVal && value != BUFMAX_VAL) { minVal = value; } } @@ -214,22 +219,7 @@ double RingBuffer::getMin(size_t amount) const template double RingBuffer::getMax() const { - if (isEmpty()) { - return dblMAX_VAL; - } - - double maxVal = dblMIN_VAL; - double value; - for (size_t i = 0; i < count; i++) { - value = get(i); - if (value > maxVal && value != dblMAX_VAL) { - maxVal = value; - } - } - if (maxVal == dblMIN_VAL) { // no change of initial value -> buffer has only invalid values (MAX_VAL) - maxVal = dblMAX_VAL; - } - return maxVal; + return getMax(getCurrentSize()); } // Get maximum value of the last values of buffer @@ -237,21 +227,21 @@ template double RingBuffer::getMax(size_t amount) const { if (isEmpty() || amount <= 0) { - return dblMAX_VAL; + return BUFMAX_VAL; } if (amount > count) amount = count; - double maxVal = dblMIN_VAL; + double maxVal = BUFMIN_VAL; double value; for (size_t i = 0; i < amount; i++) { value = get(count - 1 - i); - if (value > maxVal && value != dblMAX_VAL) { + if (value > maxVal && value != BUFMAX_VAL) { maxVal = value; } } - if (maxVal == dblMIN_VAL) { // no change of initial value -> buffer has only invalid values (MAX_VAL) - maxVal = dblMAX_VAL; + if (maxVal == BUFMIN_VAL) { // no change of initial value -> buffer has only invalid values (BUFMAX_VAL) + maxVal = BUFMAX_VAL; } return maxVal; } @@ -260,11 +250,7 @@ double RingBuffer::getMax(size_t amount) const template double RingBuffer::getMid() const { - if (isEmpty()) { - return dblMAX_VAL; - } - - return (getMin() + getMax()) / 2; + return getMid(getCurrentSize()); } // Get mid value between and value of the last values of buffer @@ -272,7 +258,7 @@ template double RingBuffer::getMid(size_t amount) const { if (isEmpty() || amount <= 0) { - return dblMAX_VAL; + return BUFMAX_VAL; } if (amount > count) @@ -285,29 +271,7 @@ double RingBuffer::getMid(size_t amount) const template double RingBuffer::getMedian() const { - if (isEmpty()) { - return dblMAX_VAL; - } - - // Create a temporary vector with current valid elements - std::vector temp; - temp.reserve(count); - - for (size_t i = 0; i < count; i++) { - temp.push_back(get(i)); - } - - // Sort to find median - std::sort(temp.begin(), temp.end()); - - if (count % 2 == 1) { - // Odd number of elements - return static_cast(temp[count / 2]); - } else { - // Even number of elements - return average of middle two - // Note: For integer types, this truncates. For floating point, it's exact. - return static_cast((temp[count / 2 - 1] + temp[count / 2]) / 2); - } + return getMedian(getCurrentSize()); } // Get the median value of the last values of buffer @@ -315,7 +279,7 @@ template double RingBuffer::getMedian(size_t amount) const { if (isEmpty() || amount <= 0) { - return dblMAX_VAL; + return BUFMAX_VAL; } if (amount > count) amount = count; @@ -387,14 +351,14 @@ bool RingBuffer::isFull() const template double RingBuffer::getMinVal() const { - return dblMIN_VAL; + return BUFMIN_VAL; } // Get highest possible value for buffer; used for unset/invalid buffer data template double RingBuffer::getMaxVal() const { - return dblMAX_VAL; + return BUFMAX_VAL; } // Clear buffer @@ -423,21 +387,14 @@ void RingBuffer::resize(size_t newSize) buffer.clear(); buffer.reserve(newSize); - buffer.resize(newSize, MAX_VAL); + buffer.resize(newSize, NUMLIMIT_HIGH); } // Get all current values in native buffer format as a vector template std::vector RingBuffer::getAllValues() const { - std::vector result; - result.reserve(count); - - for (size_t i = 0; i < count; i++) { - result.push_back(get(i)); - } - - return result; + return getAllValues(getCurrentSize()); } // Get last values in native buffer format as a vector diff --git a/lib/obp60task/OBPcharts.cpp b/lib/obp60task/OBPcharts.cpp index fbd7fd0..81b5749 100644 --- a/lib/obp60task/OBPcharts.cpp +++ b/lib/obp60task/OBPcharts.cpp @@ -3,16 +3,6 @@ #include "OBPDataOperations.h" #include "OBPRingBuffer.h" -// Default ranges for various boat data types: 1st value default range, 2nd value step for range adjustment -// should be multiple of 4 for full integer chart labels w/o decimals -std::map Chart::dfltChrtDta = { - { "formatWind", { 60.0 * DEG_TO_RAD, 10.0 * DEG_TO_RAD } }, // default wind range 60 degrees - { "formatCourse", { 60.0 * DEG_TO_RAD, 10.0 * DEG_TO_RAD } }, // default course range 60 degrees - { "formatKnots", { 2.572, 2.572 } }, // default speed range in m/s - { "formatDepth", { 10.0, 5.0 } }, // default depth range in m - { "kelvinToC", { 20.0, 5.0 } } // default temp range in °C/K -}; - // --- Class Chart --------------- // Chart - object holding the actual chart, incl. data buffer and format definition @@ -20,9 +10,8 @@ std::map Chart::dfltChrtDta = { // default range of chart, e.g. 30 = [0..30] // common program data; required for logger and color data // flag to indicate if simulation data is active -Chart::Chart(RingBuffer& dataBuf, double dfltRng, CommonData& common, bool useSimuData) +Chart::Chart(RingBuffer& dataBuf, CommonData& common, bool useSimuData) : dataBuf(dataBuf) - , dfltRng(dfltRng) , commonData(&common) , useSimuData(useSimuData) { @@ -30,41 +19,60 @@ Chart::Chart(RingBuffer& dataBuf, double dfltRng, CommonData& common, fgColor = commonData->fgcolor; bgColor = commonData->bgcolor; - // display dimensions (avoid calling width()/height() on incomplete LGFX type) - #ifdef TFT_DISPLAY - dWidth = 480; - dHeight = 320; - #else - dWidth = getdisplay().width(); - dHeight = getdisplay().height(); - #endif +// display dimensions (avoid calling width()/height() on incomplete LGFX type) +#ifdef TFT_DISPLAY + dWidth = 480; + dHeight = 320; +#else + dWidth = getdisplay().width(); + dHeight = getdisplay().height(); +#endif + + smoothCharts = commonData->config->getBool(commonData->config->smoothCharts); + if (smoothCharts) { + chrtAvg.begin(); + } + + init(); +}; + +Chart::~Chart() +{ +} + +bool Chart::init() +{ + chrtMin = 0.0; + chrtMax = 0.0; + chrtMid = 0.0; + chrtRng = 0.0; dataBuf.getMetaData(dbName, dbFormat); dbMIN_VAL = dataBuf.getMinVal(); dbMAX_VAL = dataBuf.getMaxVal(); bufSize = dataBuf.getCapacity(); - - smoothCharts = common.config->getBool(common.config->smoothCharts); - if (smoothCharts) { - chrtAvg.begin(); - } + LOG_DEBUG(GwLog::DEBUG, "Chart Init: dbMIN_VAL: %.2f, dbMAX_VAL: %.2fd, bufSize: %d", dbMIN_VAL, dbMAX_VAL, bufSize); // Initialize chart data format; shorter version of standard format indicator - if (dbFormat == "formatCourse" || dbFormat == "formatWind" || dbFormat == "formatRot") { + if (dbFormat == "formatCourse" || dbFormat == "formatWind") { chrtDataFmt = WIND; // Chart is showing data of course / wind format } else if (dbFormat == "formatRot") { chrtDataFmt = ROTATION; // Chart is showing data of rotational format } else if (dbFormat == "formatKnots") { chrtDataFmt = SPEED; // Chart is showing data of speed or windspeed format } else if (dbFormat == "formatDepth") { - chrtDataFmt = DEPTH; // Chart ist showing data of format + chrtDataFmt = DEPTH; } else if (dbFormat == "kelvinToC") { - chrtDataFmt = TEMPERATURE; // Chart ist showing data of format + chrtDataFmt = TEMPERATURE; + } else if (dbFormat.startsWith("formatXdr:P")) { + chrtDataFmt = PRESSURE; + } else if (dbFormat.startsWith("formatXdr:H")) { + chrtDataFmt = HUMIDITY; } else { chrtDataFmt = OTHER; // Chart is showing any other data format } - // "0" value is the same for any data format but for user defined temperature format + // "0" value is the same for any data format but for user defined temperature and pressure format zeroValue = 0.0; if (chrtDataFmt == TEMPERATURE) { tempFormat = commonData->config->getString(commonData->config->tempFormat); // [K|°C|°F] @@ -75,6 +83,8 @@ Chart::Chart(RingBuffer& dataBuf, double dfltRng, CommonData& common, } else if (tempFormat == "F") { zeroValue = 255.37; } + } else if (chrtDataFmt == PRESSURE) { + zeroValue = 98000.0; // typical low pressure area value } // Read default range and range step for this chart type @@ -91,14 +101,18 @@ Chart::Chart(RingBuffer& dataBuf, double dfltRng, CommonData& common, chrtMax = chrtMin + dfltRng; chrtMid = (chrtMin + chrtMax) / 2; chrtRng = dfltRng; - recalcRngMid = true; // initialize and chart borders on first screen call + recalcRngMid = true; // initialize and chart borders on first chart display call - LOG_DEBUG(GwLog::DEBUG, "Chart Init: dWidth: %d, dHeight: %d, timAxis: %d, valAxis: %d, cRoot {x,y}: %d, %d, dbname: %s, rngStep: %.4f, chrtDataFmt: %d", - dWidth, dHeight, timAxis, valAxis, cRoot.x, cRoot.y, dbName, rngStep, chrtDataFmt); -}; + if (dbFormat.isEmpty()) { + // data buffer may not exist yet, because boat data object is not available yet + initValid = false; // chart object will get invalid data during initialization + } else { + initValid = true; + } + LOG_DEBUG(GwLog::DEBUG, "Chart Init: dWidth: %d, dHeight: %d, timAxis: %d, valAxis: %d, cRoot {x,y}: %d, %d, dbname: %s, rngStep: %.4f, chrtDataFmt: %d, initValid: %d", + dWidth, dHeight, timAxis, valAxis, cRoot.x, cRoot.y, dbName, rngStep, chrtDataFmt, initValid); -Chart::~Chart() -{ + return isValid(); } // Perform all actions to draw chart @@ -108,7 +122,8 @@ Chart::~Chart() // ; print data name on horizontal half chart [true|false] // : print current boat data value [true|false] // : current boat data value; used only for test on valid data -void Chart::showChrt(char chrtDir, int8_t chrtSz, const int8_t chrtIntv, bool prntName, bool showCurrValue, GwApi::BoatValue currValue) +// void Chart::showChrt(ChrtDirection chrtDire, ChrtSize chrtSze, const int8_t chrtIntv, bool prntName, bool showCurrValue, GwApi::BoatValue currValue) +void Chart::showChrt(const char chrtDir, const int8_t chrtSz, const int8_t chrtIntv, bool prntName, bool showCurrValue, GwApi::BoatValue currValue) { if (!setChartDimensions(chrtDir, chrtSz)) { return; // wrong chart dimension parameters @@ -123,17 +138,18 @@ void Chart::showChrt(char chrtDir, int8_t chrtSz, const int8_t chrtIntv, bool pr return; } - if (showCurrValue) { // show latest value from history buffer; this should be the most current one - currValue.value = dataBuf.getLast(); + if (showCurrValue) { + currValue.value = dataBuf.getLast(); // show latest value from history buffer; this should be the most current one currValue.valid = currValue.value != dbMAX_VAL; prntCurrValue(chrtDir, currValue); } } // define dimensions and start points for chart +// bool Chart::setChartDimensions(const ChrtDirection direction, const ChrtSize size) bool Chart::setChartDimensions(const char direction, const int8_t size) { - if ((direction != HORIZONTAL && direction != VERTICAL) || (size < 0 || size > 2)) { + if ((direction != HORIZONTAL && direction != VERTICAL) || (size < 0 || size > 3)) { LOG_DEBUG(GwLog::ERROR, "obp60:setChartDimensions %s: wrong parameters", dataBuf.getName()); return false; } @@ -154,6 +170,13 @@ bool Chart::setChartDimensions(const char direction, const int8_t size) valAxis = (dHeight - top - bottom) / 2 - hGap; cRoot = { 0, top + (valAxis + hGap) + hGap - 1 }; break; + case 3: + valAxis = (dHeight - top - bottom) * 0.667 - hGap; + cRoot = { 0, top - 1 }; + break; + default: // same as case 0; should never happen + valAxis = dHeight - top - bottom; + cRoot = { 0, top - 1 }; } } else if (direction == VERTICAL) { @@ -172,10 +195,13 @@ bool Chart::setChartDimensions(const char direction, const int8_t size) valAxis = dWidth / 2 - vGap; cRoot = { dWidth / 2 + vGap - 1, top - 1 }; break; + default: // same as case 0; should never happen + valAxis = dWidth - 1; + cRoot = { 0, top - 1 }; } } - //LOG_DEBUG(GwLog::DEBUG, "obp60:setChartDimensions %s: direction: %c, size: %d, dWidth: %d, dHeight: %d, timAxis: %d, valAxis: %d, cRoot{%d, %d}, top: %d, bottom: %d, hGap: %d, vGap: %d", - // dataBuf.getName(), direction, size, dWidth, dHeight, timAxis, valAxis, cRoot.x, cRoot.y, top, bottom, hGap, vGap); + // LOG_DEBUG(GwLog::DEBUG, "obp60:setChartDimensions %s: direction: %c, size: %d, dWidth: %d, dHeight: %d, timAxis: %d, valAxis: %d, cRoot{%d, %d}, top: %d, bottom: %d, hGap: %d, vGap: %d", + // dataBuf.getName(), direction, size, dWidth, dHeight, timAxis, valAxis, cRoot.x, cRoot.y, top, bottom, hGap, vGap); return true; } @@ -186,10 +212,10 @@ void Chart::drawChrt(const char chrtDir, const int8_t chrtIntv, GwApi::BoatValue getBufferStartNSize(chrtIntv); - // LOG_DEBUG(GwLog::DEBUG, "Chart:drawChart: min: %.1f, mid: %.1f, max: %.1f, rng: %.1f", chrtMin, chrtMid, chrtMax, chrtRng); + // LOG_DEBUG(GwLog::DEBUG, "Chart:drawChart: min: %.1f, mid: %.1f, max: %.1f, rng: %.1f", chrtMin, chrtMid, chrtMax, chrtRng); calcChrtBorders(chrtMin, chrtMid, chrtMax, chrtRng); chrtScale = double(valAxis) / chrtRng; // Chart scale: pixels per value step - // LOG_DEBUG(GwLog::DEBUG, "Chart:drawChart: min: %.1f, mid: %.1f, max: %.1f, rng: %.1f", chrtMin, chrtMid, chrtMax, chrtRng); + LOG_DEBUG(GwLog::DEBUG, "Chart:drawChart: min: %.1f, mid: %.1f, max: %.1f, rng: %.1f, data valid: %d", chrtMin, chrtMid, chrtMax, chrtRng, currValue.valid); // Do we have valid buffer data? if (dataBuf.getMax() == dbMAX_VAL) { // only values in buffer -> no valid wind data available @@ -204,7 +230,8 @@ void Chart::drawChrt(const char chrtDir, const int8_t chrtIntv, GwApi::BoatValue numNoData++; bufDataValid = true; - if (numNoData > THRESHOLD_NO_DATA) { // If more than 4 invalid values in a row, flag for invalid data + // if (numNoData > THRESHOLD_NO_DATA) { // If more than 4 invalid values in a row, flag for invalid data + if (numNoData > THRESHOLD_NO_DATA * (dataBuf.getUpdFreq() / 1000)) { // If more than invalid values in a row, flag for invalid data bufDataValid = false; return; } @@ -219,17 +246,15 @@ void Chart::getBufferStartNSize(const int8_t chrtIntv) count = dataBuf.getCurrentSize(); currIdx = dataBuf.getLastIdx(); numAddedBufVals = (currIdx - lastAddedIdx + bufSize) % bufSize; // Number of values added to buffer since last display + lastAddedIdx = currIdx; - if (chrtIntv != oldChrtIntv || count == 1) { - // new data interval selected by user; this is only x * 230 values instead of 240 seconds (4 minutes) per interval step + if (chrtIntv != oldChrtIntv || count == 1) { // new data interval selected by user numBufVals = min(count, (timAxis - MIN_FREE_VALUES) * chrtIntv); // keep free or release MIN_FREE_VALUES on chart for plotting of new values bufStart = max(0, count - numBufVals); - lastAddedIdx = currIdx; oldChrtIntv = chrtIntv; } else { numBufVals = numBufVals + numAddedBufVals; - lastAddedIdx = currIdx; if (count == bufSize) { bufStart = max(0, bufStart - numAddedBufVals); } @@ -308,8 +333,15 @@ void Chart::calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, doub double currMinVal = dataBuf.getMin(numBufVals); double currMaxVal = dataBuf.getMax(numBufVals); - if (currMinVal == dbMAX_VAL || currMaxVal == dbMAX_VAL) { - return; // no valid data + if (currMinVal == dbMAX_VAL || currMaxVal == dbMAX_VAL) { // no valid data + auto chkIsNaN = [](double& val) { if (std::isnan(val)) val = 0.0; }; // define inline function + // range values can be undefined if boat data type has not been created at time of chart printing (e.g. XDR data types) + // we set them to "0.0" then + chkIsNaN(rngMin); + chkIsNaN(rngMid); + chkIsNaN(rngMax); + chkIsNaN(rng); + return; } // check if current chart border have to be adjusted @@ -333,8 +365,8 @@ void Chart::calcChrtBorders(double& rngMin, double& rngMid, double& rngMax, doub rngMid = (rngMin + rngMax) / 2.0; rng = rngMax - rngMin; - // LOG_DEBUG(GwLog::DEBUG, "calcChrtRange-end: currMinVal: %.1f, currMaxVal: %.1f, rngMin: %.1f, rngMid: %.1f, rngMax: %.1f, rng: %.1f, rngStep: %.1f, zeroValue: %.1f, dbMIN_VAL: %.1f", - // currMinVal, currMaxVal, rngMin, rngMid, rngMax, rng, rngStep, zeroValue, dbMIN_VAL); + LOG_DEBUG(GwLog::DEBUG, "calcChrtRange-end: currMinVal: %.1f, currMaxVal: %.1f, rngMin: %.1f, rngMid: %.1f, rngMax: %.1f, rng: %.1f, rngStep: %.1f, zeroValue: %.1f, dbMIN_VAL: %.1f", + currMinVal, currMaxVal, rngMin, rngMid, rngMax, rng, rngStep, zeroValue, dbMIN_VAL); } } @@ -354,7 +386,7 @@ void Chart::drawChartLines(const char direction, const int8_t chrtIntv, const do // Calculate index with wrapping: (start - offset + size) % size int primeIdx = (bufStart - (p * chrtIntv)); double primeVal = dataBuf.get(primeIdx); - + if (primeVal != dbMAX_VAL) { chrtAvg.reading(primeVal); } @@ -376,8 +408,8 @@ void Chart::drawChartLines(const char direction, const int8_t chrtIntv, const do point = setCurrentChartPoint(i, direction, chrtVal, chrtScale); - // if (i >= (numBufVals / chrtIntv) - 5) // log chart data of 1 line (adjust for test purposes) - // LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %.2f, chrtMin: %.2f, {x,y} {%d,%d}", i, chrtVal, chrtMin, x, y); + if (i >= (numBufVals / chrtIntv) - 5) // log chart data of 1 line (adjust for test purposes) + LOG_DEBUG(GwLog::DEBUG, "PageWindPlot Chart: i: %d, chrtVal: %.2f, chrtMin: %.2f, {x,y} {%d,%d}", i, chrtVal, chrtMin, x, y); if ((i == 0) || (chrtPrevVal == dbMAX_VAL)) { // just a dot for 1st chart point or after some invalid values @@ -447,10 +479,10 @@ Pos Chart::setCurrentChartPoint(const int i, const char direction, const double if (chrtDataFmt == WIND || chrtDataFmt == ROTATION) { // degree type value currentPoint.y = cRoot.y + static_cast((WindUtils::to2PI(chrtVal - chrtMin) * chrtScale) + 0.5); // calculate chart point and round - } else if (chrtDataFmt == SPEED or chrtDataFmt == TEMPERATURE) { // speed or temperature data format -> print low values at bottom - currentPoint.y = cRoot.y + valAxis - static_cast(((chrtVal - chrtMin) * chrtScale) + 0.5); // calculate chart point and round - } else { // any other data format + } else if (chrtDataFmt == WIND || chrtDataFmt == ROTATION || chrtDataFmt == DEPTH) { // print low values at bottom currentPoint.y = cRoot.y + static_cast(((chrtVal - chrtMin) * chrtScale) + 0.5); // calculate chart point and round + } else { // any other data format + currentPoint.y = cRoot.y + valAxis - static_cast(((chrtVal - chrtMin) * chrtScale) + 0.5); // calculate chart point and round } } else { // vertical chart @@ -532,7 +564,8 @@ void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntNa font = &Ubuntu_Bold12pt8b; getdisplay().setFont(font); getdisplay().fillRect(cRoot.x + timAxis - 57, cRoot.y + 2, 58, 20, bgColor); // clear small area to remove potential chart lines - drawTextRalign(cRoot.x + timAxis, cRoot.y + 19, dbName.substring(0, 5)); + String name = xdrDelete(dbName); // Value name + drawTextRalign(cRoot.x + timAxis - 1, cRoot.y + 19, name.substring(0, 5)); if (chrtDataFmt == WIND) { prntHorizChartThreeValueAxisLabel(font); @@ -548,10 +581,11 @@ void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntNa font = &Ubuntu_Bold10pt8b; if (prntName) { - // print buffer data name on right hand side of time axis (max. size 5 characters) + // print buffer data name on right hand side of time axis (max. size 6 characters) getdisplay().setFont(font); - getdisplay().fillRect(cRoot.x + timAxis - 57, cRoot.y + 2, 58, 20, bgColor); // clear small area to remove potential chart lines - drawTextRalign(cRoot.x + timAxis, cRoot.y + 16, dbName.substring(0, 5)); + getdisplay().fillRect(cRoot.x + timAxis - 57, cRoot.y + 2, 58, 16, bgColor); // clear small area to remove potential chart lines + String name = xdrDelete(dbName); // Value name + drawTextRalign(cRoot.x + timAxis - 1, cRoot.y + 16, name.substring(0, 6)); } prntHorizChartThreeValueAxisLabel(font); @@ -563,7 +597,8 @@ void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntNa if (chrtSz == FULL_SIZE) { font = &Ubuntu_Bold12pt8b; getdisplay().setFont(font); // use larger font - drawTextRalign(cRoot.x + (valAxis * 0.42), cRoot.y - 2, dbName.substring(0, 6)); // print buffer data name (max. size 5 characters) + String name = xdrDelete(dbName); // Value name + drawTextRalign(cRoot.x + (valAxis * 0.42), cRoot.y - 2, name.substring(0, 6)); // print buffer data name (max. size 6 characters) } font = &Ubuntu_Bold10pt8b; @@ -574,26 +609,32 @@ void Chart::drawChrtValAxis(const char chrtDir, const int8_t chrtSz, bool prntNa // Print current data value void Chart::prntCurrValue(const char direction, GwApi::BoatValue& currValue) { - const int xPosVal = (direction == HORIZONTAL) ? cRoot.x + (timAxis / 2) - 56 : cRoot.x + 32; - const int yPosVal = (direction == HORIZONTAL) ? cRoot.y + valAxis - 7 : cRoot.y + timAxis - 7; + const int xPosVal = (direction == HORIZONTAL) ? cRoot.x + (timAxis / 2) - 74 : cRoot.x + 31; + const int yPosVal = (direction == HORIZONTAL) ? cRoot.y + valAxis : cRoot.y + timAxis; FormattedData frmtDbData = formatValue(&currValue, *commonData, NO_SIMUDATA); String sdbValue = frmtDbData.svalue; // value as formatted string String dbUnit = frmtDbData.unit; // Unit of value; limit length to 3 characters - getdisplay().fillRect(xPosVal - 1, yPosVal - 35, 128, 41, bgColor); // Clear area for TWS value - getdisplay().drawRect(xPosVal, yPosVal - 34, 126, 40, fgColor); // Draw box for TWS value + // box + getdisplay().fillRect(xPosVal - 1, yPosVal - 40, 148, 39, bgColor); // Clear area for value + getdisplay().drawRect(xPosVal, yPosVal - 39, 146, 37, fgColor); // Draw box for value + + // value getdisplay().setFont(&DSEG7Classic_BoldItalic16pt7b); - getdisplay().setCursor(xPosVal + 1, yPosVal); - getdisplay().print(sdbValue); // value + getdisplay().setCursor(xPosVal + 1, yPosVal - 6); + getdisplay().print(sdbValue); + // name getdisplay().setFont(&Ubuntu_Bold10pt8b); - getdisplay().setCursor(xPosVal + 76, yPosVal - 17); - getdisplay().print(dbName.substring(0, 3)); // Name, limited to 3 characters + getdisplay().setCursor(xPosVal + 100, yPosVal - 23); + String name = xdrDelete(dbName); + getdisplay().print(name.substring(0, 3)); // Name, limited to 3 characters + // unit getdisplay().setFont(&Ubuntu_Bold8pt8b); - getdisplay().setCursor(xPosVal + 76, yPosVal + 0); - getdisplay().print(dbUnit); // Unit + getdisplay().setCursor(xPosVal + 101, yPosVal - 8); + getdisplay().print(dbUnit); } // print message for no valid data availabletemplate @@ -652,8 +693,8 @@ double Chart::getAngleRng(const double center, size_t amount) return (maxRng != dbMIN_VAL ? maxRng : dbMAX_VAL); // Return range from to } - // print value axis label with only three values: top, mid, and bottom for vertical chart - void Chart::prntVerticChartThreeValueAxisLabel(const GFXfont* font) +// print value axis label with only three values: top, mid, and bottom for vertical chart +void Chart::prntVerticChartThreeValueAxisLabel(const GFXfont* font) { double cVal; char sVal[7]; @@ -689,7 +730,7 @@ void Chart::prntHorizChartThreeValueAxisLabel(const GFXfont* font) double axLabel; double chrtMin, chrtMid, chrtMax; int xOffset, yOffset; // offset for text position of x axis label for different font sizes - char sVal[11]; + char sVal[11]; // data value have max. 6 digits + decimal point + 2 decimals + sign if (font == &Ubuntu_Bold10pt8b) { xOffset = 32; @@ -706,33 +747,54 @@ void Chart::prntHorizChartThreeValueAxisLabel(const GFXfont* font) chrtMax = convertValue(this->chrtMax, dbName, dbFormat, *commonData); // print top axis label - axLabel = (chrtDataFmt == SPEED || chrtDataFmt == TEMPERATURE) ? chrtMax : chrtMin; - snprintf(sVal, sizeof(sVal), "%3.0f", axLabel); - getdisplay().fillRect(cRoot.x, cRoot.y + 2, xOffset + 3, yOffset, bgColor); // Clear small area to remove potential chart lines - drawTextRalign(cRoot.x + xOffset, cRoot.y + yOffset, sVal); // range value + axLabel = (chrtDataFmt == WIND || chrtDataFmt == ROTATION || chrtDataFmt == DEPTH) ? chrtMin : chrtMax; + formatLabel(axLabel).toCharArray(sVal, 11); + if (chrtDataFmt == PRESSURE) { + snprintf(sVal, sizeof(sVal), "%4.0f", axLabel); + getdisplay().fillRect(cRoot.x, cRoot.y + 2, xOffset + 12, yOffset, bgColor); // Clear small area to remove potential chart lines + drawTextRalign(cRoot.x + xOffset + 11, cRoot.y + yOffset, sVal); // range value + } else { + if (char* dot = strchr(sVal, '.')) + *dot = '\0'; // no decimal for top axis label + getdisplay().fillRect(cRoot.x, cRoot.y + 2, xOffset + 3, yOffset, bgColor); // Clear small area to remove potential chart lines + drawTextRalign(cRoot.x + xOffset, cRoot.y + yOffset, sVal); // range value + } // print mid axis label axLabel = chrtMid; - formatLabel(axLabel).toCharArray(sVal, 11); // print mid label with 1 decimal for small numbers, if required - getdisplay().fillRect(cRoot.x, cRoot.y + (valAxis / 2) - 8, xOffset + 3, 16, bgColor); // Clear small area to remove potential chart lines - drawTextRalign(cRoot.x + xOffset, cRoot.y + (valAxis / 2) + 6, sVal); // range value - getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + (valAxis / 2), cRoot.x + timAxis, cRoot.y + (valAxis / 2), fgColor); + formatLabel(axLabel).toCharArray(sVal, 11); + if (chrtDataFmt == PRESSURE) { // print 4-digit value + getdisplay().fillRect(cRoot.x, cRoot.y + (valAxis / 2) - 8, xOffset + 12, yOffset, bgColor); // Clear small area to remove potential chart lines + drawTextRalign(cRoot.x + xOffset + 11, cRoot.y + (valAxis / 2) + 6, sVal); // range value + getdisplay().drawLine(cRoot.x + xOffset + 14, cRoot.y + (valAxis / 2), cRoot.x + timAxis, cRoot.y + (valAxis / 2), fgColor); + } else { // print 3-digit value + getdisplay().fillRect(cRoot.x, cRoot.y + (valAxis / 2) - 8, xOffset + 3, yOffset, bgColor); // Clear small area to remove potential chart lines + drawTextRalign(cRoot.x + xOffset, cRoot.y + (valAxis / 2) + 6, sVal); // range value + getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + (valAxis / 2), cRoot.x + timAxis, cRoot.y + (valAxis / 2), fgColor); + } // print bottom axis label - axLabel = (chrtDataFmt == SPEED || chrtDataFmt == TEMPERATURE) ? chrtMin : chrtMax; - snprintf(sVal, sizeof(sVal), "%3.0f", axLabel); - getdisplay().fillRect(cRoot.x, cRoot.y + valAxis - 14, xOffset + 3, 15, bgColor); // Clear small area to remove potential chart lines - drawTextRalign(cRoot.x + xOffset, cRoot.y + valAxis, sVal); // range value - getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + valAxis, cRoot.x + timAxis, cRoot.y + valAxis, fgColor); + axLabel = (chrtDataFmt == WIND || chrtDataFmt == ROTATION || chrtDataFmt == DEPTH) ? chrtMax : chrtMin; + formatLabel(axLabel).toCharArray(sVal, 11); + if (chrtDataFmt == PRESSURE) { + getdisplay().fillRect(cRoot.x, cRoot.y + valAxis - 14, xOffset + 12, yOffset, bgColor); // Clear small area to remove potential chart lines + drawTextRalign(cRoot.x + xOffset + 11, cRoot.y + valAxis, sVal); // range value + getdisplay().drawLine(cRoot.x + xOffset + 14, cRoot.y + valAxis, cRoot.x + timAxis, cRoot.y + valAxis, fgColor); + } else { + if (char* dot = strchr(sVal, '.')) + *dot = '\0'; // no decimal for bottom axis label + getdisplay().fillRect(cRoot.x, cRoot.y + valAxis - 14, xOffset + 3, yOffset, bgColor); // Clear small area to remove potential chart lines + drawTextRalign(cRoot.x + xOffset, cRoot.y + valAxis, sVal); // range value + getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + valAxis, cRoot.x + timAxis, cRoot.y + valAxis, fgColor); + } } // print value axis label with multiple axis lines for horizontal chart void Chart::prntHorizChartMultiValueAxisLabel(const GFXfont* font) { double chrtMin, chrtMax, chrtRng; -// int axSlots = 5; // no. of axis labels int xOffset; // offset for text position of x axis label for different font sizes - char sVal[11]; + char sVal[11]; // data value have max. 6 digits + decimal point + 2 decimals + sign if (font == &Ubuntu_Bold10pt8b) { xOffset = 32; @@ -753,23 +815,29 @@ void Chart::prntHorizChartMultiValueAxisLabel(const GFXfont* font) // LOG_DEBUG(GwLog::DEBUG, "Chart::printHorizMultiValueAxisLabel: chrtRng: %.2f, th-chrtRng: %.2f, axSlots: %.2f, axIntv: %.2f, axLabel: %.2f, chrtMin: %.2f, chrtMid: %.2f, chrtMax: %.2f", chrtRng, this->chrtRng, VALAXIS_SLOTS, axIntv, axLabel, this->chrtMin, chrtMid, chrtMax); int loopStrt, loopEnd, loopStp; - if (chrtDataFmt == SPEED || chrtDataFmt == TEMPERATURE || chrtDataFmt == OTHER) { - loopStrt = valAxis - valAxisStep; - loopEnd = valAxisStep / 2; - loopStp = valAxisStep * -1; - } else { + if (chrtDataFmt == WIND || chrtDataFmt == ROTATION || chrtDataFmt == DEPTH) { // Low value at top loopStrt = valAxisStep; loopEnd = valAxis - (valAxisStep / 2); loopStp = valAxisStep; + } else { + // high value at top + loopStrt = valAxis - valAxisStep; + loopEnd = valAxisStep / 2; + loopStp = valAxisStep * -1; } for (int j = loopStrt; (loopStp > 0) ? (j < loopEnd) : (j > loopEnd); j += loopStp) { - // sVal = formatLabel(axLabel); - snprintf(sVal, sizeof(sVal), "%3.0f", axLabel); - getdisplay().fillRect(cRoot.x, cRoot.y + j - 11, xOffset + 3, 21, bgColor); // Clear small area to remove potential chart lines - drawTextRalign(cRoot.x + xOffset, cRoot.y + j + 7, sVal); // range value - getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + j, cRoot.x + timAxis, cRoot.y + j, fgColor); + formatLabel(axLabel).toCharArray(sVal, 11); + if (chrtDataFmt == PRESSURE) { // print 4-digit value + getdisplay().fillRect(cRoot.x, cRoot.y + j - 11, xOffset + 12, 21, bgColor); // Clear small area to remove potential chart lines + drawTextRalign(cRoot.x + xOffset + 11, cRoot.y + j + 7, sVal); // range value + getdisplay().drawLine(cRoot.x + xOffset + 14, cRoot.y + j, cRoot.x + timAxis, cRoot.y + j, fgColor); + } else { // print 3-digit value + getdisplay().fillRect(cRoot.x, cRoot.y + j - 11, xOffset + 3, 21, bgColor); // Clear small area to remove potential chart lines + drawTextRalign(cRoot.x + xOffset, cRoot.y + j + 7, sVal); // range value + getdisplay().drawLine(cRoot.x + xOffset + 3, cRoot.y + j, cRoot.x + timAxis, cRoot.y + j, fgColor); + } axLabel += axIntv; } @@ -812,16 +880,18 @@ String Chart::formatLabel(const double& label) { char sVal[11]; - if (dbFormat == "formatCourse" || dbFormat == "formatWind") { - // Format 3 numbers with prefix zero - snprintf(sVal, sizeof(sVal), "%03.0f", label); + if (chrtDataFmt == WIND) { + snprintf(sVal, sizeof(sVal), "%03.0f", label); // Format 3 numbers with prefix zero -/* } else if (dbFormat == "formatRot") { - if (label > -10 && label < 10) { + } else if (chrtDataFmt == ROTATION) { + if (label > -9.995 && label < 9.995) { snprintf(sVal, sizeof(sVal), "%3.2f", label); } else { snprintf(sVal, sizeof(sVal), "%3.0f", label); - } */ + } + + } else if (chrtDataFmt == PRESSURE) { + snprintf(sVal, sizeof(sVal), "%4.0f", label); } else { if (label < 9.95) { diff --git a/lib/obp60task/OBPcharts.h b/lib/obp60task/OBPcharts.h index 0b6ddd7..a628336 100644 --- a/lib/obp60task/OBPcharts.h +++ b/lib/obp60task/OBPcharts.h @@ -19,6 +19,26 @@ class RingBuffer; class GwLog; class Chart { +public: + /* enum class ChrtDirection { + HORIZONTALE, + VERTICALE + }; + + enum class ChrtSize { + FULL_SIZEE, + HALF_SIZE_LEFTE, + HALF_SIZE_RIGHTE, + TWO_THIRD_TOPE + }; */ + + Chart(RingBuffer& dataBuf, CommonData& common, bool useSimuData); // Chart object of data chart + ~Chart(); + bool init(); // initialize chart object parameters + bool isValid() { return initValid; }; // Checks if chart object has been fully initialized + void showChrt(const char chrtDir, const int8_t chrtSz, const int8_t chrtIntv, bool prntName, bool showCurrValue, GwApi::BoatValue currValue); // Perform all actions to draw chart + // void showChrt(ChrtDirection chrtDir, ChrtSize chrtSz, const int8_t chrtIntv, bool prntName, bool showCurrValue, GwApi::BoatValue currValue); // Perform all actions to draw chart + protected: CommonData* commonData; GwLog* logger; @@ -29,6 +49,8 @@ protected: SPEED, DEPTH, TEMPERATURE, + PRESSURE, + HUMIDITY, OTHER }; @@ -37,6 +59,7 @@ protected: 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 TWO_THIRD_TOP = 3; 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 @@ -45,6 +68,7 @@ protected: static constexpr bool NO_SIMUDATA = true; // switch off simulation feature of function RingBuffer& dataBuf; // Buffer to display + bool initValid = false; // indicates whether object holds valid initialization data or not double dfltRng; // Default range of chart, e.g. 30 = [0..30] uint16_t fgColor; // color code for any screen writing uint16_t bgColor; // color code for screen background @@ -77,19 +101,31 @@ protected: int numBufVals; // number of wind values available for current interval selection int bufStart; // 1st data value in buffer to show int numAddedBufVals; // Number of values added to buffer since last display - size_t currIdx; // Current index in 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 currIdx; // Current index in history buffer + size_t lastIdx; // Last index of history buffer + size_t lastAddedIdx = 0; // Last index of 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 int oldChrtIntv = 0; // remember recent user selection of data interval - movingAvg chrtAvg{7}; // Store average of the last 7 chart values if chart gradient shall be smoothed + movingAvg chrtAvg { 7 }; // Store average of the last 7 chart values if chart gradient shall be smoothed double chrtPrevVal; // Last data value in chart area int x, y; // x and y coordinates for drawing int prevX, prevY; // Last x and y coordinates for drawing - bool setChartDimensions(const char direction, const int8_t size); //define dimensions and start points for chart + // Default ranges for various boat data types + // Parameters: <1st value> default range, <2nd value> step for range adjustment + std::map dfltChrtDta = { + { "formatWind", { 60.0 * DEG_TO_RAD, 10.0 * DEG_TO_RAD } }, // default wind range 60 degrees + { "formatCourse", { 60.0 * DEG_TO_RAD, 10.0 * DEG_TO_RAD } }, // default course range 60 degrees + { "formatKnots", { 2.572, 2.572 } }, // default speed range in m/s + { "formatDepth", { 10.0, 5.0 } }, // default depth range in m + { "kelvinToC", { 20.0, 5.0 } }, // default temp range in °C/K + { "formatXdr:P:P", { 4000.0, 1000.0 } } // default pressure range in Pascal (hPa * 100); XDR (bar) format is represented in gateway in the same way + }; + + bool setChartDimensions(const char direction, const int8_t size); // define dimensions and start points for chart + // bool setChartDimensions(const ChrtDirection direction, const ChrtSize size); // define dimensions and start points for 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 and @@ -106,12 +142,4 @@ protected: 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: - // Define default chart range and range step for each boat data type - static std::map dfltChrtDta; - - Chart(RingBuffer& 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 }; diff --git a/lib/obp60task/PageOneValue.cpp b/lib/obp60task/PageOneValue.cpp index f41ea09..294ad86 100644 --- a/lib/obp60task/PageOneValue.cpp +++ b/lib/obp60task/PageOneValue.cpp @@ -76,10 +76,10 @@ private: nameYoff = -34; nameFnt = &Ubuntu_Bold20pt8b; unitXoff = -295; - unitYoff = -119; + unitYoff = 21; unitFnt = &Ubuntu_Bold12pt8b; valueFnt1 = &Ubuntu_Bold12pt8b; - value1Xoff = 153; + value1Xoff = 111; value1Yoff = -119; valueFnt2 = &Ubuntu_Bold20pt8b; valueFnt3 = &DSEG7Classic_BoldItalic42pt7b; @@ -100,7 +100,7 @@ private: // Show unit getdisplay().setFont(unitFnt); - getdisplay().setCursor(305 + unitXoff, 240 + unitYoff); + getdisplay().setCursor(305 + unitXoff, 100 + unitYoff); if (holdValues) { getdisplay().print(unit1Old); // name @@ -243,7 +243,7 @@ public: dataHstryBuf = pageData.hstryBuffers->getBuffer(bValName1); if (dataHstryBuf) { - dataChart.reset(new Chart(*dataHstryBuf, Chart::dfltChrtDta[bValFormat].range, *commonData, useSimuData)); + dataChart.reset(new Chart(*dataHstryBuf, *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); @@ -276,6 +276,10 @@ public: displaySetPartialWindow(0, 0, width, height); // Set partial update + if (!dataChart->isValid()) { + dataChart->init(); // try late initialization if chart object could not be properly initialized earlier due to missing boat data + } + if (pageMode == VALUE || dataHstryBuf == nullptr) { // show only data value; ignore other pageMode options if no chart supported boat data history buffer is available showData(bValue1, FULL); diff --git a/lib/obp60task/PageTwoValues.cpp b/lib/obp60task/PageTwoValues.cpp index 82e9d32..847f12b 100644 --- a/lib/obp60task/PageTwoValues.cpp +++ b/lib/obp60task/PageTwoValues.cpp @@ -203,7 +203,7 @@ public: #if defined BOARD_OBP60S3 if (key == 5 && pageMode != VALUES) { #elif defined BOARD_OBP40S3 - if (key == 2 && pageMode != VALUES) { + if (key == 2 && pageMode != VALUES) { #endif if (dataIntv == 1) { dataIntv = 2; @@ -246,8 +246,8 @@ public: 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()); + dataChart[i].reset(new Chart(*dataHstryBuf[i], *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()); } @@ -283,6 +283,12 @@ public: displaySetPartialWindow(0, 0, width, height); // Set partial update + for (int i = 0; i < NUMVALUES; i++) { + if (!dataChart[i]->isValid()) { + dataChart[i]->init(); // try late initialization if chart object could not be properly initialized earlier due to missing boat data + } + } + 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); diff --git a/lib/obp60task/PageWeather.cpp b/lib/obp60task/PageWeather.cpp new file mode 100644 index 0000000..89156b4 --- /dev/null +++ b/lib/obp60task/PageWeather.cpp @@ -0,0 +1,307 @@ +#if defined BOARD_OBP60S3 || defined BOARD_OBP40S3 + +#include "Pagedata.h" +#include "OBP60Extensions.h" +#include "OBPDataOperations.h" +#include "OBPcharts.h" + +class PageWeather : public Page { +private: + GwLog* logger; + + enum PageMode { + VAL_CHART, + 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 int8_t TWO_THIRD_TOP = 3; + + 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 XOFFSET = 133; // x offset for display of boat values + + int width; // Screen width + int height; // Screen height + + bool keylock = false; // Keylock + PageMode pageMode = VAL_CHART; // Page display mode + int8_t dataIntv = 1; // Update interval for barograph history chart: + // (1)|(3)|(6)|(12)|(24) x 60 min. for 1, 3, 6, 12, 24 hours history chart + + // String lengthformat; + bool useSimuData; + bool holdValues; + String flashLED; + String backlightMode; + String tempFormat; + + static constexpr int NUMVALUES = 4; // number of boat values used on this page + static constexpr int NUMCHARTS = 1; // one data buffer used on this page + + // Data buffer pointer (owned by HstryBuffers) + RingBuffer* dataHstryBuf[NUMCHARTS] = { nullptr }; + std::unique_ptr dataChart[NUMCHARTS]; // Chart object + + // Old values for hold function + String sValueOld[NUMVALUES] = { "", "", "", "" }; + String unitOld[NUMVALUES] = { "", "", "", "" }; + + // display data values in display mode + void showData(const std::vector& bValue) + { + getdisplay().setTextColor(commonData->fgcolor); + + int numValues = bValue.size(); // How many values do we have to handle? We will ignore value no. 1 + + for (int i = 1; i < numValues; i++) { + String name = xdrDelete(bValue[i]->getName()); // Value name + name = name.substring(0, 7); // String length limit for value name + 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 + + int xOffset = XOFFSET * (i - 1); + + // Print name + getdisplay().setFont(&Ubuntu_Bold12pt8b); + getdisplay().setCursor(5 + xOffset, 213); + getdisplay().print(name); // name + + // Print unit + getdisplay().setFont(&Ubuntu_Bold8pt8b); + getdisplay().setCursor(5 + xOffset, 229); + if (holdValues) { + getdisplay().print(unitOld[i]); // name + } else { + getdisplay().print(unit); // name + } + + // Print value + getdisplay().setFont(&DSEG7Classic_BoldItalic20pt7b); + if (bValue[i]->getFormat() == "formatXdr:P:P" || bValue[i]->getFormat() == "formatXdr:P:B") { + getdisplay().setCursor(6 + xOffset, 275); // pressure format is always 4 digits when all other boat data has 3 digit format + } else { + getdisplay().setCursor(37 + xOffset, 275); + } + if (!holdValues || useSimuData) { + getdisplay().print(sValue); // Real value as formated string + } else { + getdisplay().print(sValueOld[i]); // Old value as formated string + } + + if (valid) { // Save value for hold function + sValueOld[i] = sValue; + unitOld[i] = unit; + } + } + + // print lines for data separation of bottom data values + getdisplay().fillRect(0, 191, 400, 2, commonData->fgcolor); // horizontal line + getdisplay().fillRect(133, 192, 2, 84, commonData->fgcolor); // vertical lines + getdisplay().fillRect(266, 192, 2, 84, commonData->fgcolor); + } + +public: + PageWeather(CommonData& common) + { + commonData = &common; + logger = commonData->logger; + LOG_DEBUG(GwLog::LOG, "Instantiate PageWeather"); + + 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/half chart | full chart + if (key == 1) { + switch (pageMode) { + case VAL_CHART: + pageMode = CHART; + break; + case CHART: + pageMode = VAL_CHART; + break; + } + setupKeys(); // Adjust key definition depending on and chart-supported boat data type + return 0; // Commit the key + } + + // Set time frame to show for 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 = 4; + } else if (dataIntv == 4) { + dataIntv = 8; + } else if (dataIntv == 8) { + dataIntv = 12; + } else { + dataIntv = 1; + } + return 0; // Commit the key + } + } + + // Keylock function + if (key == 11) { // Code for keylock + commonData->keylock = !commonData->keylock; + return 0; // Commit the key + } + return key; + } + + virtual void displayNew(PageData& pageData) + { +#ifdef BOARD_OBP60S3 + // Clear optical warning + if (flashLED == "Limit Violation") { + setBlinkingLED(false); + setFlashLED(false); + } +#endif + + for (int i = 0; i < NUMCHARTS; 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]->getFormat() == "") { // data format might have been unknown at time of buffer creation +// dataHstryBuf[i]->setFormat(bValFormat); // in that case, we specify it here, because we need it for printing of buffer data +// } + + if (dataHstryBuf[i]) { + dataChart[i].reset(new Chart(*dataHstryBuf[i], *commonData, useSimuData)); + LOG_DEBUG(GwLog::DEBUG, "PageWeather: Created chart object %d for %s, format: %s", i, bValName.c_str(), dataHstryBuf[i]->getFormat().c_str()); + } else { + LOG_DEBUG(GwLog::DEBUG, "PageWeather: No chart object available for %s", bValName.c_str()); + } + } + } + } + + int displayPage(PageData& pageData) + { + + // using CD = Chart::ChrtDirection; + + LOG_DEBUG(GwLog::LOG, "Display PageWeather"); + + // Get latest boat values for page + std::vector bValue; + for (int i = 0; i < NUMVALUES; i++) { + bValue.push_back(pageData.values[i]); + } + + // Optical warning by limit violation (unused) + if (String(flashLED) == "Limit Violation") { + setBlinkingLED(false); + setFlashLED(false); + } + + if (bValue[0] == NULL && bValue[1] == NULL && bValue[2] == NULL && bValue[3] == NULL) + return PAGE_OK; // no data, no page to display + + LOG_DEBUG(GwLog::DEBUG, "PageWeather: printing #1: %s, %.3f, %s, #2: %s, %.3f, %s, #3: %s, %.3f, %s, #4: %s, %.3f, %s", + bValue[0]->getName().c_str(), bValue[0]->value, bValue[0]->getFormat().c_str(), bValue[1]->getName().c_str(), bValue[1]->value, bValue[1]->getFormat().c_str(), + bValue[2]->getName().c_str(), bValue[2]->value, bValue[2]->getFormat().c_str(), bValue[3]->getName().c_str(), bValue[3]->value, bValue[3]->getFormat().c_str()); + + // Draw page + //*********************************************************** + + displaySetPartialWindow(0, 0, width, height); // Set partial update + + if (dataHstryBuf == nullptr) { // no buffer for main boat data item, no page display + return PAGE_UPDATE; + } + if (!dataChart[0]->isValid()) { + dataChart[0]->init(); // try late initialization if chart object could not be properly initialized earlier due to missing boat data + } + + if (pageMode == VAL_CHART) { + if (dataChart[0]) { + dataChart[0]->showChrt(HORIZONTAL, TWO_THIRD_TOP, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[0]); + } + showData(bValue); + + } else if (pageMode == CHART && dataChart[0]) { // show only data chart, but that has to exist + dataChart[0]->showChrt(HORIZONTAL, FULL_SIZE, dataIntv, PRNT_NAME, PRNT_VALUE, *bValue[0]); + } + + return PAGE_UPDATE; + }; +}; + +static Page* createPage(CommonData& common) +{ + return new PageWeather(common); +} + +/** + * with the code below we make this page known to the PageTask + * we give it a type (name) that can be selected in the config + * we define which function is to be called + * and we provide the number of user parameters we expect + * this will be number of BoatValue pointers in pageData.values + */ +PageDescription registerPageWeather( + "Weather", // Page name + createPage, // Action + 4, // Number of bus values depends on selection in Web configuration (air baro pressure, air temperature, humidity, true wind speed) + { }, // Bus values we need in the page + true // Show display header on/off +); + +#endif diff --git a/lib/obp60task/PageWindPlot.cpp b/lib/obp60task/PageWindPlot.cpp index 8f6554c..9a77d2b 100644 --- a/lib/obp60task/PageWindPlot.cpp +++ b/lib/obp60task/PageWindPlot.cpp @@ -168,10 +168,10 @@ public: twsHstry = pageData.hstryBuffers->getBuffer("TWS"); if (twdHstry) { - twdChart.reset(new Chart(*twdHstry, Chart::dfltChrtDta["formatCourse"].range, *commonData, useSimuData)); + twdChart.reset(new Chart(*twdHstry, *commonData, useSimuData)); } if (twsHstry) { - twsChart.reset(new Chart(*twsHstry, Chart::dfltChrtDta["formatKnots"].range, *commonData, useSimuData)); + twsChart.reset(new Chart(*twsHstry, *commonData, useSimuData)); } } @@ -180,10 +180,10 @@ public: awsHstry = pageData.hstryBuffers->getBuffer("AWS"); if (awdHstry) { - awdChart.reset(new Chart(*awdHstry, Chart::dfltChrtDta["formatCourse"].range, *commonData, useSimuData)); + awdChart.reset(new Chart(*awdHstry, *commonData, useSimuData)); } if (awsHstry) { - awsChart.reset(new Chart(*awsHstry, Chart::dfltChrtDta["formatKnots"].range, *commonData, useSimuData)); + awsChart.reset(new Chart(*awsHstry, *commonData, useSimuData)); } if (twdHstry && twsHstry && awdHstry && awsHstry) { LOG_DEBUG(GwLog::DEBUG, "PageWindPlot: Created wind charts"); diff --git a/lib/obp60task/config_obp40.json b/lib/obp60task/config_obp40.json index b009fcc..e73f545 100644 --- a/lib/obp60task/config_obp40.json +++ b/lib/obp60task/config_obp40.json @@ -1590,6 +1590,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -1637,6 +1638,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1671,6 +1673,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1703,6 +1706,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1734,6 +1738,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1923,6 +1928,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -1969,6 +1975,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2002,6 +2009,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2033,6 +2041,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2063,6 +2072,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2247,6 +2257,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -2292,6 +2303,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2324,6 +2336,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2354,6 +2367,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2383,6 +2397,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2562,6 +2577,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -2606,6 +2622,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2637,6 +2654,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2666,6 +2684,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2694,6 +2713,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2868,6 +2888,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -2911,6 +2932,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2941,6 +2963,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2996,6 +3019,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3165,6 +3189,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -3207,6 +3232,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3236,6 +3262,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3263,6 +3290,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3289,6 +3317,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3453,6 +3482,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -3494,6 +3524,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3522,6 +3553,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3548,6 +3580,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3573,6 +3606,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3732,6 +3766,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -3772,6 +3807,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3799,6 +3835,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3824,6 +3861,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3848,6 +3886,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4002,6 +4041,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -4041,6 +4081,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4067,6 +4108,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4091,6 +4133,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4114,6 +4157,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4263,6 +4307,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -4301,6 +4346,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4326,6 +4372,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4349,6 +4396,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4371,6 +4419,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ diff --git a/lib/obp60task/config_obp60.json b/lib/obp60task/config_obp60.json index a9e2595..fdf48d9 100644 --- a/lib/obp60task/config_obp60.json +++ b/lib/obp60task/config_obp60.json @@ -1568,6 +1568,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -1615,6 +1616,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1649,6 +1651,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1681,6 +1684,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1712,6 +1716,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1871,6 +1876,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -1917,6 +1923,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1950,6 +1957,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1981,6 +1989,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2011,6 +2020,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2166,6 +2176,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -2211,6 +2222,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2243,6 +2255,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2273,6 +2286,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2302,6 +2316,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2453,6 +2468,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -2497,6 +2513,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2528,6 +2545,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2557,6 +2575,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2585,6 +2604,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2732,6 +2752,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -2775,6 +2796,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2805,6 +2827,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2833,6 +2856,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2860,6 +2884,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3003,6 +3028,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -3045,6 +3071,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3074,6 +3101,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3101,6 +3129,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3127,6 +3156,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3266,6 +3296,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -3307,6 +3338,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3335,6 +3367,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3361,6 +3394,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3386,6 +3420,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3521,6 +3556,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -3561,6 +3597,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3588,6 +3625,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3613,6 +3651,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3637,6 +3676,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3768,6 +3808,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -3807,6 +3848,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3833,6 +3875,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3857,6 +3900,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3880,6 +3924,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4007,6 +4052,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -4045,6 +4091,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4070,6 +4117,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4093,6 +4141,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4115,6 +4164,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ diff --git a/lib/obp60task/config_obp70.json b/lib/obp60task/config_obp70.json index 3727c04..3fd704a 100644 --- a/lib/obp60task/config_obp70.json +++ b/lib/obp60task/config_obp70.json @@ -1522,6 +1522,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -1568,6 +1569,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1602,6 +1604,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1634,6 +1637,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1665,6 +1669,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1824,6 +1829,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -1869,6 +1875,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1902,6 +1909,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1933,6 +1941,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -1963,6 +1972,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2118,6 +2128,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -2162,6 +2173,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2194,6 +2206,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2224,6 +2237,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2253,6 +2267,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2404,6 +2419,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -2447,6 +2463,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2478,6 +2495,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2507,6 +2525,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2535,6 +2554,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2682,6 +2702,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -2724,6 +2745,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2754,6 +2776,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2782,6 +2805,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2809,6 +2833,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -2952,6 +2977,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -2993,6 +3019,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3022,6 +3049,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3049,6 +3077,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3075,6 +3104,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3214,6 +3244,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -3254,6 +3285,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3282,6 +3314,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3308,6 +3341,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3333,6 +3367,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3468,6 +3503,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -3507,6 +3543,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3534,6 +3571,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3559,6 +3597,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3583,6 +3622,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3714,6 +3754,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -3752,6 +3793,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3778,6 +3820,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3802,6 +3845,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3825,6 +3869,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -3952,6 +3997,7 @@ "ThreeValues", "TwoValues", "Voltage", + "Weather", "WhitePage", "Wind", "WindPlot", @@ -3989,6 +4035,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4014,6 +4061,7 @@ "SixValues", "ThreeValues", "TwoValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4037,6 +4085,7 @@ "FourValues2", "SixValues", "ThreeValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ @@ -4059,6 +4108,7 @@ "FourValues", "FourValues2", "SixValues", + "Weather", "WindRoseFlex" ], "visiblePages": [ diff --git a/lib/obp60task/obp60task.cpp b/lib/obp60task/obp60task.cpp index 1079277..3a900db 100644 --- a/lib/obp60task/obp60task.cpp +++ b/lib/obp60task/obp60task.cpp @@ -265,6 +265,8 @@ void registerAllPages(PageList &list){ list.add(®isterPageDigitalOut); extern PageDescription registerPageAutopilot; list.add(®isterPageAutopilot); + extern PageDescription registerPageWeather; + list.add(®isterPageWeather); } // Undervoltage detection for shutdown display @@ -501,15 +503,15 @@ void OBP60Task(GwApi *api){ } // Read the specified boat data types of relevant pages and create a history buffer for each type for later use in charts - // applies only for pages that uses charts - if (pages[i].parameters.pageName == "OneValue" || pages[i].parameters.pageName == "TwoValues" || pages[i].parameters.pageName == "WindPlot") { + // applies only to pages that uses charts + if (pages[i].parameters.pageName == "OneValue" || pages[i].parameters.pageName == "TwoValues" + || pages[i].parameters.pageName == "WindPlot" || pages[i].parameters.pageName == "Weather") { for (auto pVal : pages[i].parameters.values) { hstryBufferList.addBuffer(pVal->getName()); } } // Add list of history buffers to page parameters pages[i].parameters.hstryBuffers = &hstryBufferList; - } // add out of band system page (always available) @@ -846,9 +848,12 @@ void OBP60Task(GwApi *api){ api->getBoatDataValues(boatValues.numValues,boatValues.allBoatValues); api->getStatus(commonData.status); + // ulong startHndl = millis(); trueWind.handleWinds(calcTrueWnds); // calculate true wind data from apparent wind values + trueWind.setMaxWs(); // maintain MaxTWS value in any case; invalid TWS value is considered automatically calibrationDataList.handleCalibration(&boatValues); // Process calibration for all boat data in hstryBufferList.handleHstryBufs(useSimuData, commonData); // Handle history buffers for certain boat data for charts and other usage + // LOG_DEBUG(GwLog::DEBUG, "obp60task: data handling: %d ms", millis() - startHndl); // Clear display // getdisplay().fillRect(0, 0, getdisplay().width(), getdisplay().height(), commonData.bgcolor);