Introduction of state machine for TimeManager. Minor refactoring.

This commit is contained in:
2024-04-04 16:40:16 +02:00
parent 5a51707452
commit 73b390d8cd
4 changed files with 105 additions and 45 deletions

View File

@@ -12,7 +12,18 @@ typedef enum
NTP_UPDATE_PENDING = 2,
NTP_UPDATE_RETRY_DELAY = 3,
NTP_UPDATE_TOO_EARLY = 4,
} ntp_update_state;
NTP_UPDATE_SETUP_FAILED = 5,
} NtpUpdateState;
typedef enum
{
TM_INIT = 0,
TM_INITIAL_SYNC = 1,
TM_RETRY_SYNC = 2,
TM_NORMAL = 3,
TM_PROLONGED_SYNC_FAIL = 4,
TM_SETUP_FAILED = 5,
} TimeManagerState;
class TimeManager
{
@@ -20,9 +31,10 @@ class TimeManager
public:
bool ntp_sync_successful(void) const; // was there a NTP sync once?
ntp_update_state ntp_time_update(bool init = false);
bool ntp_update_failed_prolonged(void) const; // indicates if maximum time since last NTP update was too long
bool ntp_update_failed_prolonged(void); // indicates if maximum time since last NTP update was too long
NtpUpdateState ntp_time_update();
struct tm time_info(void);
TimeManagerState tm_state(void) const;
void increment_time_now_local(void);
void log_time() const; // log _time_info
void log_time(struct tm time_info) const; // log argument time_info
@@ -42,11 +54,12 @@ public:
UDPLogger *logger);
private:
void set_up_ntp(void) const; // set up NTP server
void set_up_timer_isr(void) const; // set up timer interrupt
void _set_up_ntp(void); // set up NTP server
void _set_up_timer_isr(void); // set up timer interrupt
const char *_tz; // timezone
const char *_ntp_server; // used ntp server
const char *_ntp_server; // ntp server address
UDPLogger *_logger; // logger instance
TimeManagerState _tm_state = TM_INIT; // Main state
struct tm _time_info = {0, 0, 0, 0, 0, 0, 0, 0, 0}; // structure tm holds time information
time_t _time_now_local = 0; // local timer value, updated by timer interrupt and synced by NTP when needed
time_t _time_now_ntp = 0; // NTP timer value, seconds since Epoch (1970) - UTC, only synced by NTP request.

View File

@@ -34,7 +34,7 @@
// Timings in us
#define PERIOD_ANIMATION_US (200 * 1000) // 200ms
#define PERIOD_CLOCK_UPDATE_US (1 * 1000 * 1000) // 1s
#define PERIOD_CLOCK_UPDATE_US (1 * 1000 * 1000) // Must be 1s! Do not change!
#define PERIOD_HEARTBEAT_US (1 * 1000 * 1000) // 1s
#define PERIOD_MATRIX_UPDATE_US (100 * 1000) // 100ms
#define PERIOD_NIGHTMODE_CHECK_US (20 * 1000 * 1000) // 20s

View File

@@ -31,6 +31,11 @@ struct tm TimeManager::time_info(void)
return _time_info;
}
TimeManagerState TimeManager::tm_state(void) const
{
return _tm_state;
}
bool TimeManager::ntp_sync_successful(void) const
{
return (_time_now_ntp > 0);
@@ -41,27 +46,34 @@ bool TimeManager::ntp_sync_successful(void) const
*
* @retval true if last update was successful
*/
ntp_update_state TimeManager::ntp_time_update(bool init)
NtpUpdateState TimeManager::ntp_time_update()
{
// Check if minimum update delay has elapsed
if (!init && ((system_get_time() - _ntp_sync_timestamp_us) <= _ntp_retry_delay_us))
{
return NTP_UPDATE_RETRY_DELAY; // could be replaced with appropriate enum
}
if (!init && ((system_get_time() - _ntp_sync_timestamp_us) <= (_ntp_update_period_s * 1000000)))
{
return NTP_UPDATE_TOO_EARLY; // could be replaced with appropriate enum
}
if (init)
{
set_up_ntp(); // set up NTP server once
}
ntp_update_state ntp_update_state = NTP_UPDATE_PENDING; // NTP time update
NtpUpdateState retval = NTP_UPDATE_PENDING; // NTP time update
struct tm time_info; // local NTP time info
// Set up NTP server once
if (_tm_state == TM_INIT)
{
_set_up_ntp();
}
if (_tm_state == TM_SETUP_FAILED)
{
return NTP_UPDATE_SETUP_FAILED;
}
// Check if minimum update delay has elapsed. This is always active to prevent too many NTP server requests!
if ((_tm_state != TM_INITIAL_SYNC) && (system_get_time() - _ntp_sync_timestamp_us) <= _ntp_retry_delay_us)
{
return NTP_UPDATE_RETRY_DELAY;
}
// Check if it is time for a NTP sync.
if ((_tm_state != TM_INITIAL_SYNC) && (_tm_state != TM_RETRY_SYNC) && ((system_get_time() - _ntp_sync_timestamp_us) <= (_ntp_update_period_s * 1000000)))
{
return NTP_UPDATE_TOO_EARLY;
}
_ntp_sync_timestamp_us = system_get_time(); // NTP update start time
do
@@ -72,15 +84,15 @@ ntp_update_state TimeManager::ntp_time_update(bool init)
} while (((system_get_time() - _ntp_sync_timestamp_us) <= NTP_MAX_UPDATE_TIME_US) && (time_info.tm_year < (NTP_MININUM_RX_YEAR - NTP_START_YEAR)));
ntp_update_state = (time_info.tm_year <= (NTP_MININUM_RX_YEAR - NTP_START_YEAR)) ? NTP_UPDATE_FAILED : NTP_UPDATE_OK; // sanity check
retval = (time_info.tm_year <= (NTP_MININUM_RX_YEAR - NTP_START_YEAR)) ? NTP_UPDATE_FAILED : NTP_UPDATE_OK; // sanity check
if (ntp_update_state == NTP_UPDATE_OK)
if (retval == NTP_UPDATE_OK)
{
_ntp_sync_timestamp_us = system_get_time(); // save NTP update timestamp
_time_info = time_info; // take over time_info to member variable
log_time(); // log current time
if (!init && (abs(_time_now_ntp - _time_now_local) > 10)) // in the case that the local time drifted more than 10s in _ntp_update_period_s
if ((_tm_state != TM_INITIAL_SYNC) && (abs(_time_now_ntp - _time_now_local) > 10)) // in the case that the local time drifted more than 10s in _ntp_update_period_s
{
_logger->log_string(String("Difference between local and NTP time was more than 10 seconds!\n"));
_logger->log_string("Local time was: " + String(_time_now_local) + ", NTP time is: " + String(_time_now_ntp) + "\n");
@@ -88,22 +100,33 @@ ntp_update_state TimeManager::ntp_time_update(bool init)
_time_now_local = _time_now_ntp; // sync local time with NTP time
if (init) // only set up the timer once after NTP update was successful
if (_tm_state == TM_INITIAL_SYNC) // only set up the timer once after NTP update was successful
{
set_up_timer_isr();
_set_up_timer_isr();
}
else // set state to normal
{
_tm_state = TM_NORMAL;
}
}
else
{
logger.log_string("NTP-Update was not successful. Retrying in " + String(_ntp_retry_delay_us / 1000) + "ms.\n");
_tm_state = TM_RETRY_SYNC;
logger.log_string("NTP-Update was not successful. Retrying in " + String(_ntp_retry_delay_us / 1000) + "ms.");
}
return ntp_update_state;
return retval;
}
bool TimeManager::ntp_update_failed_prolonged(void) const
bool TimeManager::ntp_update_failed_prolonged(void)
{
return _time_now_local >= (_time_now_ntp + (time_t)_ntp_max_offline_time_s);
bool retval = false;
if (_time_now_local >= (_time_now_ntp + (time_t)_ntp_max_offline_time_s))
{
_tm_state = TM_PROLONGED_SYNC_FAIL;
retval = true;
}
return retval;
}
int TimeManager::tm_min(void)
@@ -146,15 +169,17 @@ bool TimeManager::tm_isdst(void)
* @brief Sets up the timer ISR
*
*/
void TimeManager::set_up_timer_isr(void) const
void TimeManager::_set_up_timer_isr(void)
{
// set up timer interrupt after NTP update is done
if (ntp_sync_successful())
if (ITimer.attachInterruptInterval(PERIOD_CLOCK_UPDATE_US, TimerHandler))
{
(void)ITimer.attachInterruptInterval(PERIOD_CLOCK_UPDATE_US, TimerHandler);
_tm_state = TM_NORMAL;
logger.log_string(String("Timer ISR was initialized!"));
}
else
{
_tm_state = TM_SETUP_FAILED;
logger.log_string("WARNING: Timer interrupt was not attached!");
}
}
@@ -163,17 +188,19 @@ void TimeManager::set_up_timer_isr(void) const
* @brief Sets up the NTP server config
*
*/
void TimeManager::set_up_ntp(void) const
void TimeManager::_set_up_ntp(void)
{
if ((_tz != nullptr) && (_ntp_server != nullptr))
{
// set up NTP server and timezone at init
configTime(_tz, _ntp_server);
_tm_state = TM_INITIAL_SYNC;
logger.log_string(String("NTP server was initialized!"));
}
else
{
logger.log_string(String("Timezone and/or NTP-Server were not given!"));
_tm_state = TM_SETUP_FAILED;
logger.log_string(String("ERROR: Timezone and/or NTP-Server were not given correctly!"));
}
}

View File

@@ -195,7 +195,7 @@ void setup()
}
// get initial time
if (tm_mgr.ntp_time_update(true) == NTP_UPDATE_OK)
if (tm_mgr.ntp_time_update() == NTP_UPDATE_OK)
{
// show the current time for short time in words
String timeMessage = time_to_string(tm_mgr.tm_hour(), tm_mgr.tm_min());
@@ -283,14 +283,7 @@ void loop()
if ((current_time_us - last_time_update_us) >= PERIOD_TIME_UPDATE_US)
{
if (tm_mgr.ntp_sync_successful() == true) // regular case
{
(void)tm_mgr.ntp_time_update(); // NTP time update
}
else // if there was never a NTP time update before (set up failed)
{
(void)tm_mgr.ntp_time_update(true); // NTP time update with init!
}
tm_mgr.ntp_time_update(); // NTP time update
if (tm_mgr.ntp_update_failed_prolonged() == true)
{
@@ -374,14 +367,41 @@ void handle_current_state()
switch (current_state)
{
case ST_CLOCK: // state clock
{
if (tm_mgr.ntp_sync_successful() && tm_mgr.tm_state() == TM_NORMAL)
{
(void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.tm_hour(), (uint8_t)tm_mgr.tm_min()), main_color_clock);
draw_minute_indicator((uint8_t)tm_mgr.tm_min(), main_color_clock);
}
else if (tm_mgr.ntp_sync_successful() && tm_mgr.tm_state() == TM_RETRY_SYNC)
{
(void)show_string_on_clock(time_to_string((uint8_t)tm_mgr.tm_hour(), (uint8_t)tm_mgr.tm_min()), main_color_clock);
draw_minute_indicator((uint8_t)tm_mgr.tm_min(), colors_24bit[6]); // in blue to indicate a network problem
}
else
{
// clear matrix
led_matrix.flush();
// Turn on minutes LEDs (blue)
led_matrix.set_min_indicator(15, colors_24bit[6]);
led_matrix.draw_on_matrix_instant();
}
break;
}
case ST_DICLOCK: // state diclock
{
if (tm_mgr.ntp_sync_successful())
{
show_digital_clock((uint8_t)tm_mgr.tm_hour(), (uint8_t)tm_mgr.tm_min(), main_color_clock);
}
else
{
// clear matrix
led_matrix.flush();
// Turn on minutes LEDs (blue)
led_matrix.set_min_indicator(15, colors_24bit[6]);
led_matrix.draw_on_matrix_instant();
}
break;
}
case ST_SPIRAL: // state spiral