From a3cd917de29f43c5b072ef60005822626c460070 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 18 Apr 2008 18:39:32 +0000 Subject: [PATCH] General Time, from Peter Denison, Eric Norum and many others... --- src/dev/softDev/Makefile | 1 + src/dev/softDev/devGeneralTime.c | 262 +++++++++++ src/dev/softDev/devSoft.dbd | 5 + src/libCom/Makefile | 14 +- src/libCom/iocsh/libComRegister.c | 20 + src/libCom/osi/epicsGeneralTime.c | 527 +++++++++++++++++++++++ src/libCom/osi/epicsGeneralTime.h | 42 ++ src/libCom/osi/epicsTime.cpp | 8 +- src/libCom/osi/epicsTime.h | 7 + src/libCom/osi/generalTimeSup.h | 38 ++ src/libCom/osi/os/RTEMS/iocClock.c | 198 --------- src/libCom/osi/os/RTEMS/iocClock.h | 28 -- src/libCom/osi/os/RTEMS/osdTime.cpp | 51 ++- src/libCom/osi/os/RTEMS/osdTime.h | 12 +- src/libCom/osi/os/VMS/osdTime.cpp | 22 +- src/libCom/osi/os/WIN32/osdTime.cpp | 24 +- src/libCom/osi/os/default/osdSysTime.c | 185 ++++++++ src/libCom/osi/os/default/osdSysTime.h | 14 + src/libCom/osi/os/posix/osdTime.cpp | 14 +- src/libCom/osi/os/vxWorks/iocClock.c | 219 ---------- src/libCom/osi/os/vxWorks/iocClock.h | 20 - src/libCom/osi/os/vxWorks/osdTime.cpp | 43 +- src/libCom/osi/os/vxWorks/osdTime.h | 18 + src/libCom/osi/os/vxWorks/vxComLibrary.c | 4 - src/libCom/osi/osiNTPTime.c | 226 ++++++++++ src/libCom/osi/osiNTPTime.h | 14 + 26 files changed, 1481 insertions(+), 535 deletions(-) create mode 100644 src/dev/softDev/devGeneralTime.c create mode 100644 src/libCom/osi/epicsGeneralTime.c create mode 100644 src/libCom/osi/epicsGeneralTime.h create mode 100644 src/libCom/osi/generalTimeSup.h delete mode 100644 src/libCom/osi/os/RTEMS/iocClock.c delete mode 100644 src/libCom/osi/os/RTEMS/iocClock.h create mode 100644 src/libCom/osi/os/default/osdSysTime.c create mode 100644 src/libCom/osi/os/default/osdSysTime.h delete mode 100644 src/libCom/osi/os/vxWorks/iocClock.c delete mode 100644 src/libCom/osi/os/vxWorks/iocClock.h create mode 100644 src/libCom/osi/osiNTPTime.c create mode 100644 src/libCom/osi/osiNTPTime.h diff --git a/src/dev/softDev/Makefile b/src/dev/softDev/Makefile index b7a4f5dfd..3cfba9472 100644 --- a/src/dev/softDev/Makefile +++ b/src/dev/softDev/Makefile @@ -37,6 +37,7 @@ LIBSRCS += devSASoft.c LIBSRCS += devSiSoft.c LIBSRCS += devSoSoft.c LIBSRCS += devWfSoft.c +LIBSRCS += devGeneralTime.c LIBSRCS += devAoSoftCallback.c LIBSRCS += devBoSoftCallback.c diff --git a/src/dev/softDev/devGeneralTime.c b/src/dev/softDev/devGeneralTime.c new file mode 100644 index 000000000..8e6a5dd21 --- /dev/null +++ b/src/dev/softDev/devGeneralTime.c @@ -0,0 +1,262 @@ +/*************************************************************************** + * File: devGeneralTime.c + * Author: Sheng Peng + * Institution: Oak Ridge National Laboratory / SNS Project + * Date: 07/2004 + * Version: 1.2 + * + * EPICS device layer support for general EPICS timestamp support + * + * Integrated into base by Peter Denison, Diamond Light Source + ****************************************************************************/ + +/* Include header files */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "epicsGeneralTime.h" + +int GENERALTIME_DEV_DEBUG=0; + +/* define function flags */ +typedef enum { + GTIM_AI_CURRENT, + GTIM_BO_RSTERRCNT, + GTIM_LI_GETERRCNT, + GTIM_SI_BESTTCP, + GTIM_SI_BESTTEP +} GTIMFUNC; + + +/* define parameter check for convinence */ +#define CHECK_AIPARM(PARM,VAL)\ + if (!strncmp(pai->inp.value.instio.string,(PARM),strlen((PARM)))) {\ + pai->dpvt=(void *)VAL;\ + return (0);\ + } +#define CHECK_BOPARM(PARM,VAL)\ + if (!strncmp(pbo->out.value.instio.string,(PARM),strlen((PARM)))) {\ + pbo->dpvt=(void *)VAL;\ + paramOK=1;\ + } +#define CHECK_LIPARM(PARM,VAL)\ + if (!strncmp(pli->inp.value.instio.string,(PARM),strlen((PARM)))) {\ + pli->dpvt=(void *)VAL;\ + return (0);\ + } +#define CHECK_SIPARM(PARM,VAL)\ + if (!strncmp(psi->inp.value.instio.string,(PARM),strlen((PARM)))) {\ + psi->dpvt=(void *)VAL;\ + return (0);\ + } + +/* function prototypes */ +static long init(int pass); + +static long init_ai(struct aiRecord *pai); +static long read_ai(struct aiRecord *pai); + +static long init_bo(struct boRecord *pbo); +static long write_bo(struct boRecord *pbo); + +static long init_li(struct longinRecord *pli); +static long read_li(struct longinRecord *pli); + +static long init_si(struct stringinRecord *psi); +static long read_si(struct stringinRecord *psi); + +/* global struct for devSup */ +typedef struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_write; + DEVSUPFUN special_linconv;} GTIM_DEV_SUP_SET; + +GTIM_DEV_SUP_SET devAiGeneralTime= {6, NULL, init, init_ai, NULL, read_ai, NULL}; +GTIM_DEV_SUP_SET devBoGeneralTime= {6, NULL, NULL, init_bo, NULL, write_bo, NULL}; +GTIM_DEV_SUP_SET devLiGeneralTime= {6, NULL, NULL, init_li, NULL, read_li, NULL}; +GTIM_DEV_SUP_SET devSiGeneralTime= {6, NULL, NULL, init_si, NULL, read_si, NULL}; + +epicsExportAddress(dset, devAiGeneralTime); +epicsExportAddress(dset, devBoGeneralTime); +epicsExportAddress(dset, devLiGeneralTime); +epicsExportAddress(dset, devSiGeneralTime); + +/* function implementation */ + +static long init(int pass) +{ + if(pass) return 0; + return 0; +} + +static long init_ai( struct aiRecord *pai) +{ + if (pai->inp.type!=INST_IO) + { + recGblRecordError(S_db_badField, (void *)pai, + "devAiGeneralTime Init_record, Illegal INP"); + pai->pact=TRUE; + return (S_db_badField); + } + + CHECK_AIPARM("TIME", GTIM_AI_CURRENT) + /* reach here, bad parm */ + recGblRecordError(S_db_badField, (void *)pai, + "devAiGeneralTime Init_record, bad parm"); + pai->pact=TRUE; + return (S_db_badField); +} + +static long read_ai(struct aiRecord *pai) +{ + int status=-1; + double temp; + + switch ((int)pai->dpvt) + { + case GTIM_AI_CURRENT: + status=generalTimeGetCurrentDouble(&temp); + break; + default: + return -1; + } + if (status==0) + { + pai->val=temp; + pai->udf=FALSE; + return 2;/******** not convert ****/ + } + else + { + pai->udf=TRUE; + recGblSetSevr(pai,READ_ALARM,INVALID_ALARM); + return -1; + } +} + +/*********bo record **********/ +static long init_bo(struct boRecord *pbo) +{ + int paramOK=0; + + if (pbo->out.type!=INST_IO) + { + recGblRecordError(S_db_badField, (void *)pbo, + "devBoGeneralTime Init_record, Illegal OUT"); + pbo->pact=TRUE; + return (S_db_badField); + } + CHECK_BOPARM("RSTERRCNT", GTIM_BO_RSTERRCNT) + if (!paramOK) + { + recGblRecordError(S_db_badField, (void *)pbo, + "devBoGeneralTime Init_record, bad parm"); + pbo->pact=TRUE; + return (S_db_badField); + } + + pbo->mask=0; /** when convert rval from val,keep them same **/ + + return 2; /** don't convert rval to val **/ +} + +static long write_bo(struct boRecord *pbo) +{ + switch ((int)pbo->dpvt) + { + case GTIM_BO_RSTERRCNT: + generalTimeResetErrorCounts(); + break; + } + return 0; +} + + +/*******longin record *************/ +static long init_li(struct longinRecord *pli) +{ + if (pli->inp.type!=INST_IO) + { + recGblRecordError(S_db_badField, (void *)pli, + "devLiGeneralTime Init_record, Illegal INP"); + pli->pact=TRUE; + return (S_db_badField); + } + CHECK_LIPARM("GETERRCNT", GTIM_LI_GETERRCNT) + /* reach here, bad parm */ + recGblRecordError(S_db_badField, (void *)pli, + "devLiGeneralTime Init_record, bad parm"); + pli->pact=TRUE; + return (S_db_badField); +} + +static long read_li(struct longinRecord *pli) +{ + switch ((int)pli->dpvt) + { + case GTIM_LI_GETERRCNT: + pli->val=generalTimeGetErrorCounts(); + break; + default: + return -1; + } + return 0; +} + + +/********** stringin record **********/ +static long init_si(struct stringinRecord *psi) +{ + if (psi->inp.type!=INST_IO) + { + recGblRecordError(S_db_badField, (void *)psi, + "devSiGeneralTime Init_record, Illegal INP"); + psi->pact=TRUE; + return (S_db_badField); + } + CHECK_SIPARM("BESTTCP", GTIM_SI_BESTTCP) + CHECK_SIPARM("BESTTEP", GTIM_SI_BESTTEP) + /* reach here, bad parm */ + recGblRecordError(S_db_badField, (void *)psi, + "devSiGeneralTime Init_record, bad parm"); + psi->pact=TRUE; + return (S_db_badField); +} + +static long read_si(struct stringinRecord *psi) +{ + switch ((int)psi->dpvt) + { + case GTIM_SI_BESTTCP: + generalTimeGetBestTcp(psi->val); + break; + case GTIM_SI_BESTTEP: + generalTimeGetBestTep(psi->val); + break; + } + return 0; +} + diff --git a/src/dev/softDev/devSoft.dbd b/src/dev/softDev/devSoft.dbd index a717efe3c..6c28eb8c0 100644 --- a/src/dev/softDev/devSoft.dbd +++ b/src/dev/softDev/devSoft.dbd @@ -33,3 +33,8 @@ device(mbboDirect,CONSTANT,devMbboDirectSoftCallback,"Async Soft Channel") device(stringout,CONSTANT,devSoSoftCallback,"Async Soft Channel") device(stringin,INST_IO,devTimestampSI,"Soft Timestamp") + +device(ai, INST_IO,devAiGeneralTime,"General Time") +device(bo, INST_IO,devBoGeneralTime,"General Time") +device(longin, INST_IO,devLiGeneralTime,"General Time") +device(stringin,INST_IO,devSiGeneralTime,"General Time") diff --git a/src/libCom/Makefile b/src/libCom/Makefile index 511f3fdf4..3bb8e513e 100644 --- a/src/libCom/Makefile +++ b/src/libCom/Makefile @@ -174,6 +174,7 @@ INC += osdThread.h INC += epicsThread.h INC += epicsTime.h +INC += epicsGeneralTime.h INC += osdTime.h INC += epicsSignal.h INC += osiProcess.h @@ -197,6 +198,9 @@ SRCS += epicsEvent.cpp SRCS += epicsTime.cpp SRCS += epicsMessageQueue.cpp +INC += generalTimeSup.h +SRCS += epicsGeneralTime.c + SRCS += osdSock.c SRCS += osdSockAddrReuse.cpp SRCS += osiSock.c @@ -244,12 +248,12 @@ SRC_DIRS += $(LIBCOM)/tsDefs INC += tsDefs.h SRCS += tsDefs.c +SRCS_vxWorks += osiNTPTime.c +SRCS_RTEMS += osiNTPTime.c +# Time provider, in osi/os/default +SRCS_vxWorks += osdSysTime.c +SRCS_RTEMS += osdSysTime.c # These files are in osi/os/vxWorks -# IOC Time of Day clock -INC_vxWorks += iocClock.h -SRCS_vxWorks += iocClock.c -INC_RTEMS += iocClock.h -SRCS_RTEMS += iocClock.c # Special reboot hook SRCS_vxWorks += atReboot.cpp # For old vxWorks applications diff --git a/src/libCom/iocsh/libComRegister.c b/src/libCom/iocsh/libComRegister.c index 5ffd7b191..87acc4432 100644 --- a/src/libCom/iocsh/libComRegister.c +++ b/src/libCom/iocsh/libComRegister.c @@ -20,6 +20,7 @@ #include "osiUnistd.h" #include "logClient.h" #include "errlog.h" +#include "epicsGeneralTime.h" #include "libComRegister.h" @@ -291,6 +292,22 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args) } } +/* generalTimeReport */ +static const iocshArg generalTimeReportArg0 = { "interest_level", iocshArgArgv}; +static const iocshArg * const generalTimeReportArgs[1] = { &generalTimeReportArg0 }; +static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,generalTimeReportArgs}; +static void generalTimeReportCallFunc(const iocshArgBuf *args) +{ + generalTimeReport(args[0].ival); +} + +/* lastResortEventProviderInstall */ +static const iocshFuncDef lastResortEventProviderInstallFuncDef = {"lastResortEventProviderInstall", 0, NULL}; +static void lastResortEventProviderInstallCallFunc(const iocshArgBuf *args) +{ + lastResortEventProviderInstall(); +} + void epicsShareAPI libComRegister(void) { iocshRegister(&dateFuncDef, dateCallFunc); @@ -314,4 +331,7 @@ void epicsShareAPI libComRegister(void) iocshRegister(&epicsMutexShowAllFuncDef,epicsMutexShowAllCallFunc); iocshRegister(&epicsThreadSleepFuncDef,epicsThreadSleepCallFunc); iocshRegister(&epicsThreadResumeFuncDef,epicsThreadResumeCallFunc); + + iocshRegister(&generalTimeReportFuncDef,generalTimeReportCallFunc); + iocshRegister(&lastResortEventProviderInstallFuncDef, lastResortEventProviderInstallCallFunc); } diff --git a/src/libCom/osi/epicsGeneralTime.c b/src/libCom/osi/epicsGeneralTime.c new file mode 100644 index 000000000..7395f9fe8 --- /dev/null +++ b/src/libCom/osi/epicsGeneralTime.c @@ -0,0 +1,527 @@ +/*************************************************************************** + * File: epicsGeneralTime.c + * Author: Sheng Peng + * Institution: Oak Ridge National Laboratory / SNS Project + * Date: 07/2004 + * Version: 1.3 + * + * EPICS general timestamp support + * integration into EPICS Base by Peter Denison, Diamond Light Source + * + * Copyright (c) 2008 Diamond Light Source Ltd + * Copyright (c) 2004 Oak Ridge National Laboratory + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + ****************************************************************************/ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "generalTimeSup.h" +#include "epicsGeneralTime.h" +#define GENERALTIME_VERSION "GeneralTime Framework Version 1.2" + +#define TP_DESC_LEN 40 +#define NUM_OF_EVENTS 256 + +typedef struct TIME_CURRENT_PROVIDER { + ELLNODE node; /* Linked List Node */ + char tp_desc[TP_DESC_LEN]; + pepicsTimeGetCurrent tcp_getCurrent; + int tcp_priority; +} TIME_CURRENT_PROVIDER; + +typedef struct TIME_EVENT_PROVIDER { + ELLNODE node; /* Linked List Node */ + char tp_desc[TP_DESC_LEN]; + pepicsTimeGetEvent tep_getEvent; + int tep_priority; +} TIME_EVENT_PROVIDER; + +typedef struct generalTimePvt +{ + epicsMutexId tcp_list_sem; /* This is a mutex semaphore to protect time-current-provider-list operation */ + ELLLIST tcp_list; /* time current provider list */ + TIME_CURRENT_PROVIDER *pLastKnownBestTcp; + epicsTimeStamp lastKnownTimeCurrent; + + epicsMutexId tep_list_sem; /* This is a mutex semaphore to protect time-event-provider-list operation */ + ELLLIST tep_list; /* time event provider list */ + TIME_EVENT_PROVIDER *pLastKnownBestTep; + epicsTimeStamp lastKnownTimeEvent[NUM_OF_EVENTS]; + epicsTimeStamp lastKnownTimeEventBestTime; + + epicsTimerQueueId sync_queue; + unsigned int ErrorCounts; +} generalTimePvt; + +int generalTimeGetCurrent(epicsTimeStamp *pDest); +static int generalTimeGetCurrentPriority(epicsTimeStamp *pDest, int * pPriority); +int generalTimeGetEvent(epicsTimeStamp *pDest,int eventNumber); +static int generalTimeGetEventPriority(epicsTimeStamp *pDest,int eventNumber, int * pPriority); + +long generalTimeReport(int level); + +static generalTimePvt *pgeneralTimePvt=NULL; +static char logBuffer[160]; + +int GENERALTIME_DEBUG=0; + +/* implementation */ +void generalTime_InitOnce(void) +{ + pgeneralTimePvt = (generalTimePvt *)callocMustSucceed(1,sizeof(generalTimePvt),"generalTime_Init"); + + ellInit(&(pgeneralTimePvt->tcp_list)); + pgeneralTimePvt->tcp_list_sem = epicsMutexMustCreate(); + + ellInit(&(pgeneralTimePvt->tep_list)); + pgeneralTimePvt->tep_list_sem = epicsMutexMustCreate(); + + /* Initialise the "last-resort" provider on a per-architecture basis */ + osdTimeInit(); +} + +void generalTime_Init(void) +{ + /* We must only initialise generalTime once */ + static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; + + epicsThreadOnce(&onceId, (EPICSTHREADFUNC)generalTime_InitOnce, NULL); +} + +int epicsTimeGetCurrent(epicsTimeStamp *pDest) +{ + int priority; + return(generalTimeGetCurrentPriority(pDest, &priority)); +} + +static int generalTimeGetCurrentPriority(epicsTimeStamp *pDest, int * pPriority) +{ + return(generalTimeGetExceptPriority(pDest, pPriority, 0)); /* no tcp is excluded */ + +} + +int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int * pPriority, int except_tcp) +{ + int key; + TIME_CURRENT_PROVIDER * ptcp; + int status = epicsTimeERROR; + if(!pgeneralTimePvt) + generalTime_Init(); + + epicsMutexMustLock( pgeneralTimePvt->tcp_list_sem ); + for(ptcp=(TIME_CURRENT_PROVIDER *)ellFirst( &(pgeneralTimePvt->tcp_list) ); + ptcp; ptcp=(TIME_CURRENT_PROVIDER *)ellNext((ELLNODE *)ptcp)) + {/* when we register providers, we sort them by priority */ + /* Allow providers to ask for time not including their own */ + if (ptcp->tcp_priority == except_tcp) { + continue; + } + if(ptcp->tcp_getCurrent) + status=(*(ptcp->tcp_getCurrent))(pDest); + if(status!=epicsTimeERROR) + {/* we can check if time monotonic here */ + if (epicsTimeGreaterThanEqual(pDest, &(pgeneralTimePvt->lastKnownTimeCurrent))) { + pgeneralTimePvt->lastKnownTimeCurrent=*pDest; + pgeneralTimePvt->pLastKnownBestTcp=ptcp; + *pPriority = ptcp->tcp_priority; + } else { + epicsTimeStamp orig = *pDest; + *pDest=pgeneralTimePvt->lastKnownTimeCurrent; + key = epicsInterruptLock(); /* OSI has no taskLock, but if we lock interrupt, it is good enough */ + pgeneralTimePvt->ErrorCounts++; + epicsInterruptUnlock(key); /* OSI has no taskLock, but if we lock interrupt, it is good enough */ + if(GENERALTIME_DEBUG) { + sprintf(logBuffer, "TCP provider \"%s\" provides backwards time!\nnew = %us, %uns, last = %us, %uns (%s)\n", + ptcp->tp_desc,orig.secPastEpoch,orig.nsec,pDest->secPastEpoch,pDest->nsec, + pgeneralTimePvt->pLastKnownBestTcp->tp_desc); + epicsInterruptContextMessage(logBuffer); + } + *pPriority = pgeneralTimePvt->pLastKnownBestTcp->tcp_priority; + } + break; + } + } + if(status==epicsTimeERROR) + pgeneralTimePvt->pLastKnownBestTcp=NULL; + epicsMutexUnlock( pgeneralTimePvt->tcp_list_sem ); + + return status; +} + +int epicsTimeGetEvent(epicsTimeStamp *pDest,int eventNumber) +{ + int priority; + if (eventNumber == epicsTimeEventCurrentTime) { + return generalTimeGetCurrentPriority(pDest, &priority); + } else { + return(generalTimeGetEventPriority(pDest, eventNumber, &priority)); + } +} + +static int generalTimeGetEventPriority(epicsTimeStamp *pDest,int eventNumber, int * pPriority) +{ + int key; + TIME_EVENT_PROVIDER * ptep; + int status = epicsTimeERROR; + if(!pgeneralTimePvt) + generalTime_Init(); + + epicsMutexMustLock( pgeneralTimePvt->tep_list_sem ); + for(ptep=(TIME_EVENT_PROVIDER *)ellFirst( &(pgeneralTimePvt->tep_list) ); + ptep; ptep=(TIME_EVENT_PROVIDER *)ellNext((ELLNODE *)ptep)) + {/* when we register provider, we sort them by priority */ + if(ptep->tep_getEvent) + status=(*(ptep->tep_getEvent))(pDest,eventNumber); + if(status!=epicsTimeERROR) + {/* we can check if time monotonic here */ + pgeneralTimePvt->pLastKnownBestTep=ptep; + *pPriority = ptep->tep_priority; + if(eventNumber>=0 && eventNumberlastKnownTimeEvent[eventNumber]))) { + pgeneralTimePvt->lastKnownTimeEvent[eventNumber]=*pDest; + } else { + *pDest=pgeneralTimePvt->lastKnownTimeEvent[eventNumber]; + key = epicsInterruptLock(); /* OSI has no taskLock, but if we lock interrupt, it is good enough */ + pgeneralTimePvt->ErrorCounts++; + epicsInterruptUnlock(key); /* OSI has no taskLock, but if we lock interrupt, it is good enough */ + if(GENERALTIME_DEBUG) { + sprintf(logBuffer, "TEP provider \"%s\" provides backwards time on Event %d!\n", ptep->tp_desc, eventNumber); + epicsInterruptContextMessage(logBuffer); + } + } + } + if(eventNumber==epicsTimeEventBestTime) + { + if (epicsTimeGreaterThanEqual(pDest,&(pgeneralTimePvt->lastKnownTimeEventBestTime))) { + pgeneralTimePvt->lastKnownTimeEventBestTime=*pDest; + } else { + *pDest=pgeneralTimePvt->lastKnownTimeEventBestTime; + key = epicsInterruptLock(); /* OSI has no taskLock, but if we lock interrupt, it is good enough */ + pgeneralTimePvt->ErrorCounts++; + epicsInterruptUnlock(key); /* OSI has no taskLock, but if we lock interrupt, it is good enough */ + if(GENERALTIME_DEBUG) { + sprintf(logBuffer, "TEP provider \"%s\" provides backwards time on Event BestTime!\n", ptep->tp_desc); + epicsInterruptContextMessage(logBuffer); + } + } + } + break; + } + } + if(status==epicsTimeERROR) + pgeneralTimePvt->pLastKnownBestTep=NULL; + epicsMutexUnlock( pgeneralTimePvt->tep_list_sem ); + + return status; +} + +/* + * For all the similarity of the following two functions, they are + * walking separate lists, and assigning to structures of different + * types, all because of the difference in function prototype between + * the getCurrent and getEvent functions. While they could be templated, + * I think it's simpler to leave the duplication there. + */ + +int generalTimeEventTpRegister(const char *desc, int priority, pepicsTimeGetEvent getEvent) +{ + TIME_EVENT_PROVIDER * ptep, * ptepref; + + if(!pgeneralTimePvt) + generalTime_Init(); + + ptep=(TIME_EVENT_PROVIDER *)malloc(sizeof(TIME_EVENT_PROVIDER)); + + if(ptep==NULL) { + return epicsTimeERROR; + } + + strncpy(ptep->tp_desc,desc,TP_DESC_LEN-1); + ptep->tp_desc[TP_DESC_LEN-1]='\0'; + ptep->tep_priority=priority; + ptep->tep_getEvent=getEvent; + + epicsMutexMustLock( pgeneralTimePvt->tep_list_sem ); + for(ptepref=(TIME_EVENT_PROVIDER *)ellFirst( &(pgeneralTimePvt->tep_list) ); + ptepref; ptepref=(TIME_EVENT_PROVIDER *)ellNext((ELLNODE *)ptepref)) + { + if(ptepref->tep_priority > ptep->tep_priority) break; + } + if(ptepref) + {/* found a ref whose priority is just bigger than the new one */ + ptepref=(TIME_EVENT_PROVIDER *)ellPrevious((ELLNODE *)ptepref); + ellInsert( &(pgeneralTimePvt->tep_list), (ELLNODE *)ptepref, (ELLNODE *)ptep ); + } + else + {/* either list is empty or no one have bigger(lower) priority, put on tail */ + ellAdd( &(pgeneralTimePvt->tep_list), (ELLNODE *)ptep ); + } + epicsMutexUnlock( pgeneralTimePvt->tep_list_sem ); + + return epicsTimeOK; +} + +int generalTimeCurrentTpRegister(const char *desc, int priority, pepicsTimeGetCurrent getCurrent) +{ + TIME_CURRENT_PROVIDER * ptcp, * ptcpref; + + if(!pgeneralTimePvt) + generalTime_Init(); + + ptcp=(TIME_CURRENT_PROVIDER *)malloc(sizeof(TIME_CURRENT_PROVIDER)); + + if(ptcp==NULL) { + return epicsTimeERROR; + } + + strncpy(ptcp->tp_desc,desc,TP_DESC_LEN-1); + ptcp->tp_desc[TP_DESC_LEN-1]='\0'; + ptcp->tcp_priority=priority; + ptcp->tcp_getCurrent=getCurrent; + + epicsMutexMustLock( pgeneralTimePvt->tcp_list_sem ); + for(ptcpref=(TIME_CURRENT_PROVIDER *)ellFirst( &(pgeneralTimePvt->tcp_list) ); + ptcpref; ptcpref=(TIME_CURRENT_PROVIDER *)ellNext((ELLNODE *)ptcpref)) + { + if(ptcpref->tcp_priority > ptcp->tcp_priority) break; + } + if(ptcpref) + {/* found a ref whose priority is just bigger than the new one */ + ptcpref=(TIME_CURRENT_PROVIDER *)ellPrevious((ELLNODE *)ptcpref); + ellInsert( &(pgeneralTimePvt->tcp_list), (ELLNODE *)ptcpref, (ELLNODE *)ptcp ); + } + else + {/* either list is empty or no one have bigger(lower) priority, put on tail */ + ellAdd( &(pgeneralTimePvt->tcp_list), (ELLNODE *)ptcp ); + } + epicsMutexUnlock( pgeneralTimePvt->tcp_list_sem); + + return epicsTimeOK; +} + +/* + * Provide an optional "last resort" provider for Event Time. + * + * This is deliberately optional, as it represents site policy. + * It is intended to be installed as an EventTime provider at the lowest + * priority, to return the current time for an event if there is no + * better time provider for event times. + * + * Typically, this will only be used during startup, or a time-provider + * resynchronisation, where events are being generated by the event system + * but the time provided by the system is not yet valid. + */ +static int lastResortGetEvent(epicsTimeStamp *timeStamp, int eventNumber) +{ + return epicsTimeGetCurrent(timeStamp); +} + +int lastResortEventProviderInstall(void) +{ + return generalTimeEventTpRegister("Last Resort Event", LAST_RESORT_PRIORITY, lastResortGetEvent); +} + +epicsTimerId generalTimeCreateSyncTimer(epicsTimerCallback cb, void* param) +{ + if (!pgeneralTimePvt->sync_queue) { + pgeneralTimePvt->sync_queue = epicsTimerQueueAllocate(0, epicsThreadPriorityHigh); + } + return epicsTimerQueueCreateTimer(pgeneralTimePvt->sync_queue, cb, param); +} + +long generalTimeReport(int level) +{ + TIME_CURRENT_PROVIDER * ptcp; + TIME_EVENT_PROVIDER * ptep; + + int status; + epicsTimeStamp tempTS; + char tempTSText[40]; + + int items; /* how many provider we have */ + char * ptempText; /* logMsg passed pointer instead of strcpy, so we have to keep a local screen copy then printf */ + int tempTextOffset; + + printf(GENERALTIME_VERSION"\n"); + + if(!pgeneralTimePvt) + {/* GeneralTime is not used, we just report version then quit */ + printf("General time framework is not initialized yet!\n\n"); + return epicsTimeOK; + } + + /* GeneralTime is in use, we try to report more detail */ + + /* we use sprintf instead of printf because we don't want to hold xxx_list_sem too long */ + + if(level>0) + { + printf("\nFor Time-Current-Provider:\n"); + epicsMutexMustLock( pgeneralTimePvt->tcp_list_sem ); + if(( items = ellCount( &(pgeneralTimePvt->tcp_list)) )) + { + ptempText = (char *)malloc(items * 80 * 3); /* for each provider, we print 3 lines, and each line is less then 80 bytes !!!!!!!! */ + if(!ptempText) + {/* malloc failed */ + epicsMutexUnlock( pgeneralTimePvt->tcp_list_sem ); + printf("Malloced memory for print out for %d tcps failed!\n", items); + return epicsTimeERROR; + } + if(GENERALTIME_DEBUG) printf("Malloced memory for print out for %d tcps\n", items); + + bzero(ptempText, items*80*3); + tempTextOffset = 0; + + for(ptcp=(TIME_CURRENT_PROVIDER *)ellFirst( &(pgeneralTimePvt->tcp_list) ); + ptcp; ptcp=(TIME_CURRENT_PROVIDER *)ellNext((ELLNODE *)ptcp)) + { + tempTextOffset += sprintf(ptempText+tempTextOffset, "\t\"%s\",priority %d\n", ptcp->tp_desc, ptcp->tcp_priority); + if(level>1) + { + tempTextOffset += sprintf( ptempText+tempTextOffset, "\t getCurrent is 0x%lx\n", + (unsigned long)(ptcp->tcp_getCurrent) ); + if(ptcp->tcp_getCurrent) + { + status=(*(ptcp->tcp_getCurrent))(&tempTS); + if(status!=epicsTimeERROR) + { + tempTSText[0]='\0'; + epicsTimeToStrftime(tempTSText, sizeof(tempTSText), "%Y/%m/%d %H:%M:%S.%06f",&tempTS); + tempTextOffset += sprintf(ptempText+tempTextOffset, "\t Current Time is %s!\n", tempTSText); + } + else + { + tempTextOffset += sprintf(ptempText+tempTextOffset, "\t Time Current Provider \"%s\" Failed!\n", ptcp->tp_desc); + } + } + } + } + epicsMutexUnlock( pgeneralTimePvt->tcp_list_sem ); + printf("%s", ptempText); + free(ptempText); + } + else + { + epicsMutexUnlock( pgeneralTimePvt->tcp_list_sem ); + printf("\tNo Time-Current-Provider registered!\n"); + } + + printf("For Time-Event-Provider:\n"); + epicsMutexMustLock( pgeneralTimePvt->tep_list_sem ); + if(( items = ellCount( &(pgeneralTimePvt->tep_list) ) )) + { + ptempText = (char *)malloc(items * 80 * 2); /* for each provider, we print 2 lines, and each line is less then 80 bytes !!!!!!!! */ + if(!ptempText) + {/* malloc failed */ + epicsMutexUnlock( pgeneralTimePvt->tep_list_sem ); + printf("Malloced memory for print out for %d teps failed!\n", items); + return epicsTimeERROR; + } + if(GENERALTIME_DEBUG) printf("Malloced memory for print out for %d teps\n", items); + + bzero(ptempText, items*80*2); + tempTextOffset = 0; + + for(ptep=(TIME_EVENT_PROVIDER *)ellFirst( &(pgeneralTimePvt->tep_list) ); + ptep; ptep=(TIME_EVENT_PROVIDER *)ellNext((ELLNODE *)ptep)) + { + tempTextOffset += sprintf(ptempText+tempTextOffset,"\t\"%s\",priority %d\n",ptep->tp_desc,ptep->tep_priority); + if(level>1) + tempTextOffset += sprintf( ptempText+tempTextOffset, "\t getEvent is 0x%lx\n",(unsigned long)(ptep->tep_getEvent) ); + } + epicsMutexUnlock( pgeneralTimePvt->tep_list_sem ); + printf("%s", ptempText); + free(ptempText); + } + else + { + epicsMutexUnlock( pgeneralTimePvt->tep_list_sem ); + printf("\tNo Time-Event-Provider registered!\n"); + } + + printf("\n"); + } + return epicsTimeOK; +} + +/* + * The following functions are accessors for various internal values, so that + * they can be made available to device support. They are used by the + * devGeneralTime.c file in /src/dev/softDev which implements the + * 'generalTime' DTYP for ai, bo, longin and stringin records + */ + +int generalTimeGetCurrentDouble(double * pseconds) /* for ai record, seconds from 01/01/1990 */ +{ + epicsTimeStamp ts; + if(epicsTimeERROR!=epicsTimeGetCurrent(&ts)) + { + *pseconds=ts.secPastEpoch+((double)(ts.nsec))*1e-9; + return epicsTimeOK; + } + else + return epicsTimeERROR; +} + +void generalTimeResetErrorCounts() /* for bo record */ +{ + if(!pgeneralTimePvt) + generalTime_Init(); + pgeneralTimePvt->ErrorCounts=0; +} + +int generalTimeGetErrorCounts() /* for longin record */ +{ + if(!pgeneralTimePvt) + generalTime_Init(); + return pgeneralTimePvt->ErrorCounts; +} + +void generalTimeGetBestTcp(char * desc) /* for stringin record */ +{/* the assignment to pLastKnownBestTcp is atomic and desc is never changed after registeration */ + if(!pgeneralTimePvt) + generalTime_Init(); + if(pgeneralTimePvt->pLastKnownBestTcp) + {/* We know some good tcp */ + strncpy(desc,pgeneralTimePvt->pLastKnownBestTcp->tp_desc,TP_DESC_LEN-1); + desc[TP_DESC_LEN-1]='\0'; + } + else + {/* no good tcp */ + strncpy(desc,"No Good Time-Current-Provider",TP_DESC_LEN-1); + desc[TP_DESC_LEN-1]='\0'; + } +} + +void generalTimeGetBestTep(char * desc) /* for stringin record */ +{/* the assignment to pLastKnownBestTep is atomic and desc is never changed after registeration */ + if(!pgeneralTimePvt) + generalTime_Init(); + if(pgeneralTimePvt->pLastKnownBestTep) + {/* We know some good tep */ + strncpy(desc,pgeneralTimePvt->pLastKnownBestTep->tp_desc,TP_DESC_LEN-1); + desc[TP_DESC_LEN-1]='\0'; + } + else + {/* no good tep */ + strncpy(desc,"No Good Time-Event-Provider",TP_DESC_LEN-1); + desc[TP_DESC_LEN-1]='\0'; + } +} diff --git a/src/libCom/osi/epicsGeneralTime.h b/src/libCom/osi/epicsGeneralTime.h new file mode 100644 index 000000000..aab08315f --- /dev/null +++ b/src/libCom/osi/epicsGeneralTime.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * File: epicsGeneralTime.h + * Author: Sheng Peng + * Institution: Oak Ridge National Laboratory / SNS Project + * Date: 07/2004 + * Version: 1.2 + * + * general EPICS timestamp support API + * This is the interface to generalTime for all "users", rather than + * Time Providers. + * + * Integration into EPICS Base by Peter Denison, Diamond Light Source + * + * Copyright (c) 2008 Diamond Light Source Ltd + * Copyright (c) 2004 Oak Ridge National Laboratory + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + ****************************************************************************/ +#ifndef _INC_epicsGeneralTime +#define _INC_epicsGeneralTime + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void generalTime_Init(void); /* this is the init routine you can call explicitly in st.cmd */ +int lastResortEventProviderInstall(void); +int generalTimeGetCurrentDouble(double * pseconds); /* for ai record, seconds from 01/01/1990 */ +void generalTimeResetErrorCounts(); /* for bo record */ +int generalTimeGetErrorCounts(); /* for longin record */ +void generalTimeGetBestTcp(char * desc); /* for stringin record */ +void generalTimeGetBestTep(char * desc); /* for stringin record */ + +long generalTimeReport(int interest); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/libCom/osi/epicsTime.cpp b/src/libCom/osi/epicsTime.cpp index fcec5066e..43d37d792 100644 --- a/src/libCom/osi/epicsTime.cpp +++ b/src/libCom/osi/epicsTime.cpp @@ -52,7 +52,6 @@ static const unsigned nSecFracDigits = 9u; // Timescale conversion data -static const unsigned int POSIX_TIME_AT_EPICS_EPOCH = 631152000u; static const unsigned long NTP_TIME_AT_POSIX_EPOCH = 2208988800ul; static const unsigned long NTP_TIME_AT_EPICS_EPOCH = NTP_TIME_AT_POSIX_EPOCH + POSIX_TIME_AT_EPICS_EPOCH; @@ -87,7 +86,12 @@ epicsTimeLoadTimeInit::epicsTimeLoadTimeInit () time_t t_one = static_cast (1); this->time_tSecPerTick = difftime (t_one, t_zero); - /* Convert our epoch offset into time_t units */ + /* The EPICS epoch (1/1/1990 00:00:00UTC) was 631152000 seconds after + * the ANSI epoch (1/1/1970 00:00:00UTC) + * Convert this offset into time_t units, however this must not be + * calculated using local time (i.e. using mktime() or similar), since + * in the UK the ANSI Epoch had daylight saving time in effect, and + * the value calculated would be 3600 seconds wrong.*/ this->epicsEpochOffset = (double) POSIX_TIME_AT_EPICS_EPOCH / this->time_tSecPerTick; diff --git a/src/libCom/osi/epicsTime.h b/src/libCom/osi/epicsTime.h index bc8a5604d..92f9ea15b 100644 --- a/src/libCom/osi/epicsTime.h +++ b/src/libCom/osi/epicsTime.h @@ -19,6 +19,9 @@ #include "epicsTypes.h" #include "osdTime.h" +/* The EPICS Epoch is 00:00:00 Jan 1, 1990 UTC */ +#define POSIX_TIME_AT_EPICS_EPOCH 631152000u + /* epics time stamp for C interface*/ typedef struct epicsTimeStamp { epicsUInt32 secPastEpoch; /* seconds since 0000 Jan 1, 1990 */ @@ -182,10 +185,14 @@ extern "C" { #define epicsTimeEventBestTime -1 #define epicsTimeEventDeviceTime -2 +/* These are now provided by the "generalTime" framework */ epicsShareFunc int epicsShareAPI epicsTimeGetCurrent ( epicsTimeStamp * pDest ); epicsShareFunc int epicsShareAPI epicsTimeGetEvent ( epicsTimeStamp *pDest, int eventNumber); +/* Provide a way of initialising the Time Provider of "last resort" */ +epicsShareFunc int epicsShareAPI osdTimeInit ( void ); + /* convert to and from ANSI C's "time_t" */ epicsShareFunc int epicsShareAPI epicsTimeToTime_t ( time_t * pDest, const epicsTimeStamp * pSrc ); diff --git a/src/libCom/osi/generalTimeSup.h b/src/libCom/osi/generalTimeSup.h new file mode 100644 index 000000000..0a006bac4 --- /dev/null +++ b/src/libCom/osi/generalTimeSup.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * File: generalTimeSup.h + * Author: Peter Denison + * Institution: Diamond Light Source + * Date: 04/2008 + * Version: 1.1 + * + * support for general EPICS timestamp support + * + * This file should be included by Time Providers and contains the + * interface to the "lower" side of the generalTime framework + * + * Copyright (c) 2008 Diamond Light Source Ltd + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + *************************************************************************/ +#ifndef generalTimeSuphInclude +#define generalTimeSuphInclude + +#define LAST_RESORT_PRIORITY 999 +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*pepicsTimeGetCurrent)(epicsTimeStamp *pDest); +typedef int (*pepicsTimeGetEvent)(epicsTimeStamp *pDest,int eventNumber); + +int generalTimeCurrentTpRegister(const char * desc, int priority, pepicsTimeGetCurrent getCurrent); +int generalTimeEventTpRegister(const char * desc, int priority, pepicsTimeGetEvent getEvent); +int generalTimeGetExceptPriority(epicsTimeStamp *pDest, int * pPriority, int except_tcp); +epicsTimerId generalTimeCreateSyncTimer(epicsTimerCallback cb, void *param); + +#ifdef __cplusplus +} +#endif +#endif /*generalTimeSuphInclude*/ diff --git a/src/libCom/osi/os/RTEMS/iocClock.c b/src/libCom/osi/os/RTEMS/iocClock.c deleted file mode 100644 index a544421dd..000000000 --- a/src/libCom/osi/os/RTEMS/iocClock.c +++ /dev/null @@ -1,198 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne -* National Laboratory. -* Copyright (c) 2002 The Regents of the University of California, as -* Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ -/* iocClock.c */ - -/* Author: Marty Kraimer Date: 16JUN2000 */ -/*l Modified by Eric Norum to work with RTEMS */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BILLION 1000000000 -#define iocClockSyncRate 10.0 - -static int iocClockGetCurrent(epicsTimeStamp *pDest); -static int iocClockGetEvent(epicsTimeStamp *pDest, int eventNumber); - -typedef struct iocClockPvt { - epicsMutexId lock; - epicsTimeStamp clock; - unsigned long lastTick; - epicsUInt32 nanosecondsPerTick; - int tickRate; - int ticksToSkip; - pepicsTimeGetCurrent getCurrent; - pepicsTimeGetEvent getEvent; -}iocClockPvt; -static iocClockPvt *piocClockPvt = 0; -static int nConsecutiveBad = 0; - -static int -tickGet(void) -{ - rtems_interval t; - rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &t); - return t; -} -static int -sysClkRateGet(void) -{ - rtems_interval t; - rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &t); - return t; -} - -static void syncNTP(void) -{ - struct timespec Currtime; - epicsTimeStamp epicsTime; - int status; - int prevStatusBad = 0; - int firstTime=1; - - while(1) { - double diffTime; - extern int rtems_bsdnet_get_ntp(int, int(*)(), struct timespec *); - if(!firstTime)epicsThreadSleep(iocClockSyncRate); - firstTime = 0; - if(piocClockPvt->getCurrent != iocClockGetCurrent) { - errlogPrintf("syncNTP: NTP client terminating.\n"); - return; - } - status = rtems_bsdnet_get_ntp(-1, NULL, &Currtime); - if(status) { - ++nConsecutiveBad; - /*wait 1 minute before reporting failure*/ - if(nConsecutiveBad<(60/iocClockSyncRate)) continue; - if(!prevStatusBad) - errlogPrintf("iocClockSyncWithNTPserver: sntpcTimeGet %s\n", - strerror(errno)); - prevStatusBad = 1; - continue; - } - nConsecutiveBad=0; - if(prevStatusBad) { - errlogPrintf("iocClockSyncWithNTPserver: sntpcTimeGet OK\n"); - prevStatusBad = 0; - } - epicsTimeFromTimespec(&epicsTime,&Currtime); - epicsMutexMustLock(piocClockPvt->lock); - diffTime = epicsTimeDiffInSeconds(&epicsTime,&piocClockPvt->clock); - if(diffTime>=0.0) { - piocClockPvt->clock = epicsTime; - } else {/*dont go back in time*/ - piocClockPvt->ticksToSkip = (int) (diffTime*piocClockPvt->tickRate); - } - piocClockPvt->lastTick = tickGet(); - epicsMutexUnlock(piocClockPvt->lock); - } -} - -void iocClockInit() -{ - if(piocClockPvt) return; - piocClockPvt = callocMustSucceed(1,sizeof(iocClockPvt),"iocClockInit"); - piocClockPvt->lock = epicsMutexCreate(); - piocClockPvt->nanosecondsPerTick = BILLION/sysClkRateGet(); - piocClockPvt->tickRate = sysClkRateGet(); - piocClockPvt->getCurrent = iocClockGetCurrent; - piocClockPvt->getEvent = iocClockGetEvent; - epicsThreadCreate("syncNTP", - epicsThreadPriorityHigh, - epicsThreadGetStackSize(epicsThreadStackSmall), - (EPICSTHREADFUNC)syncNTP,0); - return; -} - -void iocClockRegister(pepicsTimeGetCurrent getCurrent, - pepicsTimeGetEvent getEvent) -{ - if(piocClockPvt) - printf("iocClockRegister: iocClock already initialized -- overriding\n"); - else - piocClockPvt = callocMustSucceed(1,sizeof(iocClockPvt),"iocClockRegister"); - piocClockPvt->getCurrent = getCurrent; - piocClockPvt->getEvent = getEvent; -} - -int iocClockGetCurrent(epicsTimeStamp *pDest) -{ - unsigned long currentTick,nticks,nsecs; - - epicsMutexMustLock(piocClockPvt->lock); - currentTick = tickGet(); - while(currentTick!=piocClockPvt->lastTick) { - nticks = (currentTick>piocClockPvt->lastTick) - ? (currentTick - piocClockPvt->lastTick) - : (currentTick + (ULONG_MAX - piocClockPvt->lastTick)); - if(piocClockPvt->ticksToSkip>0) {/*dont go back in time*/ - if(nticksticksToSkip) { - piocClockPvt->ticksToSkip -= nticks; - break; - } - nticks -= piocClockPvt->ticksToSkip; - } - piocClockPvt->lastTick = currentTick; - nsecs = nticks/piocClockPvt->tickRate; - nticks = nticks - nsecs*piocClockPvt->tickRate; - piocClockPvt->clock.nsec += nticks * piocClockPvt->nanosecondsPerTick; - if(piocClockPvt->clock.nsec>=BILLION) { - ++nsecs; - piocClockPvt->clock.nsec -= BILLION; - } - piocClockPvt->clock.secPastEpoch += nsecs; - } - *pDest = piocClockPvt->clock; - epicsMutexUnlock(piocClockPvt->lock); - return(0); -} - -int iocClockGetEvent(epicsTimeStamp *pDest, int eventNumber) -{ - if (eventNumber==epicsTimeEventCurrentTime) { - *pDest = piocClockPvt->clock; - return(0); - } - return(epicsTimeERROR); -} - -int epicsTimeGetCurrent (epicsTimeStamp *pDest) -{ - if(!piocClockPvt) { - iocClockInit(); - /*wait two seconds for syncNTP to contact network time server*/ - epicsThreadSleep(2.0); - } - if(piocClockPvt->getCurrent) return((*piocClockPvt->getCurrent)(pDest)); - return(epicsTimeERROR); -} - -int epicsTimeGetEvent (epicsTimeStamp *pDest, int eventNumber) -{ - if(!piocClockPvt) { - iocClockInit(); - /*wait two seconds for syncNTP to contact network time server*/ - epicsThreadSleep(2.0); - } - if(piocClockPvt->getEvent) - return((*piocClockPvt->getEvent)(pDest,eventNumber)); - return(epicsTimeERROR); -} - diff --git a/src/libCom/osi/os/RTEMS/iocClock.h b/src/libCom/osi/os/RTEMS/iocClock.h deleted file mode 100644 index e515bbb72..000000000 --- a/src/libCom/osi/os/RTEMS/iocClock.h +++ /dev/null @@ -1,28 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne -* National Laboratory. -* Copyright (c) 2002 The Regents of the University of California, as -* Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ -/* iocClock.h */ - -/* Author: Marty Kraimer Date: 16JUN2000 */ - -#include "epicsTime.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef int (*pepicsTimeGetCurrent)(epicsTimeStamp *pDest); -typedef int (*pepicsTimeGetEvent)(epicsTimeStamp *pDest,int eventNumber); - -void iocClockInit(void); -void iocClockRegister(pepicsTimeGetCurrent getCurrent,pepicsTimeGetEvent getEvent); - -#ifdef __cplusplus -} -#endif diff --git a/src/libCom/osi/os/RTEMS/osdTime.cpp b/src/libCom/osi/os/RTEMS/osdTime.cpp index 37c05f0da..6dd0aa4be 100644 --- a/src/libCom/osi/os/RTEMS/osdTime.cpp +++ b/src/libCom/osi/os/RTEMS/osdTime.cpp @@ -11,24 +11,43 @@ */ // -/* - * ANSI C - */ -#include -/*#include */ +#include "epicsTime.h" +#include "osiNTPTime.h" +#include "osdSysTime.h" +#include "generalTimeSup.h" -/* - * RTEMS -#include - */ +extern int rtems_bsdnet_get_ntp(int, int(*)(), struct timespec *); -/* - * EPICS - */ -#define epicsExportSharedSymbols -#include +extern "C" epicsShareFunc int epicsShareAPI osdTimeInit(void) +{ + NTPTime_Init(100); /* init NTP first so it can be used to sync VW */ + SysTime_Init(LAST_RESORT_PRIORITY); + return epicsTimeOK; +} -extern "C" { +int osdNTPGet(struct timespec *ts) +{ + return rtems_bsdnet_get_ntp(-1, NULL, ts); +} + +void osdNTPInit(void) +{ +} + +int +tickGet(void) +{ + rtems_interval t; + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &t); + return t; +} + +int sysClkRateGet(void) +{ + rtems_interval t; + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &t); + return t; +} int epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM ) { @@ -51,5 +70,3 @@ int epicsTime_localtime ( const time_t *clock, struct tm *result ) return epicsTimeERROR; } } - -} /* extern "C" */ diff --git a/src/libCom/osi/os/RTEMS/osdTime.h b/src/libCom/osi/os/RTEMS/osdTime.h index 2c2ce5218..bbe2d57cd 100644 --- a/src/libCom/osi/os/RTEMS/osdTime.h +++ b/src/libCom/osi/os/RTEMS/osdTime.h @@ -16,6 +16,16 @@ #ifndef osdTimeh #define osdTimeh -/* NOOP */ +#ifdef __cplusplus +extern "C" { +#endif +int osdNTPGet(struct timespec *); +void osdNTPInit(void); +int sysClkRateGet(void); +int tickGet(void); + +#ifdef __cplusplus +} +#endif #endif /* ifndef osdTimeh */ diff --git a/src/libCom/osi/os/VMS/osdTime.cpp b/src/libCom/osi/os/VMS/osdTime.cpp index 59368e9a0..e5b9703c4 100644 --- a/src/libCom/osi/os/VMS/osdTime.cpp +++ b/src/libCom/osi/os/VMS/osdTime.cpp @@ -16,11 +16,12 @@ #define epicsExportSharedSymbols #include "epicsTime.h" +#include "generalTimeSup.h" // // epicsTime::osdGetCurrent () // -extern "C" epicsShareFunc int epicsTimeGetCurrent (epicsTimeStamp *pDest) +int osdTimeGetCurrent (epicsTimeStamp *pDest) { # if defined(CLOCK_REALTIME) struct timespec ts; @@ -45,15 +46,14 @@ extern "C" epicsShareFunc int epicsTimeGetCurrent (epicsTimeStamp *pDest) # endif } -// -// epicsTimeGetEvent () -// -extern "C" epicsShareFunc int epicsShareAPI epicsTimeGetEvent (epicsTimeStamp *pDest, int eventNumber) +extern "C" epicsShareFunc int epicsShareAPI osdTimeInit (void) { - if (eventNumber==epicsTimeEventCurrentTime) { - return epicsTimeGetCurrent (pDest); - } - else { - return epicsTimeERROR; - } +#if defined (CLOCK_REALTIME) + const char name[] = "gettimeofday"; +#else + const char name[] = "clock_gettime"; +#endif + + generalTimeCurrentTpRegister(name, 150, osdTimeGetCurrent); + return epicsTimeOK; } diff --git a/src/libCom/osi/os/WIN32/osdTime.cpp b/src/libCom/osi/os/WIN32/osdTime.cpp index 5e75c9bbd..f5b5f8336 100644 --- a/src/libCom/osi/os/WIN32/osdTime.cpp +++ b/src/libCom/osi/os/WIN32/osdTime.cpp @@ -45,6 +45,8 @@ # define debugPrintf(argsInParen) #endif +static int osdTimeGetCurrent ( epicsTimeStamp *pDest ); + // GNU seems to require that 64 bit constants have LL on // them. The borland compiler fails to compile constants // with the LL suffix. MS compiler doesnt care. @@ -91,7 +93,7 @@ static const LONGLONG ET_TICKS_PER_FT_TICK = // // osdTimeInit () // -static void osdTimeInit ( void * ) +extern "C" epicsShareFunc int epicsShareAPI osdTimeInit ( void ) { static bool osdTimeInitSuccess = false; @@ -103,15 +105,15 @@ static void osdTimeInit ( void * ) // bypass recursion crowbar in epicsThreadOnce osdTimeOnceFlag = 1; pCurrentTime->startPLL (); + generalTimeCurrentTpRegister("PerfCounter", 150, osdTimeGetCurrent); } } // -// epicsTimeGetCurrent () +// osdTimeGetCurrent () // -extern "C" epicsShareFunc int epicsShareAPI epicsTimeGetCurrent ( epicsTimeStamp *pDest ) +static int osdTimeGetCurrent ( epicsTimeStamp *pDest ) { - epicsThreadOnce ( & osdTimeOnceFlag, osdTimeInit, 0 ); if ( ! pCurrentTime ) { return epicsTimeERROR; } @@ -121,20 +123,6 @@ extern "C" epicsShareFunc int epicsShareAPI epicsTimeGetCurrent ( epicsTimeStamp return epicsTimeOK; } -// -// epicsTimeGetEvent () -// -extern "C" epicsShareFunc int epicsShareAPI epicsTimeGetEvent - ( epicsTimeStamp *pDest, int eventNumber ) -{ - if ( eventNumber == epicsTimeEventCurrentTime ) { - return epicsTimeGetCurrent ( pDest ); - } - else { - return epicsTimeERROR; - } -} - inline void UnixTimeToFileTime ( const time_t * pAnsiTime, LPFILETIME pft ) { LONGLONG ll = Int32x32To64 ( *pAnsiTime, 10000000 ) + LL_CONSTANT(116444736000000000); diff --git a/src/libCom/osi/os/default/osdSysTime.c b/src/libCom/osi/os/default/osdSysTime.c new file mode 100644 index 000000000..ed63ff2cc --- /dev/null +++ b/src/libCom/osi/os/default/osdSysTime.c @@ -0,0 +1,185 @@ +/* Sheng Peng @ SNS ORNL 07/2004 */ +/* Peter Denison, Diamond Light Source, Apr 2008 */ +/* Eric Norum, Argonne National Labs, Apr 2008 */ +/* Version 1.3 */ +#include +#include +#include +#include +#include +#include +#include + +#include "epicsTypes.h" +#include "cantProceed.h" +#include "errlog.h" +#include "epicsExport.h" +#include "epicsThread.h" +#include "osdThread.h" +#include "epicsMutex.h" +#include "epicsTime.h" +#include "envDefs.h" + +#define BILLION 1000000000 + +#define SYS_TIME_DRV_VERSION "system Time Driver Version 1.3" +#define SYNC_PERIOD 60.0 + +#include "generalTimeSup.h" + +static void SysTimeSyncTime(void *param); +static int SysTimeGetCurrent(epicsTimeStamp *pDest); + +long SysTime_Report(int level); + +typedef struct SysTimePvt +{ + int synced; /* if never synced, we can't use it */ + epicsMutexId lock; + epicsTimeStamp lastReportedTS; + epicsTimeStamp lastSyncedTS; + struct timespec lastSyncedSysTime; + int priority; + int synced_priority; +} SysTimePvt; + +static SysTimePvt *pSysTimePvt = 0; + +static void SysTimeSyncTime(void *param) +{ + epicsTimeStamp now; + struct timespec sys_time_now; + + for (;;) { + epicsThreadSleep(SYNC_PERIOD); + /* Ask for the best time available, not including ourselves */ + if (generalTimeGetExceptPriority(&now, &pSysTimePvt->synced_priority, + pSysTimePvt->priority) == epicsTimeOK) { + /* It's a good time, we unconditionally sync ourselves to it, as + this driver is the bottom of the heap */ + clock_gettime(CLOCK_REALTIME, &sys_time_now); + + epicsMutexMustLock(pSysTimePvt->lock); + pSysTimePvt->lastSyncedTS = now; + pSysTimePvt->lastSyncedSysTime = sys_time_now; + pSysTimePvt->synced = 1; + epicsMutexUnlock(pSysTimePvt->lock); + } + } +} + +long SysTime_InitOnce(int priority) +{ + pSysTimePvt = callocMustSucceed(1, sizeof(SysTimePvt), "SysTime_Init"); + pSysTimePvt->synced = 0; + pSysTimePvt->lock = epicsMutexCreate(); + pSysTimePvt->priority = priority; + + /* register to link list */ + generalTimeCurrentTpRegister("system Time", priority, SysTimeGetCurrent); + + /* Create the synchronization thread */ + epicsThreadCreate("System Time Sync", epicsThreadPriorityLow, + epicsThreadGetStackSize(epicsThreadStackSmall), SysTimeSyncTime, + NULL); + return 0; +} + +struct InitInfo +{ + int priority; + long retval; +}; +static void SysTime_InitOnceWrapper(void *arg) +{ + struct InitInfo *pargs = (struct InitInfo *)arg; + pargs->retval = SysTime_InitOnce(pargs->priority); +} + +long SysTime_Init(int priority) +{ + struct InitInfo args; + static epicsThreadOnceId onceId= EPICS_THREAD_ONCE_INIT; + + args.priority = priority; + epicsThreadOnce(&onceId, SysTime_InitOnceWrapper, &args); + return args.retval; +} + +static int SysTimeGetCurrent(epicsTimeStamp *pDest) +{ + struct timespec cur_systime; + unsigned long diff_sec, diff_nsec; + epicsTimeStamp epicsTime; + + double diffTime; + + epicsMutexMustLock(pSysTimePvt->lock); + + clock_gettime(CLOCK_REALTIME, &cur_systime); + + if (!pSysTimePvt->synced) { + if (cur_systime.tv_sec < POSIX_TIME_AT_EPICS_EPOCH) { /* Earlier than 1990 is clearly wrong. Set the time to 1/1/90 + + 1 day (to avoid timezone problems) and set the SysWorks + clock to that point */ + cur_systime.tv_sec = POSIX_TIME_AT_EPICS_EPOCH + 86400; + cur_systime.tv_nsec = 0; + clock_settime(CLOCK_REALTIME, &cur_systime); + printf("****************************" + "***************************************************\n" + "WARNING: System time not set. Initialised to 2/1/90\n" + "*****************************************************" + "**************************\n"); + } + epicsTimeFromTimespec(&epicsTime, &cur_systime); + } + else/* synced, need calculation */ + {/* the system time is always monotonic */ + if (cur_systime.tv_nsec >= pSysTimePvt->lastSyncedSysTime.tv_nsec) { + diff_sec = cur_systime.tv_sec + - pSysTimePvt->lastSyncedSysTime.tv_sec; + diff_nsec = cur_systime.tv_nsec + - pSysTimePvt->lastSyncedSysTime.tv_nsec; + } else { + diff_sec = cur_systime.tv_sec + - pSysTimePvt->lastSyncedSysTime.tv_sec - 1; + diff_nsec + = BILLION - pSysTimePvt->lastSyncedSysTime.tv_nsec + cur_systime.tv_nsec; + } + + epicsTime.nsec = pSysTimePvt->lastSyncedTS.nsec + diff_nsec; + epicsTime.secPastEpoch = pSysTimePvt->lastSyncedTS.secPastEpoch + + diff_sec; + if (epicsTime.nsec >= BILLION) { + epicsTime.nsec -= BILLION; + epicsTime.secPastEpoch ++; + } + } + + diffTime = epicsTimeDiffInSeconds(&epicsTime, &pSysTimePvt->lastReportedTS); + if (diffTime >= 0.0) {/* time is monotonic */ + *pDest = epicsTime; + pSysTimePvt->lastReportedTS = epicsTime; + } else {/* time never goes back */ + *pDest = pSysTimePvt->lastReportedTS; + } + + epicsMutexUnlock(pSysTimePvt->lock); + return (0); +} + +long SysTime_Report(int level) +{ + printf(SYS_TIME_DRV_VERSION"\n"); + + if (!pSysTimePvt) {/* drvSysTime is not used, we just report version then quit */ + printf("system time driver is not initialized yet!\n\n"); + } else { + printf("%synced to %d. Last time = %lds, %ldns. ", + pSysTimePvt->synced ? "S" : "Not s", + pSysTimePvt->synced_priority, + pSysTimePvt->lastSyncedSysTime.tv_sec, + pSysTimePvt->lastSyncedSysTime.tv_nsec); + } + return 0; +} diff --git a/src/libCom/osi/os/default/osdSysTime.h b/src/libCom/osi/os/default/osdSysTime.h new file mode 100644 index 000000000..5fb4a907e --- /dev/null +++ b/src/libCom/osi/os/default/osdSysTime.h @@ -0,0 +1,14 @@ +#ifndef _INC_drvSysTime +#define _INC_drvSysTime + +#ifdef __cplusplus +extern "C" { +#endif + +long SysTime_Init(int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libCom/osi/os/posix/osdTime.cpp b/src/libCom/osi/os/posix/osdTime.cpp index f78558692..0ac36f9a7 100644 --- a/src/libCom/osi/os/posix/osdTime.cpp +++ b/src/libCom/osi/os/posix/osdTime.cpp @@ -23,12 +23,12 @@ #define epicsExportSharedSymbols #include "epicsTime.h" - +#include "generalTimeSup.h" // // epicsTime::osdGetCurrent () // -extern "C" epicsShareFunc int epicsShareAPI epicsTimeGetCurrent (epicsTimeStamp *pDest) +extern "C" epicsShareFunc int epicsShareAPI osdTimeGetCurrent (epicsTimeStamp *pDest) { # ifdef CLOCK_REALTIME struct timespec ts; @@ -55,15 +55,9 @@ extern "C" epicsShareFunc int epicsShareAPI epicsTimeGetCurrent (epicsTimeStamp # endif } -// -// epicsTimeGetEvent () -// -extern "C" epicsShareFunc int epicsShareAPI epicsTimeGetEvent (epicsTimeStamp *pDest, int eventNumber) +extern "C" epicsShareFunc int epicsShareAPI osdTimeInit(void) { - if (eventNumber==epicsTimeEventCurrentTime) { - return epicsTimeGetCurrent (pDest); - } - return epicsTimeERROR; + return generalTimeCurrentTpRegister("posix Time", 100, osdTimeGetCurrent); } int epicsTime_gmtime ( const time_t *pAnsiTime, // X aCC 361 diff --git a/src/libCom/osi/os/vxWorks/iocClock.c b/src/libCom/osi/os/vxWorks/iocClock.c deleted file mode 100644 index 5617b3ed0..000000000 --- a/src/libCom/osi/os/vxWorks/iocClock.c +++ /dev/null @@ -1,219 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne -* National Laboratory. -* Copyright (c) 2002 The Regents of the University of California, as -* Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ -/* iocClock.c */ - -/* Author: Marty Kraimer Date: 16JUN2000 */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "epicsTypes.h" -#include "cantProceed.h" -#include "errlog.h" -#include "epicsThread.h" -#include "epicsMutex.h" -#include "epicsTime.h" -#include "iocClock.h" -#include "envDefs.h" - -#define BILLION 1000000000 -#define iocClockSyncRate 10.0 - -static int iocClockGetCurrent(epicsTimeStamp *pDest); -static int iocClockGetEvent(epicsTimeStamp *pDest, int eventNumber); - -typedef struct iocClockPvt { - epicsMutexId lock; - epicsTimeStamp clock; - unsigned long lastTick; - epicsUInt32 nanosecondsPerTick; - int tickRate; - int ticksToSkip; - pepicsTimeGetCurrent getCurrent; - pepicsTimeGetEvent getEvent; - const char *pserverAddr; -}iocClockPvt; -static iocClockPvt *piocClockPvt = 0; -static int nConsecutiveBad = 0; - -extern char* sysBootLine; - -static void syncNTP(void) -{ - struct timespec Currtime; - epicsTimeStamp epicsTime; - STATUS status; - int prevStatusBad = 0; - int firstTime=1; - - while(1) { - double diffTime; - if(!firstTime)epicsThreadSleep(iocClockSyncRate); - firstTime = 0; - status = sntpcTimeGet((char *)piocClockPvt->pserverAddr, - piocClockPvt->tickRate,&Currtime); - if(status) { - ++nConsecutiveBad; - /*wait 1 minute before reporting failure*/ - if(nConsecutiveBad<(60/iocClockSyncRate)) continue; - if(!prevStatusBad) - errlogPrintf("iocClockSyncWithNTPserver: sntpcTimeGet %s\n", - strerror(errno)); - prevStatusBad = 1; - continue; - } - nConsecutiveBad=0; - if(prevStatusBad) { - errlogPrintf("iocClockSyncWithNTPserver: sntpcTimeGet OK\n"); - prevStatusBad = 0; - } - epicsTimeFromTimespec(&epicsTime,&Currtime); - epicsMutexMustLock(piocClockPvt->lock); - diffTime = epicsTimeDiffInSeconds(&epicsTime,&piocClockPvt->clock); - if(diffTime>=0.0) { - piocClockPvt->clock = epicsTime; - } else {/*dont go back in time*/ - piocClockPvt->ticksToSkip = (int) (diffTime*piocClockPvt->tickRate); - } - piocClockPvt->lastTick = tickGet(); - epicsMutexUnlock(piocClockPvt->lock); - } -} - -void iocClockInit() -{ - if(piocClockPvt) return; - piocClockPvt = callocMustSucceed(1,sizeof(iocClockPvt),"iocClockInit"); - piocClockPvt->lock = epicsMutexCreate(); - piocClockPvt->nanosecondsPerTick = BILLION/sysClkRateGet(); - piocClockPvt->tickRate = sysClkRateGet(); - piocClockPvt->getCurrent = iocClockGetCurrent; - piocClockPvt->getEvent = iocClockGetEvent; - /* look first for environment variable or CONFIG_SITE_ENV default */ - piocClockPvt->pserverAddr = envGetConfigParamPtr(&EPICS_TS_NTP_INET); - if(!piocClockPvt->pserverAddr) { /* if neither, use the boot host */ - BOOT_PARAMS bootParms; - static char host_addr[BOOT_ADDR_LEN]; - bootStringToStruct(sysBootLine,&bootParms); - /* bootParms.had = host IP address */ - strncpy(host_addr,bootParms.had,BOOT_ADDR_LEN); - piocClockPvt->pserverAddr = host_addr; - } - if(!piocClockPvt->pserverAddr) { - errlogPrintf("No NTP server is defined. Clock does not work\n"); - return; - } - /* if TIMEZONE not defined, set it from EPICS_TIMEZONE */ - if (getenv("TIMEZONE") == NULL) { - const char *timezone = envGetConfigParamPtr(&EPICS_TIMEZONE); - if(timezone == NULL) { - printf("iocClockInit: No Time Zone Information\n"); - } else { - char envtimezone[100]; - sprintf(envtimezone,"TIMEZONE=%s",timezone); - if(putenv(envtimezone)==ERROR) { - printf("iocClockInit: TIMEZONE putenv failed\n"); - } - } - } - epicsThreadCreate("syncNTP", - epicsThreadPriorityHigh, - epicsThreadGetStackSize(epicsThreadStackSmall), - (EPICSTHREADFUNC)syncNTP,0); - return; -} - -void iocClockRegister(pepicsTimeGetCurrent getCurrent, - pepicsTimeGetEvent getEvent) -{ - if(piocClockPvt) { - printf("iocClockRegister: iocClock already initialized\n"); - return; - } - piocClockPvt = callocMustSucceed(1,sizeof(iocClockPvt),"iocClockRegister"); - piocClockPvt->getCurrent = getCurrent; - piocClockPvt->getEvent = getEvent; -} - -int iocClockGetCurrent(epicsTimeStamp *pDest) -{ - unsigned long currentTick,nticks,nsecs; - - epicsMutexMustLock(piocClockPvt->lock); - currentTick = tickGet(); - while(currentTick!=piocClockPvt->lastTick) { - nticks = (currentTick>piocClockPvt->lastTick) - ? (currentTick - piocClockPvt->lastTick) - : (currentTick + (ULONG_MAX - piocClockPvt->lastTick)); - if(piocClockPvt->ticksToSkip>0) {/*dont go back in time*/ - if(nticksticksToSkip) { - piocClockPvt->ticksToSkip -= nticks; - break; - } - nticks -= piocClockPvt->ticksToSkip; - } - piocClockPvt->lastTick = currentTick; - nsecs = nticks/piocClockPvt->tickRate; - nticks = nticks - nsecs*piocClockPvt->tickRate; - piocClockPvt->clock.nsec += nticks * piocClockPvt->nanosecondsPerTick; - if(piocClockPvt->clock.nsec>=BILLION) { - ++nsecs; - piocClockPvt->clock.nsec -= BILLION; - } - piocClockPvt->clock.secPastEpoch += nsecs; - } - *pDest = piocClockPvt->clock; - epicsMutexUnlock(piocClockPvt->lock); - return(0); -} - -int iocClockGetEvent(epicsTimeStamp *pDest, int eventNumber) -{ - if (eventNumber==epicsTimeEventCurrentTime) { - *pDest = piocClockPvt->clock; - return(0); - } - return(epicsTimeERROR); -} - -int epicsTimeGetCurrent (epicsTimeStamp *pDest) -{ - if(!piocClockPvt) { - iocClockInit(); - /*wait two seconds for syncNTP to contact network time server*/ - epicsThreadSleep(2.0); - } - if(piocClockPvt->getCurrent) return((*piocClockPvt->getCurrent)(pDest)); - return(epicsTimeERROR); -} - -int epicsTimeGetEvent (epicsTimeStamp *pDest, int eventNumber) -{ - if(!piocClockPvt) { - iocClockInit(); - /*wait two seconds for syncNTP to contact network time server*/ - epicsThreadSleep(2.0); - } - if(piocClockPvt->getEvent) - return((*piocClockPvt->getEvent)(pDest,eventNumber)); - return(epicsTimeERROR); -} - diff --git a/src/libCom/osi/os/vxWorks/iocClock.h b/src/libCom/osi/os/vxWorks/iocClock.h deleted file mode 100644 index 91b6813dc..000000000 --- a/src/libCom/osi/os/vxWorks/iocClock.h +++ /dev/null @@ -1,20 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne -* National Laboratory. -* Copyright (c) 2002 The Regents of the University of California, as -* Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ -/* iocClock.h */ - -/* Author: Marty Kraimer Date: 16JUN2000 */ - -#include "epicsTime.h" - -typedef int (*pepicsTimeGetCurrent)(epicsTimeStamp *pDest); -typedef int (*pepicsTimeGetEvent)(epicsTimeStamp *pDest,int eventNumber); - -void iocClockInit(void); -void iocClockRegister(pepicsTimeGetCurrent getCurrent,pepicsTimeGetEvent getEvent); diff --git a/src/libCom/osi/os/vxWorks/osdTime.cpp b/src/libCom/osi/os/vxWorks/osdTime.cpp index a8e2947f3..53a3211b1 100644 --- a/src/libCom/osi/os/vxWorks/osdTime.cpp +++ b/src/libCom/osi/os/vxWorks/osdTime.cpp @@ -8,9 +8,48 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ -#include "epicsTime.h" +#include +#include +#include +#include -// epicsTimeGetCurrent and epicsTimeGetEvent are implemented in iocClock.c +#include "epicsTime.h" +#include "errlog.h" +#include "osiNTPTime.h" +#include "osdSysTime.h" +#include "generalTimeSup.h" +#include "envDefs.h" + +const char *pserverAddr = NULL; +extern char* sysBootLine; + +extern "C" epicsShareFunc int epicsShareAPI osdTimeInit(void) +{ + NTPTime_Init(100); /* init NTP first so it can be used to sync VW */ + SysTime_Init(LAST_RESORT_PRIORITY); + return epicsTimeOK; +} + +int osdNTPGet(struct timespec *ts) +{ + return sntpcTimeGet((char *)pserverAddr, sysClkRateGet() ,ts); +} + +void osdNTPInit(void) +{ + pserverAddr = envGetConfigParamPtr(&EPICS_TS_NTP_INET); + if(!pserverAddr) { /* if neither, use the boot host */ + BOOT_PARAMS bootParms; + static char host_addr[BOOT_ADDR_LEN]; + bootStringToStruct(sysBootLine,&bootParms); + /* bootParms.had = host IP address */ + strncpy(host_addr,bootParms.had,BOOT_ADDR_LEN); + pserverAddr = host_addr; + } + if(!pserverAddr) { + errlogPrintf("No NTP server is defined. Clock does not work\n"); + } +} // vxWorks localtime_r interface does not match POSIX standards int epicsTime_localtime ( const time_t *clock, struct tm *result ) diff --git a/src/libCom/osi/os/vxWorks/osdTime.h b/src/libCom/osi/os/vxWorks/osdTime.h index aa303bbc1..fb23a14f8 100644 --- a/src/libCom/osi/os/vxWorks/osdTime.h +++ b/src/libCom/osi/os/vxWorks/osdTime.h @@ -9,3 +9,21 @@ \*************************************************************************/ /* Following needed for struct timeval */ #include + +#ifndef osdTimeh +#define osdTimeh + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int osdNTPGet(struct timespec *); +void osdNTPInit(void); +int sysClkRateGet(void); + +#ifdef __cplusplus +} +#endif +#endif /* ifndef osdTimeh */ diff --git a/src/libCom/osi/os/vxWorks/vxComLibrary.c b/src/libCom/osi/os/vxWorks/vxComLibrary.c index ff92a323a..429eaba5e 100644 --- a/src/libCom/osi/os/vxWorks/vxComLibrary.c +++ b/src/libCom/osi/os/vxWorks/vxComLibrary.c @@ -17,16 +17,12 @@ * modules top be loaded */ -extern void iocClockInit(void); #include "epicsDynLink.h" extern int logMsgToErrlog(void); extern int veclist(int); void vxComLibrary(int callit) { - - - if(callit) iocClockInit(); if(callit) symFindByNameEPICS(0,0,0,0); if(callit) logMsgToErrlog(); if(callit) veclist(0); diff --git a/src/libCom/osi/osiNTPTime.c b/src/libCom/osi/osiNTPTime.c new file mode 100644 index 000000000..0f75938ff --- /dev/null +++ b/src/libCom/osi/osiNTPTime.c @@ -0,0 +1,226 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Author: Marty Kraimer Date: 16JUN2000 */ + +/* This file is originally named as iocClock.c and provide the NTP time + * as default time source if no other time source registered during initialization. + * Now we made some minor change to add this to the time provider list */ + +/* Sheng Peng @ SNS ORNL 07/2004 */ +/* Version 1.3 */ +#include +#include +#include +#include +#include +#include +#include + +#include "epicsTypes.h" +#include "cantProceed.h" +#include "epicsThread.h" +#include "epicsMutex.h" +#include "epicsTime.h" +#include "envDefs.h" +#include "errlog.h" +#include "generalTimeSup.h" +#include "osdTime.h" + +#define BILLION 1000000000 +#define NTPTimeSyncInterval 10.0 + +#define NTPTIME_DRV_VERSION "NTPTime Driver Version 1.3" + +#include "epicsGeneralTime.h" + +static void NTPTimeSyncNTP(void); +static int NTPTimeGetCurrent(epicsTimeStamp *pDest); + +long NTPTime_Report(int level); + +typedef struct NTPTimePvt { + int synced; /* if never synced, we can't use it */ + epicsMutexId lock; + epicsTimeStamp clock; + unsigned long lastTick; + epicsUInt32 nanosecondsPerTick; + int tickRate; + int ticksToSkip; +}NTPTimePvt; +static NTPTimePvt *pNTPTimePvt = 0; +static int nConsecutiveBad = 0; + +static void NTPTimeSyncNTP(void) +{ + struct timespec Currtime; + epicsTimeStamp epicsTime; + int status; + int prevStatusBad = 0; + + while(1) { + double diffTime; + epicsThreadSleep(NTPTimeSyncInterval); + pNTPTimePvt->tickRate = sysClkRateGet(); + status = osdNTPGet(&Currtime); + if(status) { + ++nConsecutiveBad; + /*wait 1 minute before reporting failure*/ + if(nConsecutiveBad<(60/NTPTimeSyncInterval)) continue; + if(!prevStatusBad) + errlogPrintf("NTPTimeSyncWithNTPserver: sntpcTimeGet %s\n", + strerror(errno)); + prevStatusBad = 1; + pNTPTimePvt->synced = FALSE; + continue; + } + nConsecutiveBad=0; + if(prevStatusBad) { + errlogPrintf("NTPTimeSyncWithNTPserver: sntpcTimeGet OK\n"); + prevStatusBad = 0; + } + epicsTimeFromTimespec(&epicsTime,&Currtime); + epicsMutexMustLock(pNTPTimePvt->lock); + diffTime = epicsTimeDiffInSeconds(&epicsTime,&pNTPTimePvt->clock); + if(diffTime>=0.0) { + pNTPTimePvt->clock = epicsTime; + pNTPTimePvt->ticksToSkip = 0;/* fix bug here */ + } else {/*dont go back in time*/ + pNTPTimePvt->ticksToSkip = (int) ((-1)*diffTime*pNTPTimePvt->tickRate);/* fix bug here */ + } + pNTPTimePvt->lastTick = tickGet(); + pNTPTimePvt->synced = TRUE; + epicsMutexUnlock(pNTPTimePvt->lock); + } +} + +static long NTPTime_InitOnce(int priority) +{ + int status; + struct timespec Currtime; + epicsTimeStamp epicsTime; + + pNTPTimePvt = callocMustSucceed(1,sizeof(NTPTimePvt),"NTPTime_Init"); + + pNTPTimePvt->synced = FALSE; + pNTPTimePvt->lock = epicsMutexCreate(); + pNTPTimePvt->nanosecondsPerTick = BILLION/sysClkRateGet(); + pNTPTimePvt->tickRate = sysClkRateGet(); + /* look first for environment variable or CONFIG_SITE_ENV default */ + osdNTPInit(); + /* if TIMEZONE not defined, set it from EPICS_TIMEZONE */ + if (getenv("TIMEZONE") == NULL) { + const char *timezone = envGetConfigParamPtr(&EPICS_TIMEZONE); + if(timezone == NULL) { + printf("NTPTime_Init: No Time Zone Information\n"); + } else { + epicsEnvSet("TIMEZONE",timezone); + } + } + + /* try to sync with NTP server once here */ + pNTPTimePvt->tickRate = sysClkRateGet(); + status = osdNTPGet(&Currtime); + if(status) + {/* sync failed */ + printf("First try to sync with NTP server failed!\n"); + } + else + {/* sync OK */ + epicsTimeFromTimespec(&epicsTime,&Currtime); + epicsMutexMustLock(pNTPTimePvt->lock); + pNTPTimePvt->clock = epicsTime; + pNTPTimePvt->lastTick = tickGet(); + pNTPTimePvt->synced = TRUE; + epicsMutexUnlock(pNTPTimePvt->lock); + printf("First try to sync with NTP server succeed!\n"); + } + + epicsThreadCreate("NTPTimeSyncNTP", + epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackSmall), + (EPICSTHREADFUNC)NTPTimeSyncNTP,0); + + /* register to link list */ + generalTimeCurrentTpRegister("NTP", priority, NTPTimeGetCurrent); + + return 0; +} + +struct InitInfo { + int priority; + long retval; +}; +static void NTPTime_InitOnceWrapper(void *arg) +{ + struct InitInfo *pargs = (struct InitInfo *)arg; + pargs->retval = NTPTime_InitOnce(pargs->priority); +} + +long NTPTime_Init(int priority) +{ + struct InitInfo args; + static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; + + args.priority = priority; + epicsThreadOnce(&onceId, NTPTime_InitOnceWrapper, &args); + return args.retval; +} + +static int NTPTimeGetCurrent(epicsTimeStamp *pDest) +{ + unsigned long currentTick,nticks,nsecs; + + if(!pNTPTimePvt->synced) return epicsTimeERROR; + + epicsMutexMustLock(pNTPTimePvt->lock); + currentTick = tickGet(); + while(currentTick!=pNTPTimePvt->lastTick) { + nticks = (currentTick>pNTPTimePvt->lastTick) + ? (currentTick - pNTPTimePvt->lastTick) + : (currentTick + (ULONG_MAX - pNTPTimePvt->lastTick)); + if(pNTPTimePvt->ticksToSkip>0) {/*dont go back in time*/ + if(nticksticksToSkip) { + /*pNTPTimePvt->ticksToSkip -= nticks;*/ /* fix bug here */ + break; + } + nticks -= pNTPTimePvt->ticksToSkip; + pNTPTimePvt->ticksToSkip = 0; /* fix bug here */ + } + pNTPTimePvt->lastTick = currentTick; + pNTPTimePvt->tickRate = sysClkRateGet(); + nsecs = nticks/pNTPTimePvt->tickRate; + nticks = nticks - nsecs*pNTPTimePvt->tickRate; + pNTPTimePvt->clock.nsec += nticks * pNTPTimePvt->nanosecondsPerTick; + if(pNTPTimePvt->clock.nsec>=BILLION) { + ++nsecs; + pNTPTimePvt->clock.nsec -= BILLION; + } + pNTPTimePvt->clock.secPastEpoch += nsecs; + } + *pDest = pNTPTimePvt->clock; + epicsMutexUnlock(pNTPTimePvt->lock); + return(0); +} + +long NTPTime_Report(int level) +{ + printf(NTPTIME_DRV_VERSION"\n"); + + if(!pNTPTimePvt) + {/* drvNTPTime is not used, we just report version then quit */ + printf("NTP time driver is not initialized yet!\n\n"); + } + else + { + printf("%synced.\n", pNTPTimePvt->synced?"S":"Not s"); + } + return 0; +} diff --git a/src/libCom/osi/osiNTPTime.h b/src/libCom/osi/osiNTPTime.h new file mode 100644 index 000000000..4242f26b5 --- /dev/null +++ b/src/libCom/osi/osiNTPTime.h @@ -0,0 +1,14 @@ +#ifndef _INC_drvDevNTPTime +#define _INC_drvDevNTPTime + +#ifdef __cplusplus +extern "C" { +#endif + +long NTPTime_Init(int); + +#ifdef __cplusplus +} +#endif + +#endif