AceTime  0.6
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.
SystemClockCoroutine.h
1 /*
2  * MIT License
3  * Copyright (c) 2018 Brian T. Park
4  */
5 
6 #ifndef ACE_TIME_SYSTEM_CLOCK_COROUTINE_H
7 #define ACE_TIME_SYSTEM_CLOCK_COROUTINE_H
8 
9 #include <stdint.h>
10 #include <AceRoutine.h>
11 #include "../common/TimingStats.h"
12 #include "SystemClock.h"
13 
14 class SystemClockCoroutineTest_runCoroutine;
15 
16 namespace ace_time {
17 namespace clock {
18 
30 class SystemClockCoroutine: public SystemClock, public ace_routine::Coroutine {
31  public:
33  static const uint8_t kStatusUnknown = 0;
34 
36  static const uint8_t kStatusSent = 1;
37 
39  static const uint8_t kStatusOk = 2;
40 
42  static const uint8_t kStatusTimedOut = 3;
43 
61  Clock* referenceClock /* nullable */,
62  Clock* backupClock /* nullable */,
63  uint16_t syncPeriodSeconds = 3600,
64  uint16_t initialSyncPeriodSeconds = 5,
65  uint16_t requestTimeoutMillis = 1000,
66  common::TimingStats* timingStats = nullptr):
67  SystemClock(referenceClock, backupClock),
68  mSyncPeriodSeconds(syncPeriodSeconds),
69  mRequestTimeoutMillis(requestTimeoutMillis),
70  mTimingStats(timingStats),
71  mCurrentSyncPeriodSeconds(initialSyncPeriodSeconds) {}
72 
80  int runCoroutine() override {
81  keepAlive();
82  if (mReferenceClock == nullptr) return 0;
83 
84  COROUTINE_LOOP() {
85  // Send request
86  mReferenceClock->sendRequest();
87  mRequestStartMillis = this->millis();
88  mRequestStatus = kStatusSent;
89 
90  // Wait for request
91  while (true) {
92  if (mReferenceClock->isResponseReady()) {
93  mRequestStatus = kStatusOk;
94  break;
95  }
96 
97  {
98  // Local variable waitMillis must be scoped with {} so that the
99  // goto in COROUTINE_LOOP() 16skip past it in clang++. g++ seems to
100  // be fine without it.
101  uint16_t waitMillis =
102  (uint16_t) this->millis() - mRequestStartMillis;
103  if (waitMillis >= mRequestTimeoutMillis) {
104  mRequestStatus = kStatusTimedOut;
105  break;
106  }
107  }
108 
109  COROUTINE_YIELD();
110  }
111 
112  // Process the response
113  if (mRequestStatus == kStatusOk) {
114  acetime_t nowSeconds = mReferenceClock->readResponse();
115  if (mTimingStats != nullptr) {
116  uint16_t elapsedMillis =
117  (uint16_t) this->millis() - mRequestStartMillis;
118  mTimingStats->update(elapsedMillis);
119  }
120  syncNow(nowSeconds);
121  mCurrentSyncPeriodSeconds = mSyncPeriodSeconds;
122  }
123 
124  COROUTINE_DELAY_SECONDS(mDelayLoopCounter, mCurrentSyncPeriodSeconds);
125 
126  // Determine the retry delay time based on success or failure. If
127  // failure, retry with exponential backoff, until the delay becomes
128  // mSyncPeriodSeconds.
129  if (mRequestStatus == kStatusTimedOut) {
130  if (mCurrentSyncPeriodSeconds >= mSyncPeriodSeconds / 2) {
131  mCurrentSyncPeriodSeconds = mSyncPeriodSeconds;
132  } else {
133  mCurrentSyncPeriodSeconds *= 2;
134  }
135  }
136  }
137  }
138 
140  uint8_t getRequestStatus() const { return mRequestStatus; }
141 
142  private:
143  friend class ::SystemClockCoroutineTest_runCoroutine;
144 
145  // disable copy constructor and assignment operator
147  SystemClockCoroutine& operator=(const SystemClockCoroutine&) = delete;
148 
149  uint16_t const mSyncPeriodSeconds;
150  uint16_t const mRequestTimeoutMillis;
151  common::TimingStats* const mTimingStats;
152 
153  uint16_t mRequestStartMillis; // lower 16-bit of millis()
154  uint16_t mCurrentSyncPeriodSeconds;
155  uint16_t mDelayLoopCounter;
156  uint8_t mRequestStatus = kStatusUnknown;
157 };
158 
159 }
160 }
161 
162 #endif
static const uint8_t kStatusSent
Request has been sent and waiting for response.
A Clock that uses the Arduino millis() function to advance the time returned to the user...
Definition: SystemClock.h:53
static const uint8_t kStatusUnknown
Request state unknown.
static const uint8_t kStatusTimedOut
Request timed out.
virtual bool isResponseReady() const
Return true if a response is ready.
Definition: Clock.h:41
Helper class to collect timing statistics such as min, max, average.
Definition: TimingStats.h:19
void keepAlive()
Call this (or getNow() every 65.535 seconds or faster to keep the internal counter in sync with milli...
Definition: SystemClock.h:132
A version of SystemClock that mixes in the ace_routine::Coroutine class so that that the non-block me...
virtual void sendRequest() const
Send a time request asynchronously.
Definition: Clock.h:38
uint8_t getRequestStatus() const
Return the current request status.
Base class for objects that provide and store time.
Definition: Clock.h:20
virtual acetime_t readResponse() const
Returns number of seconds since AceTime epoch (2000-01-01).
Definition: Clock.h:48
static const uint8_t kStatusOk
Request received and valid.
void syncNow(acetime_t epochSeconds)
Similar to setNow() except that backupNow() is called only if the backupClock is different from the r...
Definition: SystemClock.h:156
SystemClockCoroutine(Clock *referenceClock, Clock *backupClock, uint16_t syncPeriodSeconds=3600, uint16_t initialSyncPeriodSeconds=5, uint16_t requestTimeoutMillis=1000, common::TimingStats *timingStats=nullptr)
Constructor.