mrfioc2  2.3.0
mrmtimesrc.cpp
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2017 Michael Davidsaver
3 * mrfioc2 is distributed subject to a Software License Agreement found
4 * in file LICENSE that is included with this distribution.
5 \*************************************************************************/
6 
7 #include <stdexcept>
8 #include <vector>
9 
10 // support for clock_nanosleep
11 #if _POSIX_C_SOURCE>=200112L
12 # define HAVE_CNS
13 # include <time.h>
14 #endif
15 
16 #include <errlog.h>
17 #include <epicsGuard.h>
18 #include <epicsEvent.h>
19 #include <epicsMutex.h>
20 #include <epicsThread.h>
21 #include <epicsTypes.h>
22 #include <epicsTime.h>
23 #include <generalTimeSup.h>
24 
25 #include "mrfCommon.h"
26 #include "mrmtimesrc.h"
27 
28 typedef epicsGuard<epicsMutex> Guard;
29 typedef epicsGuardRelease<epicsMutex> UnGuard;
30 
32 {
34  Impl(TimeStampSource *owner, double period)
35  :owner(owner)
36  ,timeoutRun(*this)
37 #ifdef HAVE_CNS
38  ,softsrcRun(*this)
39  ,stopsrc(true)
40 #endif
41  ,stop(false)
42  ,resync(true)
43  ,okCnt(0u)
44  ,lastError(-1.0)
45  ,period(period*1.1) // our timeout period is 10% longer than the expected reset period
46  ,next(0u)
47  {}
49  {
50  {
51  Guard G(mutex);
52  stop = true;
53  }
54  wakeup.signal();
55  if(timeout.get()) timeout->exitWait();
56  }
57 
58  /* We want to have a timeout on timestamps.
59  *
60  * That is, to detect and error if tickSecond() isn't called every second.
61  * The complication is to do this in a portable manner.
62  * We must also be careful to avoid depending on epicsTime
63  * as this time source may stop if we stall.
64  *
65  * So rather hacky, but epicsEvent::wait(double) can be depended
66  * upon to timeout. So have a seperate thread to detect timeout.
67  */
68  void runTimeout()
69  {
70  Guard G(mutex);
71 
72  while(!stop) {
73  bool ok;
74  {
75  UnGuard U(G);
76  ok = wakeup.wait(1.1); // false == timeout
77  }
78  if(ok && okCnt<5) {
79  okCnt++;
80  } else if(!ok) {
81  okCnt = 0u;
82  }
83  }
84  }
85 
86 #ifdef HAVE_CNS
87  void runSrc()
88  {
89  Guard G(mutex);
90  while(!stopsrc) {
91  UnGuard U(G);
92 
93  timespec now;
94  if(clock_gettime(CLOCK_REALTIME, &now)!=0) {
95  wakeupsrc.wait(10.0);
96  continue;
97  }
98 
99  // try to wake up just before the start of the second.
100  // reality is that we'll wake up a little later
101  // TODO: tune this so that now.tv_nsec~=0 after first run
102  now.tv_nsec = 999999000;
103 
104  if(clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &now, NULL)!=0) {
105  wakeupsrc.wait(10.0);
106  continue;
107  }
108 
109  try {
111  }catch(std::exception& e){
112  errlogPrintf("Soft timestamp source can't reset event: %s\n", e.what());
113  wakeupsrc.wait(10.0);
114  continue;
115  }
116 
117  owner->postSoftSecondsSrc();
118  }
119  }
120 #endif
121 
122  epicsMutex mutex;
123  epicsEvent wakeup;
124  epicsThreadRunableMethod<Impl, &Impl::runTimeout> timeoutRun;
125  mrf::auto_ptr<epicsThread> timeout;
126 
127 #ifdef HAVE_CNS
128  epicsThreadRunableMethod<Impl, &Impl::runSrc> softsrcRun;
129  mrf::auto_ptr<epicsThread> softsrc;
130  bool stopsrc;
131  epicsEvent wakeupsrc;
132 #endif
133 
134  bool stop;
135  bool resync;
136  unsigned okCnt;
137  double lastError;
138  const double period;
139 
140  epicsUInt32 next;
141 };
142 
144  :impl(new Impl(this, period))
145 {
146  resyncSecond();
147 }
148 
150 {
151  delete impl;
152 }
153 
155 {
156  Guard G(impl->mutex);
157  impl->resync = true;
158 }
159 
161 {
162  epicsUInt32 tosend=0;
163  bool ok;
164 
165  epicsTimeStamp ts;
166  bool valid = epicsTimeOK == generalTimeGetExceptPriority(&ts, 0, ER_PROVIDER_PRIORITY);
167 
168  {
169  Guard G(impl->mutex);
170 
171  ok = impl->okCnt>=5;
172 
173  /* delay re-sync request until 1Hz is stable, valid system time is available */
174  if(ok && valid && impl->resync) {
175  impl->next = ts.secPastEpoch+POSIX_TIME_AT_EPICS_EPOCH+1;
176  impl->resync = false;
177  }
178 
179  if(ok) {
180  tosend = impl->next;
181  }
182 
183  impl->next++;
184  ok &= tosend!=0;
185 
186  if(ok && valid) {
187  impl->lastError = double(tosend) - (ts.secPastEpoch+POSIX_TIME_AT_EPICS_EPOCH);
188  } else {
189  impl->lastError = -1.0;
190  }
191 
192  if(!impl->timeout.get()) {
193  // lazy start of timestamp timeout thread
194  impl->timeout.reset(new epicsThread(impl->timeoutRun,
195  "TimeStampTimeout",
196  epicsThreadGetStackSize(epicsThreadStackSmall)));
197  impl->timeout->start();
198  }
199  }
200 
201  impl->wakeup.signal();
202 
203  if(!ok) return;
204 
205  for(unsigned i = 0; i < 32; tosend <<= 1, i++) {
206  try {
207  if( tosend & 0x80000000 )
209  else
211  }catch(std::exception& e){
212  errlogPrintf("Soft timestamp source can't send shift event: %s\n", e.what());
213  return;
214  }
215  }
216 }
217 
219 {
220  Guard G(impl->mutex);
221  return impl->okCnt>=5;
222 }
223 
225 {
226  Guard G(impl->mutex);
227  return impl->lastError;
228 }
229 
230 
232 {
233 #ifdef HAVE_CNS
234  mrf::auto_ptr<epicsThread> cleanup;
235  {
236  Guard G(impl->mutex);
237  if(enable && !impl->softsrc.get()) {
238  // start it
239  impl->stopsrc = false;
240  impl->softsrc.reset(new epicsThread(impl->softsrcRun,
241  "SoftTimeSrc",
242  epicsThreadGetStackSize(epicsThreadStackSmall),
243  epicsThreadPriorityHigh));
244  impl->softsrc->start();
245 
246  resyncSecond();
247 
248  } else if(!enable && impl->softsrc.get()) {
249  impl->stopsrc = true;
250  cleanup = PTRMOVE(impl->softsrc);
251  }
252  }
253  if(cleanup.get()) {
254  impl->wakeup.signal();
255  cleanup->exitWait();
256  }
257 #else
258  if(enable)
259  throw std::runtime_error("Soft timestamp source not supported");
260 #endif
261 }
262 
264 {
265 #ifdef HAVE_CNS
266  Guard G(impl->mutex);
267  return !!impl->softsrc.get();
268 #else
269  return false;
270 #endif
271 }
272 
273 std::string TimeStampSource::nextSecond() const
274 {
275  epicsTimeStamp raw;
276  {
277  Guard G(impl->mutex);
278  raw.secPastEpoch = impl->next - POSIX_TIME_AT_EPICS_EPOCH;
279  raw.nsec = 0;
280  }
281  epicsTime time(raw);
282 
283  std::vector<char> buf(40);
284 
285  buf.resize(time.strftime(&buf[0], buf.size(), "%a, %d %b %Y %H:%M:%S"));
286  // buf.size() doesn't include trailing nil
287 
288  return std::string(&buf[0], buf.size());
289 }
TimeStampSource(double period)
Definition: mrmtimesrc.cpp:143
std::string nextSecond() const
Definition: mrmtimesrc.cpp:273
void softSecondsSrc(bool enable)
enable sending of event 125 by software timer. Simulation of external HW clock
Definition: mrmtimesrc.cpp:231
virtual void setEvtCode(epicsUInt32 evtCode)=0
mrf::auto_ptr< epicsThread > timeout
Definition: mrmtimesrc.cpp:125
bool isSoftSeconds() const
Definition: mrmtimesrc.cpp:263
TimeStampSource *const owner
Definition: mrmtimesrc.cpp:33
void tickSecond()
Call just after the start of each second.
Definition: mrmtimesrc.cpp:160
#define MRF_EVENT_TS_SHIFT_0
Definition: mrfCommon.h:113
Impl(TimeStampSource *owner, double period)
Definition: mrmtimesrc.cpp:34
void resyncSecond()
Call to re-initialize timestamp counter from system time.
Definition: mrmtimesrc.cpp:154
#define MRF_EVENT_TS_SHIFT_1
Definition: mrfCommon.h:114
#define ER_PROVIDER_PRIORITY
Priority given to EVR&#39;s timestamp/event provider.
Definition: mrfCommon.h:131
epicsGuardRelease< epicsMutex > UnGuard
Definition: mrmtimesrc.cpp:29
#define MRF_EVENT_TS_COUNTER_RST
Definition: mrfCommon.h:124
virtual void postSoftSecondsSrc()
Definition: mrmtimesrc.h:45
bool validSeconds() const
Whether tickSecond() has been called for the past 5 seconds.
Definition: mrmtimesrc.cpp:218
virtual ~TimeStampSource()
Definition: mrmtimesrc.cpp:149
epicsThreadRunableMethod< Impl, &Impl::runTimeout > timeoutRun
Definition: mrmtimesrc.cpp:124
double deltaSeconds() const
last difference between
Definition: mrmtimesrc.cpp:224
epicsGuard< epicsMutex > Guard
Definition: mrmtimesrc.cpp:28