AceTime  0.5.2
Date and time classes for Arduino that support timezones from the TZ Database, and a system clock that can synchronize from an NTP server or an RTC chip.
SystemClockSyncCoroutine.h
1 /*
2  * MIT License
3  * Copyright (c) 2018 Brian T. Park
4  */
5 
6 #ifndef ACE_TIME_SYSTEM_CLOCK_SYNC_COROUTINE_H
7 #define ACE_TIME_SYSTEM_CLOCK_SYNC_COROUTINE_H
8 
9 #include <stdint.h>
10 #include "../common/TimingStats.h"
11 #include "SystemClock.h"
12 
13 extern "C" unsigned long millis();
14 
15 class SystemClockSyncCoroutineTest;
16 
17 namespace ace_time {
18 namespace clock {
19 
31 class SystemClockSyncCoroutine: public ace_routine::Coroutine {
32  public:
45  explicit SystemClockSyncCoroutine(SystemClock& systemClock,
46  uint16_t syncPeriodSeconds = 3600,
47  uint16_t initialSyncPeriodSeconds = 5,
48  uint16_t requestTimeoutMillis = 1000,
49  common::TimingStats* timingStats = nullptr):
50  mSystemClock(systemClock),
51  mSyncPeriodSeconds(syncPeriodSeconds),
52  mRequestTimeoutMillis(requestTimeoutMillis),
53  mTimingStats(timingStats),
54  mCurrentSyncPeriodSeconds(initialSyncPeriodSeconds) {}
55 
63  int runCoroutine() override {
64  if (mSystemClock.mSyncTimeProvider == nullptr) return 0;
65 
66  COROUTINE_LOOP() {
67  // Send request
68  mSystemClock.mSyncTimeProvider->sendRequest();
69  mRequestStartTime = millis();
70 
71  // Wait for request
72  while (true) {
73  if (mSystemClock.mSyncTimeProvider->isResponseReady()) {
74  mRequestStatus = kStatusOk;
75  break;
76  }
77 
78  {
79  // Local variable waitTime must be scoped with {} so that the goto
80  // in COROUTINE_LOOP() skip past it in clang++. g++ seems to be
81  // fine without it.
82  uint16_t waitTime = millis() - mRequestStartTime;
83  if (waitTime >= mRequestTimeoutMillis) {
84  mRequestStatus = kStatusTimedOut;
85  break;
86  }
87  }
88 
89  COROUTINE_YIELD();
90  }
91 
92  // Process the response
93  if (mRequestStatus == kStatusOk) {
94  acetime_t nowSeconds =
95  mSystemClock.mSyncTimeProvider->readResponse();
96  uint16_t elapsedTime = millis() - mRequestStartTime;
97  if (mTimingStats != nullptr) {
98  mTimingStats->update(elapsedTime);
99  }
100  mSystemClock.sync(nowSeconds);
101  mCurrentSyncPeriodSeconds = mSyncPeriodSeconds;
102  }
103 
104  COROUTINE_DELAY_SECONDS(mDelayLoopCounter, mCurrentSyncPeriodSeconds);
105 
106  // Determine the retry delay time based on success or failure. If
107  // failure, retry with exponential backoff, until the delay becomes
108  // mSyncPeriodSeconds.
109  if (mRequestStatus == kStatusTimedOut) {
110  if (mCurrentSyncPeriodSeconds >= mSyncPeriodSeconds / 2) {
111  mCurrentSyncPeriodSeconds = mSyncPeriodSeconds;
112  } else {
113  mCurrentSyncPeriodSeconds *= 2;
114  }
115  }
116  }
117  }
118 
119  private:
120  friend class ::SystemClockSyncCoroutineTest;
121 
122  // disable copy constructor and assignment operator
125  delete;
126 
127  static const uint8_t kStatusOk = 0;
128  static const uint8_t kStatusTimedOut = 1;
129 
130  SystemClock& mSystemClock;
131  uint16_t const mSyncPeriodSeconds;
132  uint16_t const mRequestTimeoutMillis;
133  common::TimingStats* const mTimingStats;
134 
135  uint16_t mRequestStartTime;
136  uint16_t mCurrentSyncPeriodSeconds;
137  uint8_t mRequestStatus;
138  uint16_t mDelayLoopCounter;
139 };
140 
141 }
142 }
143 
144 #endif
A coroutine that syncs the SystemClock with its mSyncTimeProvider.
A TimeKeeper that uses the Arduino millis() function to advance the time returned to the user...
Definition: SystemClock.h:50
virtual bool isResponseReady() const
Return true if a response is ready.
Definition: TimeProvider.h:37
void sync(acetime_t epochSeconds)
Similar to setNow() except that backupNow() is called only if the backupTimeKeeper is different from ...
Definition: SystemClock.h:110
Helper class to collect timing statistics such as min, max, average.
Definition: TimingStats.h:19
virtual void sendRequest() const
Send a time request asynchronously.
Definition: TimeProvider.h:34
virtual acetime_t readResponse() const
Returns number of seconds since AceTime epoch (2000-01-01).
Definition: TimeProvider.h:44
SystemClockSyncCoroutine(SystemClock &systemClock, uint16_t syncPeriodSeconds=3600, uint16_t initialSyncPeriodSeconds=5, uint16_t requestTimeoutMillis=1000, common::TimingStats *timingStats=nullptr)
Constructor.