diff --git a/inc/domain/TicFrameParser.h b/inc/domain/TicFrameParser.h index 8af6496..cb4396f 100644 --- a/inc/domain/TicFrameParser.h +++ b/inc/domain/TicFrameParser.h @@ -84,8 +84,20 @@ class TicFrameParser { protected: void onNewMeasurementAvailable(); + /** + * @brief Take a TIC frame date timestamp into account + * + * @param horodate The orodate data that has been read in the current TIC frame + **/ void onNewDate(const TIC::Horodate& horodate); + /** + * @brief Try to guess the arrival time for the current frame and store it as the current frame's horodate + * + * @note This is required for historical TIC frames that include no date timestamp + **/ + void guessFrameArrivalTime(); + void onRefPowerInfo(uint32_t power); void onNewComputedPower(int minValue, int maxValue); diff --git a/inc/hal/Stm32DebugOutput.h b/inc/hal/Stm32DebugOutput.h index f5357cd..def3c0f 100644 --- a/inc/hal/Stm32DebugOutput.h +++ b/inc/hal/Stm32DebugOutput.h @@ -49,6 +49,11 @@ class Stm32DebugOutput { bool send(const std::string& text); #endif + /** + * @brief Send an unsigned int value to the debug console + */ + bool send(unsigned int value); + /** * @brief Send the hex dump of a byte buffer to the debug console */ diff --git a/src/domain/PowerHistory.cpp b/src/domain/PowerHistory.cpp index 70f6010..85f311a 100644 --- a/src/domain/PowerHistory.cpp +++ b/src/domain/PowerHistory.cpp @@ -97,9 +97,17 @@ void PowerHistory::setContext(TicProcessingContext* context) { void PowerHistory::onNewPowerData(const TicEvaluatedPower& power, const TIC::Horodate& horodate, unsigned int frameSequenceNb) { #ifdef EMBEDDED_DEBUG_CONSOLE - Stm32DebugOutput::get().send("onNewPowerData()\n"); + Stm32DebugOutput::get().send("onNewPowerData() with "); + if (!(power.isValid)) { + Stm32DebugOutput::get().send("in"); + } + Stm32DebugOutput::get().send("valid power and "); + if (!(horodate.isValid)) { + Stm32DebugOutput::get().send("in"); + } + Stm32DebugOutput::get().send("valid horodate\n"); #endif - if (!power.isValid /*|| !horodate.isValid*/) { + if (!power.isValid || !horodate.isValid) { #ifdef EMBEDDED_DEBUG_CONSOLE Stm32DebugOutput::get().send("Skipping invalid frame\n"); #endif diff --git a/src/domain/TicFrameParser.cpp b/src/domain/TicFrameParser.cpp index 55c49a3..cc5f853 100644 --- a/src/domain/TicFrameParser.cpp +++ b/src/domain/TicFrameParser.cpp @@ -208,6 +208,38 @@ void TicFrameParser::onNewDate(const TIC::Horodate& horodate) { this->lastFrameMeasurements.horodate = horodate; } +void TicFrameParser::guessFrameArrivalTime() { + this->onNewMeasurementAvailable(); + if (this->lastFrameMeasurements.fromFrameNb != this->nbFramesParsed) { +#ifdef EMBEDDED_DEBUG_CONSOLE + Stm32DebugOutput::get().send("It seems we are in a new frame ID "); + Stm32DebugOutput::get().send(static_cast(this->nbFramesParsed)); + Stm32DebugOutput::get().send("\n"); +#endif + } + this->lastFrameMeasurements.horodate = TIC::Horodate(); + this->lastFrameMeasurements.horodate.isValid = true; /* Fake the horodate */ + this->lastFrameMeasurements.horodate.year = 2024; + this->lastFrameMeasurements.horodate.month = 1; + this->lastFrameMeasurements.horodate.second = (this->nbFramesParsed * 3) % 60; /* Assume 1 historical TIC frame every 3 seconds */ + unsigned int emulatedHorodateRemainder = (this->nbFramesParsed / 20); /* Counts total remainder as minutes */ + this->lastFrameMeasurements.horodate.minute = emulatedHorodateRemainder % 60; + emulatedHorodateRemainder = emulatedHorodateRemainder / 60; /* Now count total remainder as hours */ + this->lastFrameMeasurements.horodate.hour = emulatedHorodateRemainder % 24; + this->lastFrameMeasurements.horodate.degradedTime = true; + this->lastFrameMeasurements.horodate.season = TIC::Horodate::Season::Unknown; + /* Note: we discard days and month for now */ +#ifdef EMBEDDED_DEBUG_CONSOLE + Stm32DebugOutput::get().send("Injecting timestamp in historical frame: "); + Stm32DebugOutput::get().send(static_cast(this->lastFrameMeasurements.horodate.hour)); + Stm32DebugOutput::get().send(":"); + Stm32DebugOutput::get().send(static_cast(this->lastFrameMeasurements.horodate.minute)); + Stm32DebugOutput::get().send(":"); + Stm32DebugOutput::get().send(static_cast(this->lastFrameMeasurements.horodate.second)); + Stm32DebugOutput::get().send("\n"); +#endif +} + void TicFrameParser::onRefPowerInfo(uint32_t power) { //FIXME: Todo } @@ -232,15 +264,36 @@ void TicFrameParser::onNewComputedPower(int minValue, int maxValue) { #ifdef EMBEDDED_DEBUG_CONSOLE Stm32DebugOutput::get().send("onNewComputedPower("); - char p[6]; - p[0]='0' + (minValue / 10000)%10; - p[1]='0' + (minValue / 1000)%10; - p[2]='0' + (minValue / 100)%10; - p[3]='0' + (minValue / 10)%10; - p[4]='0' + (minValue / 1)%10; - p[5]='\0'; - Stm32DebugOutput::get().send(p); - Stm32DebugOutput::get().send("W)\n"); + if (minValue != maxValue) + Stm32DebugOutput::get().send("["); + { + if (minValue < 0) { + Stm32DebugOutput::get().send("-"); + Stm32DebugOutput::get().send(static_cast(-minValue)); + } + else { + Stm32DebugOutput::get().send(static_cast(minValue)); + } + } + if (minValue != maxValue) { + Stm32DebugOutput::get().send(";"); + if (maxValue < 0) { + Stm32DebugOutput::get().send("-"); + Stm32DebugOutput::get().send(static_cast(-maxValue)); + } + else { + Stm32DebugOutput::get().send(static_cast(maxValue)); + } + Stm32DebugOutput::get().send("]"); + } + Stm32DebugOutput::get().send("W) with "); + if (this->lastFrameMeasurements.horodate.isValid) { + Stm32DebugOutput::get().send("a valid"); + } + else { + Stm32DebugOutput::get().send("no"); + } + Stm32DebugOutput::get().send("horodate\n"); #endif this->lastFrameMeasurements.instPower.setMinMax(minValue, maxValue); if (this->onNewPowerData != nullptr) { @@ -259,12 +312,7 @@ void TicFrameParser::onDatasetExtracted(const uint8_t* buf, unsigned int cnt) { #ifdef EMBEDDED_DEBUG_CONSOLE Stm32DebugOutput::get().send("onDatasetExtracted() called with "); - char sz[4]; - sz[0]='0' + (cnt / 100)%10; - sz[1]='0' + (cnt / 10)%10; - sz[2]='0' + (cnt / 1)%10; - sz[3]='\0'; - Stm32DebugOutput::get().send(sz); + Stm32DebugOutput::get().send(static_cast(cnt)); Stm32DebugOutput::get().send(" bytes\n"); #endif TIC::DatasetView dv(buf, cnt); /* Decode the TIC dataset using a dataset view object */ @@ -275,6 +323,12 @@ void TicFrameParser::onDatasetExtracted(const uint8_t* buf, unsigned int cnt) { Stm32DebugOutput::get().send(dv.labelBuffer, dv.labelSz); Stm32DebugOutput::get().send("\n"); #endif + if (dv.decodedType == TIC::DatasetView::ValidHistorical) { /* In this case, we will have no horodate, evaluate time instead */ +#ifdef EMBEDDED_DEBUG_CONSOLE + Stm32DebugOutput::get().send("Dataset above is following historical format\n"); +#endif + this->guessFrameArrivalTime(); + } //std::vector datasetLabel(dv.labelBuffer, dv.labelBuffer+dv.labelSz); //std::cout << "Dataset has label \"" << std::string(datasetLabel.begin(), datasetLabel.end()) << "\"\n"; if (dv.labelSz == 4 && diff --git a/src/hal/Stm32DebugOutput.cpp b/src/hal/Stm32DebugOutput.cpp index 2a77324..c795991 100644 --- a/src/hal/Stm32DebugOutput.cpp +++ b/src/hal/Stm32DebugOutput.cpp @@ -45,6 +45,27 @@ bool send(const std::string& text) { } #endif +bool Stm32DebugOutput::send(unsigned int value) { + char valueAsInt[10 + 1]; + char *firstNon0Digit = nullptr; + char *currentDigit = valueAsInt + 10; + *currentDigit = '\0'; /* Terminate the string */ + do { + currentDigit--; + *currentDigit = '0' + (value % 10); + if (*currentDigit != '0') { + firstNon0Digit = currentDigit; + } + value /= 10; + } while (currentDigit != valueAsInt /* Check for underflow */); + if (!firstNon0Digit) { + valueAsInt[0] = '0'; + firstNon0Digit = valueAsInt; + valueAsInt[1] = '\0'; + } + return this->send(firstNon0Digit); +} + bool Stm32DebugOutput::hexdumpBuffer(const uint8_t* buffer, unsigned int len) { char byteHexDump[]="@@"; unsigned char nibble;