AceTime  1.1
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 // activate only if <AceRoutine.h> is included before this header
10 #ifdef ACE_ROUTINE_VERSION
11 
12 #include <stdint.h>
13 #include <AceRoutine.h>
14 #include "../common/TimingStats.h"
15 #include "SystemClock.h"
16 
17 class SystemClockCoroutineTest_runCoroutine;
18 
19 namespace ace_time {
20 namespace clock {
21 
33 class SystemClockCoroutine: public SystemClock, public ace_routine::Coroutine {
34  public:
36  static const uint8_t kStatusUnknown = 0;
37 
39  static const uint8_t kStatusSent = 1;
40 
42  static const uint8_t kStatusOk = 2;
43 
45  static const uint8_t kStatusTimedOut = 3;
46 
64  Clock* referenceClock /* nullable */,
65  Clock* backupClock /* nullable */,
66  uint16_t syncPeriodSeconds = 3600,
67  uint16_t initialSyncPeriodSeconds = 5,
68  uint16_t requestTimeoutMillis = 1000,
69  common::TimingStats* timingStats = nullptr):
70  SystemClock(referenceClock, backupClock),
71  mSyncPeriodSeconds(syncPeriodSeconds),
72  mRequestTimeoutMillis(requestTimeoutMillis),
73  mTimingStats(timingStats),
74  mCurrentSyncPeriodSeconds(initialSyncPeriodSeconds) {}
75 
83  int runCoroutine() override {
84  keepAlive();
85  if (mReferenceClock == nullptr) return 0;
86 
87  COROUTINE_LOOP() {
88  // Send request
89  mReferenceClock->sendRequest();
90  mRequestStartMillis = coroutineMillis();
91  mRequestStatus = kStatusSent;
92 
93  // Wait for request
94  while (true) {
95  if (mReferenceClock->isResponseReady()) {
96  mRequestStatus = kStatusOk;
97  break;
98  }
99 
100  {
101  // Local variable waitMillis must be scoped with {} so that the
102  // goto in COROUTINE_LOOP() 16skip past it in clang++. g++ seems to
103  // be fine without it.
104  uint16_t waitMillis =
105  (uint16_t) coroutineMillis() - mRequestStartMillis;
106  if (waitMillis >= mRequestTimeoutMillis) {
107  mRequestStatus = kStatusTimedOut;
108  break;
109  }
110  }
111 
112  COROUTINE_YIELD();
113  }
114 
115  // Process the response
116  if (mRequestStatus == kStatusOk) {
117  acetime_t nowSeconds = mReferenceClock->readResponse();
118  if (mTimingStats != nullptr) {
119  uint16_t elapsedMillis =
120  (uint16_t) coroutineMillis() - mRequestStartMillis;
121  mTimingStats->update(elapsedMillis);
122  }
123  syncNow(nowSeconds);
124  mCurrentSyncPeriodSeconds = mSyncPeriodSeconds;
125  }
126 
127  COROUTINE_DELAY_SECONDS(mCurrentSyncPeriodSeconds);
128 
129  // Determine the retry delay time based on success or failure. If
130  // failure, retry with exponential backoff, until the delay becomes
131  // mSyncPeriodSeconds.
132  if (mRequestStatus == kStatusTimedOut) {
133  if (mCurrentSyncPeriodSeconds >= mSyncPeriodSeconds / 2) {
134  mCurrentSyncPeriodSeconds = mSyncPeriodSeconds;
135  } else {
136  mCurrentSyncPeriodSeconds *= 2;
137  }
138  }
139  }
140  }
141 
143  uint8_t getRequestStatus() const { return mRequestStatus; }
144 
145  private:
146  friend class ::SystemClockCoroutineTest_runCoroutine;
147 
148  // disable copy constructor and assignment operator
150  SystemClockCoroutine& operator=(const SystemClockCoroutine&) = delete;
151 
152  uint16_t const mSyncPeriodSeconds;
153  uint16_t const mRequestTimeoutMillis;
154  common::TimingStats* const mTimingStats;
155 
156  uint16_t mRequestStartMillis; // lower 16-bit of millis()
157  uint16_t mCurrentSyncPeriodSeconds;
158  uint8_t mRequestStatus = kStatusUnknown;
159 };
160 
161 }
162 }
163 
164 #endif
165 
166 #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.