Skip to content

Commit

Permalink
Adding sync to midnight on max power reset
Browse files Browse the repository at this point in the history
  • Loading branch information
lains committed May 12, 2024
1 parent 6382d3c commit 7c9e5c0
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 33 deletions.
30 changes: 27 additions & 3 deletions inc/domain/TicFrameParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ class TicMeasurements {
class TicFrameParser {
public:
/* Types */
typedef void(*FOnNewPowerData)(const TicEvaluatedPower& power, const TimeOfDay& timestamp, unsigned int frameId, void* context); /*!< The prototype of callbacks invoked on new power data */
typedef void(*FOnNewPowerDataFunc)(const TicEvaluatedPower& power, const TimeOfDay& timestamp, unsigned int frameId, void* context); /*!< The prototype of callbacks invoked on new power data */
typedef void(*FOnDayOverFunc)(void* context); /*!< The prototype of callbacks invoked we we switch to the next day */

/* Methods */
/**
Expand All @@ -80,7 +81,15 @@ class TicFrameParser {
* This is because we don't have 100% guarantee that exceptions are allowed (especially on embedded targets) and using std::function requires enabling exceptions.
* We can still use non-capturing lambdas as function pointer if needed (see https://stackoverflow.com/questions/28746744/passing-capturing-lambda-as-function-pointer)
*/
TicFrameParser(FOnNewPowerData onNewPowerData = nullptr, void* onNewPowerDataContext = nullptr);
TicFrameParser(FOnNewPowerDataFunc onNewPowerData = nullptr, void* onNewPowerDataContext = nullptr);

/**
* @brief Set the method to invoke when we detect a switch to the next day
*
* @param power The method to invoke
* @param context A context provided to the method
*/
void onDayOverInvoke(FOnDayOverFunc dayOverFunc, void* context);

protected:
void onNewMeasurementAvailable();
Expand All @@ -99,8 +108,21 @@ class TicFrameParser {
**/
void guessFrameArrivalTime();


/**
* @brief Method invoked when we get data about the reference power
*
* @param power The reference power in Watts
*/
void onRefPowerInfo(uint32_t power);

/**
* @brief Method invoked when we get data about the maximum daily withdrawn power
*
* @param power The maximum power in Watts
*/
void onMaxPowerInfo(uint32_t maxPower);

void onNewComputedPower(int minValue, int maxValue);

/**
Expand Down Expand Up @@ -189,8 +211,10 @@ class TicFrameParser {
static void ticFrameParserUnWrapDatasetExtractor(const uint8_t* buf, unsigned int cnt, void* context);

/* Attributes */
FOnNewPowerData onNewPowerData; /*!< Pointer to a function invoked at each new power data (withdrawn or injected) computed from a TIC frame */
FOnNewPowerDataFunc onNewPowerData; /*!< Pointer to a function invoked at each new power data (withdrawn or injected) computed from a TIC frame */
void* onNewPowerDataContext; /*!< A context pointer passed to onNewFrameBytes() and onFrameComplete() at invokation */
FOnDayOverFunc onDayOverFunc; /*!< Pointer to a function invoked when we detect switching to the next day */
void* onDayOverFuncContext; /*!< A context pointer passed as argument to the above method */
unsigned int nbFramesParsed; /*!< Total number of complete frames parsed */
TIC::DatasetExtractor de; /*!< The encapsulated dataset extractor instance (programmed to call us back on newly decoded datasets) */
TicMeasurements lastFrameMeasurements; /*!< Gathers all interesting measurement of the last frame */
Expand Down
5 changes: 5 additions & 0 deletions inc/domain/TicProcessingContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ struct Stm32SerialDriver {
struct SystemCurrentTime {
SystemCurrentTime();

/**
* @brief React to a new day starting (just after midnight)
*/
void startNewDayAtMidnight();

/* Attributes */
TimeOfDay time; /*!< The current time of day */
bool relativeToBoot; /*!< Is the time of day relative to boot (uptime) or absolute to day start */
Expand Down
10 changes: 10 additions & 0 deletions inc/domain/TimeOfDay.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

#include "TIC/DatasetView.h" // For TIC::Horodate

/* Forward declarations */
class TimeOfDay;
namespace std {
void swap(TimeOfDay& first, TimeOfDay& second);
}

class TimeOfDay {
public:
/* Methods */
Expand Down Expand Up @@ -56,6 +62,10 @@ class TimeOfDay {
bool operator<=(const TimeOfDay& other) const;
bool operator>=(const TimeOfDay& other) const;

void swapWith(TimeOfDay& other);

friend void ::std::swap(TimeOfDay& first, TimeOfDay& second);

#ifdef __TIC_LIB_USE_STD_STRING__
std::string toString() const;
#endif
Expand Down
31 changes: 30 additions & 1 deletion src/domain/TicFrameParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,11 @@ void std::swap(TicMeasurements& first, TicMeasurements& second) {
first.swapWith(second);
}

TicFrameParser::TicFrameParser(FOnNewPowerData onNewPowerData, void* onNewPowerDataContext) :
TicFrameParser::TicFrameParser(FOnNewPowerDataFunc onNewPowerData, void* onNewPowerDataContext) :
onNewPowerData(onNewPowerData),
onNewPowerDataContext(onNewPowerDataContext),
onDayOverFunc([](void* context) { }),
onDayOverFuncContext(nullptr),
nbFramesParsed(0),
de(ticFrameParserUnWrapDatasetExtractor, this),
lastFrameMeasurements()
Expand Down Expand Up @@ -235,10 +237,23 @@ void TicFrameParser::guessFrameArrivalTime() {
#endif
}

void TicFrameParser::onDayOverInvoke(FOnDayOverFunc dayOverFunc, void* context) {
this->onDayOverFunc = dayOverFunc;
this->onDayOverFuncContext = context;
}

void TicFrameParser::onRefPowerInfo(uint32_t power) {
//FIXME: Todo
}

void TicFrameParser::onMaxPowerInfo(uint32_t maxPower) {
static uint32_t lastKnownMaxPower = 0;
if (maxPower < lastKnownMaxPower) { /* Daily max power reduces (probably 0), this means we are starting a new day, we are at midnight */
//invoke our own onModnightInvoke callback
}
lastKnownMaxPower = maxPower;
}

void TicFrameParser::onNewInstVoltageMeasurement(uint32_t voltage) {
this->onNewMeasurementAvailable();
this->lastFrameMeasurements.instVoltage = voltage;
Expand Down Expand Up @@ -377,6 +392,20 @@ void TicFrameParser::onDatasetExtracted(const uint8_t* buf, unsigned int cnt) {
if (pref != (uint32_t)-1)
this->onRefPowerInfo(pref);
}
else if (dv.labelSz == 4 &&
memcmp(dv.labelBuffer, "PMAX", 4) == 0 &&
dv.dataSz > 0) { /* Max withdrawn power today */
uint32_t pmax = dv.uint32FromValueBuffer(dv.dataBuffer, dv.dataSz);
if (pmax != (uint32_t)-1)
this->onMaxPowerInfo(pmax);
}
else if (dv.labelSz == 8 &&
memcmp(dv.labelBuffer, "SMAXSN", 6) == 0 &&
dv.dataSz > 0) { /* Max withdrawn power today */
uint32_t pmax = dv.uint32FromValueBuffer(dv.dataBuffer, dv.dataSz);
if (pmax != (uint32_t)-1)
this->onMaxPowerInfo(pmax);
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/domain/TicProcessingContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ SystemCurrentTime::SystemCurrentTime() :
{
}

void SystemCurrentTime::startNewDayAtMidnight() {
TimeOfDay midnight(0, 0, 0);
std::swap(this->time, midnight);
this->relativeToBoot = false;
}

TicProcessingContext::TicProcessingContext(Stm32SerialDriver& ticSerial, TIC::Unframer& ticUnframer) :
ticSerial(ticSerial),
ticUnframer(ticUnframer),
Expand All @@ -18,3 +24,4 @@ TicProcessingContext::TicProcessingContext(Stm32SerialDriver& ticSerial, TIC::Un
currentTime()
{
}

15 changes: 15 additions & 0 deletions src/domain/TimeOfDay.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "TimeOfDay.h"
#include <utility> // For std::swap()

TimeOfDay::TimeOfDay():
isValid(false),
Expand Down Expand Up @@ -135,3 +136,17 @@ std::string TimeOfDay::toString() const {
return result;
}
#endif // __TIC_LIB_USE_STD_STRING__

void TimeOfDay::swapWith(TimeOfDay& other) {
std::swap(this->isValid, other.isValid);
std::swap(this->estimatedTime, other.estimatedTime);
std::swap(this->hour, other.hour);
std::swap(this->minute, other.minute);
std::swap(this->second, other.second);
std::swap(this->millisecond, other.millisecond);
std::swap(this->knownMilliseconds, other.knownMilliseconds);
}

void std::swap(TimeOfDay& first, TimeOfDay& second) {
first.swapWith(second);
}
1 change: 1 addition & 0 deletions src/hal/Stm32MonotonicTimeDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,4 @@ void Stm32MonotonicTimeDriver::setOnPeriodElapsed(FOnPeriodElapsedFunc toRunOnPe
this->onPeriodElapsedCallback = toRunOnPeriodElapsed;
this->onPeriodElapsedContext = context;
}

80 changes: 51 additions & 29 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ int main(void) {

TicProcessingContext ticContext(ticSerial, ticUnframer);

auto performAtMidnight = [](void* context) {
TicProcessingContext* ticContext = static_cast<TicProcessingContext*>(context);
ticContext->currentTime.startNewDayAtMidnight();
};
ticParser.onDayOverInvoke(performAtMidnight, static_cast<void*>(&ticContext));

powerHistory.setContext(&ticContext);

Stm32DebugOutput::get().send("Waiting for TIC data...\n");
Expand Down Expand Up @@ -223,8 +229,7 @@ int main(void) {

unsigned int lcdRefreshCount = 0;
#ifdef SIMULATE_POWER_VALUES_WITHOUT_TIC
char sampleHorodateAsCString[] = "e230502000000";
TIC::Horodate fakeHorodate = TIC::Horodate::fromLabelBytes(reinterpret_cast<uint8_t*>(sampleHorodateAsCString), strlen(sampleHorodateAsCString));
TimeOfDay fakeHorodate(23, 30, 0); // 23h30m00s
#endif

TicEvaluatedPower lastReceivedPower;
Expand All @@ -237,17 +242,24 @@ int main(void) {
Stm32MeasurementTimer fullDisplayCycleTimeMs(true);

#ifdef SIMULATE_POWER_VALUES_WITHOUT_TIC
int fakePowerRefV = static_cast<int>(3000) - (static_cast<int>((lcdRefreshCount*30) % 6000)); /* Results in sweeping from +3000 to -3000W */
int fakeMinPower = fakePowerRefV;
int fakeMaxPower = fakePowerRefV;
if (fakePowerRefV < 0) { /* When reaching negative values, forge a range instead of an exact measurement to be more realistic */
fakeMinPower = fakePowerRefV/3;
fakeMaxPower = fakePowerRefV/4; /* If negative, corrects to -3000 to [-1000;-750] */
{
int fakePowerRefV = static_cast<int>(3000) - (static_cast<int>((lcdRefreshCount*30) % 6000)); /* Results in sweeping from +3000 to -3000W */
int fakeMinPower = fakePowerRefV;
int fakeMaxPower = fakePowerRefV;
if (fakePowerRefV < 0) { /* When reaching negative values, forge a range instead of an exact measurement to be more realistic */
fakeMinPower = fakePowerRefV/3;
fakeMaxPower = fakePowerRefV/4; /* If negative, corrects to -3000 to [-1000;-750] */
}
TicEvaluatedPower fakePower(fakeMinPower, fakeMaxPower);
char sampleHorodateAsCString[] = "e230502233000";
TIC::Horodate fakeTICFrameHorodate = TIC::Horodate::fromLabelBytes(reinterpret_cast<uint8_t*>(sampleHorodateAsCString), strlen(sampleHorodateAsCString));
fakeTICFrameHorodate.hour = fakeHorodate.hour;
fakeTICFrameHorodate.minute = fakeHorodate.minute;
fakeTICFrameHorodate.second = fakeHorodate.second;
fakeHorodate.addSeconds(30); /* Fast forward in time */
ticParser.lastFrameMeasurements.instPower = fakePower;
powerHistory.onNewPowerData(fakePower, fakeHorodate, ticContext.lastParsedFrameNb);
}
TicEvaluatedPower fakePower(fakeMinPower, fakeMaxPower);
fakeHorodate.addSeconds(30); /* Fast forward in time */
ticParser.lastFrameMeasurements.instPower = fakePower;
powerHistory.onNewPowerData(fakePower, fakeHorodate, ticContext.lastParsedFrameNb);
ticParser.nbFramesParsed++; /* Introspection for debug */
ticContext.lastParsedFrameNb = ticParser.nbFramesParsed;
#endif
Expand Down Expand Up @@ -314,33 +326,43 @@ int main(void) {
else {
pos += 8;
}
pos++;

pos++; // 'T' like system time (or uptime)
unsigned int systemTimeHour = ticContext.currentTime.time.hour;
statusLine[pos++]=(systemTimeHour / 10) % 10 + '0';
statusLine[pos++]=(systemTimeHour / 1) % 10 + '0';
pos++;
unsigned int systemTimeMinute = ticContext.currentTime.time.minute;
statusLine[pos++]=(systemTimeMinute / 10) % 10 + '0';
statusLine[pos++]=(systemTimeMinute / 1) % 10 + '0';
pos++;
unsigned int systemTimeSecond = ticContext.currentTime.time.second;
statusLine[pos++]=(systemTimeSecond / 10) % 10 + '0';
statusLine[pos++]=(systemTimeSecond / 1) % 10 + '0';
pos++;

statusLine[pos] = '\0'; /* Temporarily terminate string, wiping the Txx:xx:xx text */
BSP_LCD_SetFont(&Font24);
lcd.fillRect(0, 3*24, lcd.getWidth(), 24, Stm32LcdDriver::LCD_Color::Black);
BSP_LCD_SetTextColor(LCD_COLOR_BLUE);
BSP_LCD_SetBackColor(LCD_COLOR_BLACK);
auto get_font24_ptr = [](const char c) {
unsigned int bytesPerGlyph = Font24.Height * ((Font24.Width + 7) / 8);
return &(Font24.table[(c-' ') * bytesPerGlyph]);
};

lcd.drawText(0, 3*24, statusLine, Font24.Width, Font24.Height, get_font24_ptr, Stm32LcdDriver::LCD_Color::White, Stm32LcdDriver::LCD_Color::Black);

statusLine[pos++] = ' ';
{
Stm32LcdDriver::LCD_Color textColor = Stm32LcdDriver::LCD_Color::Green;
if (ticContext.currentTime.relativeToBoot) {
textColor = Stm32LcdDriver::LCD_Color::Red;
}
char* statusLineSystemTime = &(statusLine[pos]);
pos++; // 'T' like system time (or uptime)
unsigned int systemTimeHour = ticContext.currentTime.time.hour;
statusLine[pos++]=(systemTimeHour / 10) % 10 + '0';
statusLine[pos++]=(systemTimeHour / 1) % 10 + '0';
pos++;
unsigned int systemTimeMinute = ticContext.currentTime.time.minute;
statusLine[pos++]=(systemTimeMinute / 10) % 10 + '0';
statusLine[pos++]=(systemTimeMinute / 1) % 10 + '0';
pos++;
unsigned int systemTimeSecond = ticContext.currentTime.time.second;
statusLine[pos++]=(systemTimeSecond / 10) % 10 + '0';
statusLine[pos++]=(systemTimeSecond / 1) % 10 + '0';
pos++;

/* Draw only the system time part of statusLine (the trailing characters) */
lcd.drawText((statusLineSystemTime - statusLine)*17, 3*24, statusLineSystemTime, Font24.Width, Font24.Height, get_font24_ptr, textColor, Stm32LcdDriver::LCD_Color::Black);
}


lcd.fillRect(0, 4*24, lcd.getWidth(), lcd.getHeight() - 4*24, Stm32LcdDriver::LCD_Color::White);
ticContext.lastDisplayedPowerFrameNb = ticContext.lastParsedFrameNb; /* Used to detect a new TIC frame and display it as soon as it appears */

Expand Down

0 comments on commit 7c9e5c0

Please sign in to comment.