This is Unofficial EPICS BASE Doxygen Site
osiNTPTime.c
Go to the documentation of this file.
1 /*************************************************************************\
2 * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
3 * National Laboratory.
4 * Copyright (c) 2002 The Regents of the University of California, as
5 * Operator of Los Alamos National Laboratory.
6 * EPICS BASE is distributed subject to a Software License Agreement found
7 * in file LICENSE that is included with this distribution.
8 \*************************************************************************/
9 
10 /* Original Author: Marty Kraimer
11  * Date: 16JUN2000
12  */
13 
14 #include <stddef.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <limits.h>
18 #include <time.h>
19 #include <errno.h>
20 
21 #include "epicsEvent.h"
22 #include "epicsExit.h"
23 #include "epicsTypes.h"
24 #include "cantProceed.h"
25 #include "epicsThread.h"
26 #include "epicsMutex.h"
27 #include "errlog.h"
28 #include "epicsGeneralTime.h"
29 #include "generalTimeSup.h"
30 #include "iocsh.h"
31 #include "osdTime.h"
32 #include "osiNTPTime.h"
33 #include "taskwd.h"
34 
35 #define NSEC_PER_SEC 1000000000
36 #define NTPTimeSyncInterval 60.0
37 #define NTPTimeSyncRetries 4
38 
39 
40 static struct {
42  int synchronized;
51  double tickRate;
52 } NTPTimePvt;
53 
55 
56 
57 /* Forward references */
58 
59 static int NTPTimeGetCurrent(epicsTimeStamp *pDest);
60 static void NTPTimeSync(void *dummy);
61 
62 
63 /* NTPTime_Report iocsh command */
64 static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv};
65 static const iocshArg * const ReportArgs[1] = { &ReportArg0 };
66 static const iocshFuncDef ReportFuncDef = {"NTPTime_Report", 1, ReportArgs};
67 static void ReportCallFunc(const iocshArgBuf *args)
68 {
69  NTPTime_Report(args[0].ival);
70 }
71 
72 /* NTPTime_Shutdown iocsh command */
73 static const iocshFuncDef ShutdownFuncDef = {"NTPTime_Shutdown", 0, NULL};
74 static void ShutdownCallFunc(const iocshArgBuf *args)
75 {
77 }
78 
79 
80 /* Initialization */
81 
82 static void NTPTime_InitOnce(void *pprio)
83 {
84  struct timespec timespecNow;
85 
86  NTPTimePvt.synchronize = 1;
87  NTPTimePvt.synchronized = 0;
88  NTPTimePvt.loopEvent = epicsEventMustCreate(epicsEventEmpty);
89  NTPTimePvt.syncsFailed = 0;
90  NTPTimePvt.lock = epicsMutexCreate();
91 
92  /* Initialize OS-dependent code */
93  osdNTPInit();
94 
95  /* Try to sync with NTP server */
96  if (!osdNTPGet(&timespecNow)) {
97  NTPTimePvt.syncTick = osdTickGet();
98  if (timespecNow.tv_sec > POSIX_TIME_AT_EPICS_EPOCH && epicsTimeOK ==
99  epicsTimeFromTimespec(&NTPTimePvt.syncTime, &timespecNow)) {
100  NTPTimePvt.clockTick = NTPTimePvt.syncTick;
101  NTPTimePvt.clockTime = NTPTimePvt.syncTime;
102  NTPTimePvt.synchronized = 1;
103  }
104  }
105 
106  /* Start the sync thread */
109  NTPTimeSync, NULL);
110 
112 
113  /* Register the iocsh commands */
114  iocshRegister(&ReportFuncDef, ReportCallFunc);
115  iocshRegister(&ShutdownFuncDef, ShutdownCallFunc);
116 
117  /* Finally register as a time provider */
118  generalTimeRegisterCurrentProvider("NTP", *(int *)pprio, NTPTimeGetCurrent);
119 }
120 
121 void NTPTime_Init(int priority)
122 {
123  epicsThreadOnce(&onceId, NTPTime_InitOnce, &priority);
124 }
125 
126 
127 /* Shutdown */
128 
130 {
131  NTPTimePvt.synchronize = 0;
132  epicsEventSignal(NTPTimePvt.loopEvent);
133 }
134 
135 
136 /* Synchronization thread */
137 
138 static void NTPTimeSync(void *dummy)
139 {
140  taskwdInsert(0, NULL, NULL);
141 
142  for (epicsEventWaitWithTimeout(NTPTimePvt.loopEvent, NTPTimeSyncInterval);
143  NTPTimePvt.synchronize;
144  epicsEventWaitWithTimeout(NTPTimePvt.loopEvent, NTPTimeSyncInterval)) {
145  int status;
146  struct timespec timespecNow;
147  epicsTimeStamp timeNow;
148  epicsUInt32 tickNow;
149  double diff;
150  double ntpDelta;
151 
152  status = osdNTPGet(&timespecNow);
153  tickNow = osdTickGet();
154 
155  if (status) {
156  if (++NTPTimePvt.syncsFailed > NTPTimeSyncRetries &&
157  NTPTimePvt.synchronized) {
158  errlogPrintf("NTPTimeSync: NTP requests failing - %s\n",
159  strerror(errno));
160  NTPTimePvt.synchronized = 0;
161  }
162  continue;
163  }
164 
165  if (timespecNow.tv_sec <= POSIX_TIME_AT_EPICS_EPOCH ||
166  epicsTimeFromTimespec(&timeNow, &timespecNow) != epicsTimeOK) {
167  errlogPrintf("NTPTimeSync: Bad time received from NTP server\n");
168  NTPTimePvt.synchronized = 0;
169  continue;
170  }
171 
172  ntpDelta = epicsTimeDiffInSeconds(&timeNow, &NTPTimePvt.syncTime);
173  if (ntpDelta <= 0.0 && NTPTimePvt.synchronized) {
174  errlogPrintf("NTPTimeSync: NTP time not increasing, delta = %g\n",
175  ntpDelta);
176  NTPTimePvt.synchronized = 0;
177  continue;
178  }
179 
180  NTPTimePvt.syncsFailed = 0;
181  if (!NTPTimePvt.synchronized) {
182  errlogPrintf("NTPTimeSync: Sync recovered.\n");
183  }
184 
185  epicsMutexMustLock(NTPTimePvt.lock);
186  diff = epicsTimeDiffInSeconds(&timeNow, &NTPTimePvt.clockTime);
187  if (diff >= 0.0) {
188  NTPTimePvt.ticksToSkip = 0;
189  } else { /* dont go back in time */
190  NTPTimePvt.ticksToSkip = -diff * osdTickRateGet();
191  }
192  NTPTimePvt.clockTick = tickNow;
193  NTPTimePvt.clockTime = timeNow;
194  NTPTimePvt.synchronized = 1;
195  epicsMutexUnlock(NTPTimePvt.lock);
196 
197  NTPTimePvt.tickRate = (tickNow - NTPTimePvt.syncTick) / ntpDelta;
198  NTPTimePvt.syncTick = tickNow;
199  NTPTimePvt.syncTime = timeNow;
200  }
201 
202  NTPTimePvt.synchronized = 0;
203  taskwdRemove(0);
204 }
205 
206 
207 /* Time Provider Routine */
208 
209 static int NTPTimeGetCurrent(epicsTimeStamp *pDest)
210 {
211  epicsUInt32 tickNow;
212  epicsUInt32 ticksSince;
213 
214  if (!NTPTimePvt.synchronized)
215  return S_time_unsynchronized;
216 
217  epicsMutexMustLock(NTPTimePvt.lock);
218 
219  tickNow = osdTickGet();
220  ticksSince = tickNow - NTPTimePvt.clockTick;
221 
222  if (NTPTimePvt.ticksToSkip <= ticksSince) {
223  if (NTPTimePvt.ticksToSkip) {
224  ticksSince -= NTPTimePvt.ticksToSkip;
225  NTPTimePvt.ticksToSkip = 0;
226  }
227 
228  if (ticksSince) {
229  epicsUInt32 ticksPerSecond = osdTickRateGet();
230  epicsUInt32 nsecsPerTick = NSEC_PER_SEC / ticksPerSecond;
231  epicsUInt32 secsSince = ticksSince / ticksPerSecond;
232 
233  ticksSince -= secsSince * ticksPerSecond;
234  NTPTimePvt.clockTime.nsec += ticksSince * nsecsPerTick;
235  if (NTPTimePvt.clockTime.nsec >= NSEC_PER_SEC) {
236  secsSince++;
237  NTPTimePvt.clockTime.nsec -= NSEC_PER_SEC;
238  }
239  NTPTimePvt.clockTime.secPastEpoch += secsSince;
240  NTPTimePvt.clockTick = tickNow;
241  }
242  }
243 
244  *pDest = NTPTimePvt.clockTime;
245 
246  epicsMutexUnlock(NTPTimePvt.lock);
247  return 0;
248 }
249 
250 
251 /* Status Report */
252 
253 int NTPTime_Report(int level)
254 {
255  if (onceId == EPICS_THREAD_ONCE_INIT) {
256  printf("NTP driver not initialized\n");
257  } else if (NTPTimePvt.synchronize) {
258  printf("NTP driver %s synchronized with server\n",
259  NTPTimePvt.synchronized ? "is" : "is *not*");
260  if (NTPTimePvt.syncsFailed) {
261  printf("Last successful sync was %.1f minutes ago\n",
262  NTPTimePvt.syncsFailed * NTPTimeSyncInterval / 60.0);
263  }
264  if (level) {
265  char lastSync[32];
266 
267  epicsTimeToStrftime(lastSync, sizeof(lastSync),
268  "%Y-%m-%d %H:%M:%S.%06f", &NTPTimePvt.syncTime);
269  printf("Syncronization interval = %.1f seconds\n",
271  printf("Last synchronized at %s\n",
272  lastSync);
273  printf("Current OS tick rate = %u Hz\n",
274  osdTickRateGet());
275  printf("Measured tick rate = %.3f Hz\n",
276  NTPTimePvt.tickRate);
277  osdNTPReport();
278  }
279  } else {
280  printf("NTP synchronization thread not running.\n");
281  }
282  return 0;
283 }
LIBCOM_API int epicsStdCall epicsTimeFromTimespec(epicsTimeStamp *pDest, const struct timespec *pSrc)
Set epicsTimeStamp from struct timespec
Definition: epicsTime.cpp:1018
The generalTime framework provides a mechanism for several time providers to be present within the sy...
int generalTimeRegisterCurrentProvider(const char *name, int priority, TIMECURRENTFUN getTime)
void taskwdInsert(epicsThreadId tid, TASKWDFUNC callback, void *usr)
Definition: taskwd.c:176
int osdNTPGet(struct timespec *ts)
Definition: osdTime.cpp:45
pvd::Status status
void osdNTPReport(void)
Definition: osdTime.cpp:101
epicsUInt32 syncTick
Definition: osiNTPTime.c:47
#define printf
Definition: epicsStdio.h:41
void epicsStdCall iocshRegister(const iocshFuncDef *piocshFuncDef, iocshCallFunc func)
Definition: iocsh.cpp:111
int NTPTime_Report(int level)
Definition: osiNTPTime.c:253
#define NULL
Definition: catime.c:38
#define epicsTimeOK
Success.
Definition: epicsTime.h:339
#define S_time_unsynchronized
Provider not synchronized.
Definition: epicsTime.h:349
LIBCOM_API epicsEventStatus epicsEventWaitWithTimeout(epicsEventId id, double timeOut)
Wait an the event or until the specified timeout period is over.
Definition: osdEvent.c:117
epicsTimeStamp syncTime
Definition: osiNTPTime.c:46
unsigned int epicsUInt32
Definition: epicsTypes.h:43
void NTPTime_Init(int priority)
Definition: osiNTPTime.c:121
LIBCOM_API epicsEventId epicsEventMustCreate(epicsEventInitialState initialState)
Create an epicsEvent for use from C code.
Definition: epicsEvent.cpp:106
#define NSEC_PER_SEC
Definition: osiNTPTime.c:35
void epicsStdCall epicsMutexUnlock(epicsMutexId pmutexNode)
Release the semaphore.
Definition: epicsMutex.cpp:140
time_t tv_sec
Definition: osdTime.h:22
LIBCOM_API unsigned int epicsStdCall epicsThreadGetStackSize(epicsThreadStackSizeClass size)
Definition: osdThread.c:466
epicsThreadId epicsStdCall epicsThreadCreate(const char *name, unsigned int priority, unsigned int stackSize, EPICSTHREADFUNC funptr, void *parm)
Definition: epicsThread.cpp:33
#define NTPTimeSyncInterval
Definition: osiNTPTime.c:36
LIBCOM_API size_t epicsStdCall epicsTimeToStrftime(char *pBuff, size_t bufLength, const char *pFormat, const epicsTimeStamp *pTS)
Convert epicsTimeStamp to string. See epicsTime::strftime()
Definition: epicsTime.cpp:1120
epicsUInt32 clockTick
Definition: osiNTPTime.c:49
#define EPICS_THREAD_ONCE_INIT
Definition: epicsThread.h:109
double tickRate
Definition: osiNTPTime.c:51
#define epicsEventSignal(ID)
A synonym for epicsEventTrigger().
Definition: epicsEvent.h:172
APIs for the epicsMutex mutual exclusion semaphore.
LIBCOM_API void epicsStdCall epicsThreadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC, void *arg)
Extended replacement for the Posix exit and atexit routines.
#define osdTickGet
Definition: osdTime.h:28
#define POSIX_TIME_AT_EPICS_EPOCH
The EPICS Epoch is 00:00:00 Jan 1, 1990 UTC.
Definition: epicsTime.h:26
epicsMutexId lock
Definition: osiNTPTime.c:45
int errlogPrintf(const char *pFormat,...)
Definition: errlog.c:105
epicsEventId loopEvent
Definition: osiNTPTime.c:43
APIs for the epicsEvent binary semaphore.
epics::pvData::PVStructurePtr dummy
Definition: pvAccess.cpp:72
epicsTimeStamp clockTime
Definition: osiNTPTime.c:48
void osdNTPInit(void)
Definition: osdTime.cpp:81
#define epicsThreadPriorityHigh
Definition: epicsThread.h:77
void taskwdRemove(epicsThreadId tid)
Definition: taskwd.c:206
EPICS time stamp, for use from C code.
Definition: epicsTime.h:33
#define epicsMutexCreate()
Create an epicsMutex semaphore for use from C code.
Definition: epicsMutex.h:168
Defined by POSIX Real Time.
Definition: osdTime.h:21
LIBCOM_API double epicsStdCall epicsTimeDiffInSeconds(const epicsTimeStamp *pLeft, const epicsTimeStamp *pRight)
Time difference between left and right in seconds.
Definition: epicsTime.cpp:1048
Routines for code that can&#39;t continue or return after an error.
int synchronize
Definition: osiNTPTime.c:41
#define epicsAtExit(F, A)
Convenience macro to register a function and context value to be run when the process exits...
Definition: epicsExit.h:70
void NTPTime_Shutdown(void *dummy)
Definition: osiNTPTime.c:129
C++ and C descriptions for a thread.
int syncsFailed
Definition: osiNTPTime.c:44
#define epicsMutexMustLock(ID)
Claim a semaphore (see epicsMutexLock()).
Definition: epicsMutex.h:214
epicsUInt32 ticksToSkip
Definition: osiNTPTime.c:50
#define osdTickRateGet
Definition: osdTime.h:27
#define NTPTimeSyncRetries
Definition: osiNTPTime.c:37
Definition: iocsh.h:56