From b84ee89d8785a71d77a35585b20d6e1801c6bcce Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 16 Apr 2018 18:50:51 -0700 Subject: [PATCH 01/34] Fix ECHO definition to not match variables in MAKEFLAGS by using MFLAGS instead. Prior ECHO definition strips T_A=XXX command line variable definitions from MAKEFLAGS but doesn't strip other variable definitions such as INSTALL_LOCATION. As a result, if you "make INSTALL_LOCATION=XXX" the ECHO definition erroneously matches if your install location contains 's'. Changing the ECHO definition to MFLAGS avoids all command line variable definitions. --- configure/CONFIG_COMMON | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index e6e60332b..43f4866d7 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -82,7 +82,7 @@ IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION)) #------------------------------------------------------- # Make echo output - suppress echoing if make's '-s' flag is set NOP = : -ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo) +ECHO = @$(if $(findstring s,$(MFLAGS)),$(NOP),echo) #------------------------------------------------------- ifdef T_A From 59ec8d897dec4e836c2a1582e5933d3e392ba025 Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Fri, 10 Aug 2018 10:17:13 -0400 Subject: [PATCH 02/34] Expose callback queue status This allows tools like iocStats to monitor the queue status of the callback queues. This fixes lp:1786540. --- src/ioc/db/callback.c | 51 ++++++++++++++++++++++++++-- src/ioc/db/callback.h | 9 +++++ src/ioc/db/dbIocRegister.c | 24 +++++++++++++ src/ioc/db/dbScan.c | 37 ++++++++++++++++++++ src/ioc/db/dbScan.h | 9 +++++ src/libCom/ring/epicsRingBytes.c | 29 ++++++++++++++-- src/libCom/ring/epicsRingBytes.h | 3 ++ src/libCom/ring/epicsRingPointer.cpp | 12 +++++++ src/libCom/ring/epicsRingPointer.h | 38 +++++++++++++++++++-- 9 files changed, 205 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/callback.c b/src/ioc/db/callback.c index ae074141c..2b95cc5af 100644 --- a/src/ioc/db/callback.c +++ b/src/ioc/db/callback.c @@ -54,6 +54,7 @@ typedef struct cbQueueSet { epicsEventId semWakeUp; epicsRingPointerId queue; int queueOverflow; + int queueOverflows; int shutdown; int threadsConfigured; int threadsRunning; @@ -99,10 +100,55 @@ int callbackSetQueueSize(int size) fprintf(stderr, "Callback system already initialized\n"); return -1; } - callbackQueueSize = size; + epicsAtomicSetIntT(&callbackQueueSize, size); return 0; } +int callbackQueueStatus(const int reset, callbackQueueStats *result) +{ + if (!callbackIsInit) return -1; + int ret; + if (result) { + result->size = epicsAtomicGetIntT(&callbackQueueSize); + int prio; + for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { + epicsRingPointerId qId = callbackQueue[prio].queue; + result->numUsed[prio] = epicsRingPointerGetUsed(qId); + result->maxUsed[prio] = epicsRingPointerGetHighWaterMark(qId); + result->numOverflow[prio] = epicsAtomicGetIntT(&callbackQueue[prio].queueOverflows); + } + ret = 0; + } else { + ret = -2; + } + if (reset) { + int prio; + for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { + epicsRingPointerResetHighWaterMark(callbackQueue[prio].queue); + } + } + return ret; +} + +void callbackQueuePrintStatus(const int reset) +{ + callbackQueueStats stats; + if (callbackQueueStatus(reset, &stats) == -1) { + fprintf(stderr, "Callback system not initialized, yet. Please run " + "iocInit before using this command.\n"); + return; + } + + printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n"); + int prio; + for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { + double qusage = 100.0 * stats.numUsed[prio] / stats.size; + printf("%8s %15d %10d %6d %6.1f %11d\n", threadNamePrefix[prio], + stats.maxUsed[prio], stats.numUsed[prio], stats.size, + qusage, stats.numOverflow[prio]); + } +} + int callbackParallelThreads(int count, const char *prio) { if (callbackIsInit) { @@ -240,7 +286,7 @@ void callbackInit(void) epicsThreadId tid; callbackQueue[i].semWakeUp = epicsEventMustCreate(epicsEventEmpty); - callbackQueue[i].queue = epicsRingPointerLockedCreate(callbackQueueSize); + callbackQueue[i].queue = epicsRingPointerLockedCreate(epicsAtomicGetIntT(&callbackQueueSize)); if (callbackQueue[i].queue == 0) cantProceed("epicsRingPointerLockedCreate failed for %s\n", threadNamePrefix[i]); @@ -290,6 +336,7 @@ int callbackRequest(CALLBACK *pcallback) if (!pushOK) { epicsInterruptContextMessage(fullMessage[priority]); mySet->queueOverflow = TRUE; + epicsAtomicIncrIntT(&mySet->queueOverflows); return S_db_bufFull; } epicsEventSignal(mySet->semWakeUp); diff --git a/src/ioc/db/callback.h b/src/ioc/db/callback.h index fa626d1d0..dd13cdb81 100644 --- a/src/ioc/db/callback.h +++ b/src/ioc/db/callback.h @@ -48,6 +48,13 @@ typedef epicsCallback CALLBACK; typedef void (*CALLBACKFUNC)(struct callbackPvt*); +typedef struct callbackQueueStats { + int size; + int numUsed[NUM_CALLBACK_PRIORITIES]; + int maxUsed[NUM_CALLBACK_PRIORITIES]; + int numOverflow[NUM_CALLBACK_PRIORITIES]; +} callbackQueueStats; + #define callbackSetCallback(PFUN, PCALLBACK) \ ( (PCALLBACK)->callback = (PFUN) ) #define callbackSetPriority(PRIORITY, PCALLBACK) \ @@ -73,6 +80,8 @@ epicsShareFunc void callbackCancelDelayed(CALLBACK *pcallback); epicsShareFunc void callbackRequestProcessCallbackDelayed( CALLBACK *pCallback, int Priority, void *pRec, double seconds); epicsShareFunc int callbackSetQueueSize(int size); +epicsShareFunc int callbackQueueStatus(const int reset, callbackQueueStats *result); +void callbackQueuePrintStatus(const int reset); epicsShareFunc int callbackParallelThreads(int count, const char *prio); #ifdef __cplusplus diff --git a/src/ioc/db/dbIocRegister.c b/src/ioc/db/dbIocRegister.c index 4d0b88cd9..cecf75608 100644 --- a/src/ioc/db/dbIocRegister.c +++ b/src/ioc/db/dbIocRegister.c @@ -296,6 +296,17 @@ static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args) scanOnceSetQueueSize(args[0].ival); } +/* scanOnceQueueStatus */ +static const iocshArg scanOnceQueueStatusArg0 = { "reset",iocshArgInt}; +static const iocshArg * const scanOnceQueueStatusArgs[1] = + {&scanOnceQueueStatusArg0}; +static const iocshFuncDef scanOnceQueueStatusFuncDef = + {"scanOnceQueueStatus",1,scanOnceQueueStatusArgs}; +static void scanOnceQueueStatusCallFunc(const iocshArgBuf *args) +{ + scanOnceQueuePrintStatus(args[0].ival); +} + /* scanppl */ static const iocshArg scanpplArg0 = { "rate",iocshArgDouble}; static const iocshArg * const scanpplArgs[1] = {&scanpplArg0}; @@ -335,6 +346,17 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args) callbackSetQueueSize(args[0].ival); } +/* callbackQueueStatus */ +static const iocshArg callbackQueueStatusArg0 = { "reset", iocshArgInt}; +static const iocshArg * const callbackQueueStatusArgs[1] = + {&callbackQueueStatusArg0}; +static const iocshFuncDef callbackQueueStatusFuncDef = + {"callbackQueueStatus",1,callbackQueueStatusArgs}; +static void callbackQueueStatusCallFunc(const iocshArgBuf *args) +{ + callbackQueuePrintStatus(args[0].ival); +} + /* callbackParallelThreads */ static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgInt}; static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString}; @@ -441,12 +463,14 @@ void dbIocRegister(void) iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc); iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc); + iocshRegister(&scanOnceQueueStatusFuncDef,scanOnceQueueStatusCallFunc); iocshRegister(&scanpplFuncDef,scanpplCallFunc); iocshRegister(&scanpelFuncDef,scanpelCallFunc); iocshRegister(&postEventFuncDef,postEventCallFunc); iocshRegister(&scanpiolFuncDef,scanpiolCallFunc); iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc); + iocshRegister(&callbackQueueStatusFuncDef,callbackQueueStatusCallFunc); iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc); /* Needed before callback system is initialized */ diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index e5c78fea1..c94632794 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -24,6 +24,7 @@ #include "cantProceed.h" #include "dbDefs.h" #include "ellLib.h" +#include "epicsAtomic.h" #include "epicsEvent.h" #include "epicsMutex.h" #include "epicsPrint.h" @@ -63,6 +64,7 @@ static volatile enum ctl scanCtl; static int onceQueueSize = 1000; static epicsEventId onceSem; static epicsRingBytesId onceQ; +static int onceQOverruns = 0; static epicsThreadId onceTaskId; static void *exitOnce; @@ -676,6 +678,7 @@ int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr) if (!pushOK) { if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n"); newOverflow = FALSE; + epicsAtomicIncrIntT(&onceQOverruns); } else { newOverflow = TRUE; } @@ -722,6 +725,40 @@ int scanOnceSetQueueSize(int size) return 0; } +int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result) +{ + if (!onceQ) return -1; + int ret; + if (result) { + result->size = epicsRingBytesSize(onceQ) / sizeof(onceEntry); + result->numUsed = epicsRingBytesUsedBytes(onceQ) / sizeof(onceEntry); + result->maxUsed = epicsRingBytesHighWaterMark(onceQ) / sizeof(onceEntry); + result->numOverflow = epicsAtomicGetIntT(&onceQOverruns); + ret = 0; + } else { + ret = -2; + } + if (reset) { + epicsRingBytesResetHighWaterMark(onceQ); + } + return ret; +} + +void scanOnceQueuePrintStatus(const int reset) +{ + scanOnceQueueStats stats; + if (scanOnceQueueStatus(reset, &stats) == -1) { + fprintf(stderr, "scanOnce system not initialized, yet. Please run " + "iocInit before using this command.\n"); + return; + } + + printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n"); + double qusage = 100.0 * stats.numUsed / stats.size; + printf("%8s %15d %10d %6d %6.1f %11d\n", "scanOnce", stats.maxUsed, stats.numUsed, stats.size, qusage, + epicsAtomicGetIntT(&onceQOverruns)); +} + static void initOnce(void) { if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) { diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h index d483a0c30..830d3a8dc 100644 --- a/src/ioc/db/dbScan.h +++ b/src/ioc/db/dbScan.h @@ -42,6 +42,13 @@ struct dbCommon; typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio); typedef void (*once_complete)(void *usr, struct dbCommon*); +typedef struct scanOnceQueueStats { + int size; + int numUsed; + int maxUsed; + int numOverflow; +} scanOnceQueueStats; + epicsShareFunc long scanInit(void); epicsShareFunc void scanRun(void); epicsShareFunc void scanPause(void); @@ -57,6 +64,8 @@ epicsShareFunc double scanPeriod(int scan); epicsShareFunc int scanOnce(struct dbCommon *); epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr); epicsShareFunc int scanOnceSetQueueSize(int size); +epicsShareFunc int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result); +void scanOnceQueuePrintStatus(const int reset); /*print periodic lists*/ epicsShareFunc int scanppl(double rate); diff --git a/src/libCom/ring/epicsRingBytes.c b/src/libCom/ring/epicsRingBytes.c index cb7e52e83..a976d8412 100644 --- a/src/libCom/ring/epicsRingBytes.c +++ b/src/libCom/ring/epicsRingBytes.c @@ -21,6 +21,7 @@ #include #define epicsExportSharedSymbols +#include "epicsAtomic.h" #include "epicsSpin.h" #include "dbDefs.h" #include "epicsRingBytes.h" @@ -38,6 +39,7 @@ typedef struct ringPvt { volatile int nextPut; volatile int nextGet; int size; + int highWaterMark; volatile char buffer[1]; /* actually larger */ }ringPvt; @@ -47,6 +49,7 @@ epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int size) if(!pring) return NULL; pring->size = size + SLOP; + pring->highWaterMark = 0; pring->nextGet = 0; pring->nextPut = 0; pring->lock = 0; @@ -131,8 +134,13 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( if (pring->lock) epicsSpinUnlock(pring->lock); return 0; } - if (nbytes) + if (nbytes) { memcpy ((void *)&pring->buffer[nextPut], value, nbytes); + int curUsed = pring->size - SLOP - freeCount; + if (curUsed > epicsAtomicGetIntT(&pring->highWaterMark)) { + epicsAtomicSetIntT(&pring->highWaterMark, curUsed); + } + } nextPut += nbytes; } else { @@ -143,8 +151,13 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( } topCount = size - nextPut; copyCount = (nbytes > topCount) ? topCount : nbytes; - if (copyCount) + if (copyCount) { memcpy ((void *)&pring->buffer[nextPut], value, copyCount); + int curUsed = pring->size - SLOP - freeCount; + if (curUsed > epicsAtomicGetIntT(&pring->highWaterMark)) { + epicsAtomicSetIntT(&pring->highWaterMark, curUsed); + } + } nextPut += copyCount; if (nextPut == size) { int nLeft = nbytes - copyCount; @@ -224,3 +237,15 @@ epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id) { return (epicsRingBytesFreeBytes(id) <= 0); } + +epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id) +{ + ringPvt *pring = (ringPvt *)id; + return epicsAtomicGetIntT(&pring->highWaterMark); +} + +epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)id; + epicsAtomicSetIntT(&pring->highWaterMark, epicsRingBytesUsedBytes(id)); +} diff --git a/src/libCom/ring/epicsRingBytes.h b/src/libCom/ring/epicsRingBytes.h index 011829bfe..3dc0081ad 100644 --- a/src/libCom/ring/epicsRingBytes.h +++ b/src/libCom/ring/epicsRingBytes.h @@ -24,6 +24,7 @@ extern "C" { #include "shareLib.h" typedef void *epicsRingBytesId; +typedef void const *epicsRingBytesIdConst; epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int nbytes); /* Same, but secured by a spinlock */ @@ -39,6 +40,8 @@ epicsShareFunc int epicsShareAPI epicsRingBytesUsedBytes(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesSize(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesIsEmpty(epicsRingBytesId id); epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id); +epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id); +epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id); #ifdef __cplusplus } diff --git a/src/libCom/ring/epicsRingPointer.cpp b/src/libCom/ring/epicsRingPointer.cpp index 9c144cec1..709ab6509 100644 --- a/src/libCom/ring/epicsRingPointer.cpp +++ b/src/libCom/ring/epicsRingPointer.cpp @@ -90,3 +90,15 @@ epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id) voidPointer *pvoidPointer = reinterpret_cast(id); return((pvoidPointer->isFull()) ? 1 : 0); } + +epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id) +{ + voidPointer const *pvoidPointer = reinterpret_cast(id); + return(pvoidPointer->getHighWaterMark()); +} + +epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id) +{ + voidPointer *pvoidPointer = reinterpret_cast(id); + pvoidPointer->resetHighWaterMark(); +} diff --git a/src/libCom/ring/epicsRingPointer.h b/src/libCom/ring/epicsRingPointer.h index 48d62036d..b62923883 100644 --- a/src/libCom/ring/epicsRingPointer.h +++ b/src/libCom/ring/epicsRingPointer.h @@ -23,6 +23,7 @@ * epicsRingPointerLocked uses a spinlock. */ +#include "epicsAtomic.h" #include "epicsSpin.h" #include "shareLib.h" @@ -40,18 +41,22 @@ public: /* Functions */ int getSize() const; bool isEmpty() const; bool isFull() const; + int getHighWaterMark() const; + void resetHighWaterMark(); private: /* Prevent compiler-generated member functions */ /* default constructor, copy constructor, assignment operator */ epicsRingPointer(); epicsRingPointer(const epicsRingPointer &); epicsRingPointer& operator=(const epicsRingPointer &); + int getUsedNoLock() const; private: /* Data */ epicsSpinId lock; volatile int nextPush; volatile int nextPop; int size; + int highWaterMark; T * volatile * buffer; }; @@ -59,6 +64,7 @@ extern "C" { #endif /*__cplusplus */ typedef void *epicsRingPointerId; +typedef void const *epicsRingPointerIdConst; epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size); /* Same, but secured by a spinlock */ @@ -74,6 +80,8 @@ epicsShareFunc int epicsShareAPI epicsRingPointerGetUsed(epicsRingPointerId id) epicsShareFunc int epicsShareAPI epicsRingPointerGetSize(epicsRingPointerId id); epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id); epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id); +epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id); +epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id); /* This routine was incorrectly named in previous releases */ #define epicsRingPointerSize epicsRingPointerGetSize @@ -95,7 +103,8 @@ epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id); template inline epicsRingPointer::epicsRingPointer(int sz, bool locked) : - lock(0), nextPush(0), nextPop(0), size(sz+1), buffer(new T* [sz+1]) + lock(0), nextPush(0), nextPop(0), size(sz+1), highWaterMark(0), + buffer(new T* [sz+1]) { if (locked) lock = epicsSpinCreate(); @@ -121,6 +130,10 @@ inline bool epicsRingPointer::push(T *p) } buffer[next] = p; nextPush = newNext; + int used = getUsedNoLock(); + if (used > epicsAtomicGetIntT(&highWaterMark)) { + epicsAtomicSetIntT(&highWaterMark, used); + } if (lock) epicsSpinUnlock(lock); return(true); } @@ -161,12 +174,19 @@ inline int epicsRingPointer::getFree() const return n; } +template +inline int epicsRingPointer::getUsedNoLock() const +{ + int n = nextPush - nextPop; + if (n < 0) n += size; + return n; +} + template inline int epicsRingPointer::getUsed() const { if (lock) epicsSpinLock(lock); - int n = nextPush - nextPop; - if (n < 0) n += size; + int n = getUsedNoLock(); if (lock) epicsSpinUnlock(lock); return n; } @@ -196,6 +216,18 @@ inline bool epicsRingPointer::isFull() const return((count == 0) || (count == size)); } +template +inline int epicsRingPointer::getHighWaterMark() const +{ + return epicsAtomicGetIntT(&highWaterMark); +} + +template +inline void epicsRingPointer::resetHighWaterMark() +{ + epicsAtomicSetIntT(&highWaterMark, getUsed()); +} + #endif /* __cplusplus */ #endif /* INCepicsRingPointerh */ From 215c5d954bbc6312e0f17656da37a234b1daffae Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Tue, 6 Mar 2018 01:45:21 -0800 Subject: [PATCH 03/34] Modify generalTimeGetEventPriority() to allow eventNumber > 256. eventNumbers between 0 and NUM_TIME_EVENTS will continue to get checked for advancing timestamps. Support for eventNumbers > NUM_TIME_EVENTS will be up to the generalTime provider. --- src/libCom/osi/epicsGeneralTime.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libCom/osi/epicsGeneralTime.c b/src/libCom/osi/epicsGeneralTime.c index 3054175ef..6fb42f755 100644 --- a/src/libCom/osi/epicsGeneralTime.c +++ b/src/libCom/osi/epicsGeneralTime.c @@ -231,8 +231,8 @@ static int generalTimeGetEventPriority(epicsTimeStamp *pDest, int eventNumber, IFDEBUG(2) printf("generalTimeGetEventPriority(eventNum=%d)\n", eventNumber); - if ((eventNumber < 0 || eventNumber >= NUM_TIME_EVENTS) && - (eventNumber != epicsTimeEventBestTime)) + STATIC_ASSERT ( epicsTimeEventBestTime == -1 ); + if (eventNumber < epicsTimeEventBestTime) return S_time_badEvent; epicsMutexMustLock(gtPvt.eventListLock); @@ -245,7 +245,9 @@ static int generalTimeGetEventPriority(epicsTimeStamp *pDest, int eventNumber, if (pPrio) *pPrio = ptp->priority; - if (eventNumber == epicsTimeEventBestTime) { + if (eventNumber >= NUM_TIME_EVENTS) { + *pDest = ts; + } else if (eventNumber == epicsTimeEventBestTime) { if (epicsTimeGreaterThanEqual(&ts, >Pvt.lastProvidedBestTime)) { *pDest = ts; From 3d88c8495b8eef6b71223ff28329fba9304a2baa Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Wed, 31 Oct 2018 20:30:58 -0700 Subject: [PATCH 04/34] Added comments re handling of event numbers >= NUM_TIME_EVENTS. --- src/libCom/osi/epicsGeneralTime.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libCom/osi/epicsGeneralTime.h b/src/libCom/osi/epicsGeneralTime.h index 3adc23705..6bbb0b21b 100644 --- a/src/libCom/osi/epicsGeneralTime.h +++ b/src/libCom/osi/epicsGeneralTime.h @@ -17,7 +17,11 @@ extern "C" { #endif #define NUM_TIME_EVENTS 256 -/* Time Events are numbered 0 through (NUM_TIME_EVENTS-1) */ +/* Time Events numbered 0 through (NUM_TIME_EVENTS-1) are validated by */ +/* code in epicsGeneralTime.c that tests for advancing timestamps and */ +/* enforces that restriction. Event numbers greater than or equal to */ +/* NUM_TIME_EVENTS are now allowed if supported by a custom time provider */ +/* which should provide its own advancing timestamp validation. */ epicsShareFunc void generalTime_Init(void); From fcb5675040ba5be3f9bf86cfa686e12a1f0bcad1 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Wed, 31 Oct 2018 20:35:52 -0700 Subject: [PATCH 05/34] Added release notes re handling of event numbers >= NUM_TIME_EVENTS. --- documentation/RELEASE_NOTES.html | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 366721455..b8cc55aa6 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -19,6 +19,15 @@ --> +

Support for event codes greater than or equal to NUM_TIME_EVENTS

+

Event numbers greater than or equal to +NUM_TIME_EVENTS are now allowed if supported by a custom time provider +which should provide its own advancing timestamp validation.

+ +

Time Events numbered 0 through (NUM_TIME_EVENTS-1) are still validated by +code in epicsGeneralTime.c that tests for advancing timestamps and +enforces that restriction.

+

Type-safe Device and Driver Support Tables

Type-safe versions of the device and driver support structures dset From 43322335dfae49b62930f95b634265ac936968f9 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Wed, 30 May 2018 01:46:29 -0700 Subject: [PATCH 06/34] Add -V flag to ca client tools to show EPICS and CA versions. --- src/ca/client/tools/caget.c | 17 +++++++++++------ src/ca/client/tools/cainfo.c | 14 ++++++++++---- src/ca/client/tools/camonitor.c | 15 ++++++++++----- src/ca/client/tools/caput.c | 17 +++++++++++------ 4 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/ca/client/tools/caget.c b/src/ca/client/tools/caget.c index a2b0e08b0..ffc4244f0 100644 --- a/src/ca/client/tools/caget.c +++ b/src/ca/client/tools/caget.c @@ -28,12 +28,13 @@ #include #include -#include -#include +#include "epicsStdlib.h" +#include "epicsString.h" -#include -#include -#include +#include "alarm.h" +#include "cadef.h" +#include "epicsGetopt.h" +#include "epicsVersion.h" #include "tool_lib.h" @@ -55,6 +56,7 @@ static void usage (void) { fprintf (stderr, "\nUsage: caget [options] ...\n\n" " -h: Help: Print this message\n" + " -V: Version: Show EPICS and CA versions\n" "Channel Access options:\n" " -w : Wait time, specifies CA timeout, default is %f second(s)\n" " -c: Asynchronous get (use ca_get_callback and wait for completion)\n" @@ -389,11 +391,14 @@ int main (int argc, char *argv[]) LINE_BUFFER(stdout); /* Configure stdout buffering */ - while ((opt = getopt(argc, argv, ":taicnhsSe:f:g:l:#:d:0:w:p:F:")) != -1) { + while ((opt = getopt(argc, argv, ":taicnhsSVe:f:g:l:#:d:0:w:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; + case 'V': + printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); + return 0; case 't': /* Terse output mode */ complainIfNotPlainAndSet(&format, terse); break; diff --git a/src/ca/client/tools/cainfo.c b/src/ca/client/tools/cainfo.c index 924c34b10..e881ef240 100644 --- a/src/ca/client/tools/cainfo.c +++ b/src/ca/client/tools/cainfo.c @@ -22,10 +22,11 @@ */ #include -#include +#include "epicsStdlib.h" +#include "epicsVersion.h" -#include -#include +#include "cadef.h" +#include "epicsGetopt.h" #include "tool_lib.h" @@ -36,12 +37,14 @@ void usage (void) { fprintf (stderr, "\nUsage: cainfo [options] ...\n\n" " -h: Help: Print this message\n" + " -V: Version: Show EPICS and CA versions\n" "Channel Access options:\n" " -w : Wait time, specifies CA timeout, default is %f second(s)\n" " -s : Call ca_client_status with the specified interest level\n" " -p : CA priority (0-%u, default 0=lowest)\n" "\nExample: cainfo my_channel another_channel\n\n" , DEFAULT_TIMEOUT, CA_PRIORITY_MAX); + fprintf (stderr, "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); } @@ -137,11 +140,14 @@ int main (int argc, char *argv[]) LINE_BUFFER(stdout); /* Configure stdout buffering */ - while ((opt = getopt(argc, argv, ":nhw:s:p:")) != -1) { + while ((opt = getopt(argc, argv, ":nhVw:s:p:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; + case 'V': + printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); + return 0; case 'w': /* Set CA timeout value */ if(epicsScanDouble(optarg, &caTimeout) != 1) { diff --git a/src/ca/client/tools/camonitor.c b/src/ca/client/tools/camonitor.c index 307dad8d6..09c965ebb 100644 --- a/src/ca/client/tools/camonitor.c +++ b/src/ca/client/tools/camonitor.c @@ -24,11 +24,12 @@ */ #include -#include +#include "epicsStdlib.h" #include +#include "epicsVersion.h" -#include -#include +#include "cadef.h" +#include "epicsGetopt.h" #include "tool_lib.h" @@ -44,7 +45,8 @@ void usage (void) { fprintf (stderr, "\nUsage: camonitor [options] ...\n" "\n" - " -h: Help; Print this message\n" + " -h: Help: Print this message\n" + " -V: Version: Show EPICS and CA versions\n" "Channel Access options:\n" " -w : Wait time, specifies CA timeout, default is %f second(s)\n" " -m : Specify CA event mask to use. is any combination of\n" @@ -209,11 +211,14 @@ int main (int argc, char *argv[]) LINE_BUFFER(stdout); /* Configure stdout buffering */ - while ((opt = getopt(argc, argv, ":nhm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) { + while ((opt = getopt(argc, argv, ":nhVm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; + case 'V': + printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); + return 0; case 'n': /* Print ENUM as index numbers */ enumAsNr=1; break; diff --git a/src/ca/client/tools/caput.c b/src/ca/client/tools/caput.c index f6dcc21cd..a7ea618a6 100644 --- a/src/ca/client/tools/caput.c +++ b/src/ca/client/tools/caput.c @@ -31,12 +31,13 @@ #include #include -#include +#include "epicsStdlib.h" -#include -#include -#include -#include +#include "cadef.h" +#include "epicsGetopt.h" +#include "epicsEvent.h" +#include "epicsString.h" +#include "epicsVersion.h" #include "tool_lib.h" @@ -59,6 +60,7 @@ void usage (void) fprintf (stderr, "\nUsage: caput [options] \n" " caput -a [options] ...\n\n" " -h: Help: Print this message\n" + " -V: Version: Show EPICS and CA versions\n" "Channel Access options:\n" " -w : Wait time, specifies CA timeout, default is %f second(s)\n" " -c: Asynchronous put (use ca_put_callback and wait for completion)\n" @@ -279,11 +281,14 @@ int main (int argc, char *argv[]) LINE_BUFFER(stdout); /* Configure stdout buffering */ putenv("POSIXLY_CORRECT="); /* Behave correct on GNU getopt systems */ - while ((opt = getopt(argc, argv, ":cnlhatsS#:w:p:F:")) != -1) { + while ((opt = getopt(argc, argv, ":cnlhatsVS#:w:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; + case 'V': + printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); + return 0; case 'n': /* Force interpret ENUM as index number */ enumAsNr = 1; enumAsString = 0; From 072dbd53e7d8babe70a40e25c72379703e6bfaad Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 2 Nov 2018 17:47:13 -0500 Subject: [PATCH 07/34] Adjust clean-tests rule Only delete test-result files that actually exist. --- configure/RULES_BUILD | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 1c38eef4c..7061c5658 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -353,14 +353,17 @@ ifneq ($(TAPFILES),) ifdef RUNTESTS_ENABLED prove --failures --ext .tap --exec "$(CAT)" --color $(TAPFILES) endif + +CURRENT_TAPFILES := $(wildcard $(TAPFILES)) +CURRENT_JUNITFILES := $(wildcard $(JUNITFILES)) endif clean-tests: -ifneq ($(TAPFILES),) - $(RM) $(TAPFILES) +ifneq ($(CURRENT_TAPFILES),) + $(RM) $(CURRENT_TAPFILES) endif -ifneq ($(JUNITFILES),) - $(RM) $(JUNITFILES) +ifneq ($(CURRENT_JUNITFILES),) + $(RM) $(CURRENT_JUNITFILES) endif tapfiles: $(TESTSCRIPTS) $(TAPFILES) From 3d8e2d933d035e2bf71990b27b5356d4e7faecd0 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 13 Aug 2018 19:14:16 -0700 Subject: [PATCH 08/34] Include epicsStdio.h so driver report output can be redirected from iocsh --- src/ioc/db/dbChannel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index c6a8454f6..399d13dab 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -21,6 +21,7 @@ #include "epicsAssert.h" #include "epicsExit.h" #include "epicsString.h" +#include "epicsStdio.h" #include "errlog.h" #include "freeList.h" #include "gpHash.h" From 63ddb2d4fcd44c552b17085b8ca678cca97e304e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 28 Oct 2018 19:03:42 -0700 Subject: [PATCH 09/34] fix mingw build --- src/libCom/osi/osiClockTime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libCom/osi/osiClockTime.c b/src/libCom/osi/osiClockTime.c index fb9d1532f..b0f4c35eb 100644 --- a/src/libCom/osi/osiClockTime.c +++ b/src/libCom/osi/osiClockTime.c @@ -40,7 +40,7 @@ static struct { static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; -#ifdef CLOCK_REALTIME +#if defined(CLOCK_REALTIME) && !defined(_WIN32) /* This code is not used on systems without Posix CLOCK_REALTIME, * but the only way to detect that is from the OS headers, so the * Makefile can't exclude compiling this file on those systems. From 693c1020f259c287cdf1a408f69238c9cc2211a8 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 4 Nov 2018 22:04:39 -0800 Subject: [PATCH 10/34] Test if disconnected CA link alarms --- src/std/rec/test/Makefile | 1 + src/std/rec/test/badCaLink.db | 4 ++++ src/std/rec/test/regressTest.c | 19 ++++++++++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/std/rec/test/badCaLink.db diff --git a/src/std/rec/test/Makefile b/src/std/rec/test/Makefile index 3fcc6bdeb..3a0a76153 100644 --- a/src/std/rec/test/Makefile +++ b/src/std/rec/test/Makefile @@ -114,6 +114,7 @@ TESTPROD_HOST += regressTest regressTest_SRCS += regressTest.c regressTest_SRCS += regressTest_registerRecordDeviceDriver.cpp TESTFILES += $(COMMON_DIR)/regressTest.dbd ../regressArray1.db ../regressHex.db ../regressLinkMS.db +TESTFILES += ../badCaLink.db TESTS += regressTest TESTPROD_HOST += mbbioDirectTest diff --git a/src/std/rec/test/badCaLink.db b/src/std/rec/test/badCaLink.db new file mode 100644 index 000000000..c71bc9d2f --- /dev/null +++ b/src/std/rec/test/badCaLink.db @@ -0,0 +1,4 @@ +record(ai, "ai:disconn") { + field(INP , "invalid CA") + field(UDF , "0") +} diff --git a/src/std/rec/test/regressTest.c b/src/std/rec/test/regressTest.c index 15010cebf..0c68f7ffb 100644 --- a/src/std/rec/test/regressTest.c +++ b/src/std/rec/test/regressTest.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -123,12 +124,28 @@ void testLinkMS(void) testdbCleanup(); } +/* lp:1798855 disconnected CA link must alarm */ +static +void testCADisconn(void) +{ + testDiag("In testCADisconn()"); + + startRegressTestIoc("badCaLink.db"); + + testdbPutFieldOk("ai:disconn.PROC", DBF_LONG, 1); + testTodoBegin("lp:1798855"); + testdbGetFieldEqual("ai:disconn.SEVR", DBF_LONG, INVALID_ALARM); + testdbGetFieldEqual("ai:disconn.STAT", DBF_LONG, LINK_ALARM); + testTodoEnd(); +} + MAIN(regressTest) { - testPlan(31); + testPlan(34); testArrayLength1(); testHexConstantLinks(); testLinkMS(); + testCADisconn(); return testDone(); } From 9a8860b77197ff6ab1cf4e9a65e5d24aaf10a51f Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 5 Nov 2018 18:44:46 -0800 Subject: [PATCH 11/34] Expand tabs and revert #include lines to <> instead of quotes. --- src/ca/client/tools/caget.c | 14 +++++++------- src/ca/client/tools/cainfo.c | 10 +++++----- src/ca/client/tools/camonitor.c | 10 +++++----- src/ca/client/tools/caput.c | 14 +++++++------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/ca/client/tools/caget.c b/src/ca/client/tools/caget.c index ffc4244f0..383a8e1ce 100644 --- a/src/ca/client/tools/caget.c +++ b/src/ca/client/tools/caget.c @@ -28,12 +28,12 @@ #include #include -#include "epicsStdlib.h" -#include "epicsString.h" +#include +#include -#include "alarm.h" -#include "cadef.h" -#include "epicsGetopt.h" +#include +#include +#include #include "epicsVersion.h" #include "tool_lib.h" @@ -396,8 +396,8 @@ int main (int argc, char *argv[]) case 'h': /* Print usage */ usage(); return 0; - case 'V': - printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); + case 'V': + printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); return 0; case 't': /* Terse output mode */ complainIfNotPlainAndSet(&format, terse); diff --git a/src/ca/client/tools/cainfo.c b/src/ca/client/tools/cainfo.c index e881ef240..2263249fb 100644 --- a/src/ca/client/tools/cainfo.c +++ b/src/ca/client/tools/cainfo.c @@ -22,11 +22,11 @@ */ #include -#include "epicsStdlib.h" +#include #include "epicsVersion.h" -#include "cadef.h" -#include "epicsGetopt.h" +#include +#include #include "tool_lib.h" @@ -145,8 +145,8 @@ int main (int argc, char *argv[]) case 'h': /* Print usage */ usage(); return 0; - case 'V': - printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); + case 'V': + printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); return 0; case 'w': /* Set CA timeout value */ if(epicsScanDouble(optarg, &caTimeout) != 1) diff --git a/src/ca/client/tools/camonitor.c b/src/ca/client/tools/camonitor.c index 09c965ebb..a3fdecd55 100644 --- a/src/ca/client/tools/camonitor.c +++ b/src/ca/client/tools/camonitor.c @@ -24,12 +24,12 @@ */ #include -#include "epicsStdlib.h" +#include #include #include "epicsVersion.h" -#include "cadef.h" -#include "epicsGetopt.h" +#include +#include #include "tool_lib.h" @@ -216,8 +216,8 @@ int main (int argc, char *argv[]) case 'h': /* Print usage */ usage(); return 0; - case 'V': - printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); + case 'V': + printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); return 0; case 'n': /* Print ENUM as index numbers */ enumAsNr=1; diff --git a/src/ca/client/tools/caput.c b/src/ca/client/tools/caput.c index a7ea618a6..d6db8ba7f 100644 --- a/src/ca/client/tools/caput.c +++ b/src/ca/client/tools/caput.c @@ -31,12 +31,12 @@ #include #include -#include "epicsStdlib.h" +#include -#include "cadef.h" -#include "epicsGetopt.h" -#include "epicsEvent.h" -#include "epicsString.h" +#include +#include +#include +#include #include "epicsVersion.h" #include "tool_lib.h" @@ -286,8 +286,8 @@ int main (int argc, char *argv[]) case 'h': /* Print usage */ usage(); return 0; - case 'V': - printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); + case 'V': + printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); return 0; case 'n': /* Force interpret ENUM as index number */ enumAsNr = 1; From 168d430921791007a0cdbe6806db13e054afbd96 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 6 Nov 2018 10:15:06 -0600 Subject: [PATCH 12/34] Unify shebang line in tap-to-junit-xml --- src/tools/tap-to-junit-xml.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/tap-to-junit-xml.pl b/src/tools/tap-to-junit-xml.pl index fdcfcf99b..76f9721a3 100644 --- a/src/tools/tap-to-junit-xml.pl +++ b/src/tools/tap-to-junit-xml.pl @@ -1,4 +1,4 @@ -#!/usr/bin/perl +#!/usr/bin/env perl =head1 NAME tap-to-junit-xml - convert perl-style TAP test output to JUnit-style XML From 6ea6c6ff665f43fe05402d6fbdbee17617a29afe Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 6 Nov 2018 13:02:36 -0600 Subject: [PATCH 13/34] VxWorks: Don't use GCC 2.x for dependency file generation --- configure/os/CONFIG.Common.vxWorksCommon | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure/os/CONFIG.Common.vxWorksCommon b/configure/os/CONFIG.Common.vxWorksCommon index 875fe81b7..95f8f7f70 100644 --- a/configure/os/CONFIG.Common.vxWorksCommon +++ b/configure/os/CONFIG.Common.vxWorksCommon @@ -185,6 +185,12 @@ COMPILE.ctdt = $(CC) -c $(CPPFLAGS) $(CFLAGS_ctdt) $(INCLUDES) $(SOURCE_FLAG) VXCPPFLAGS = $(filter-out $(OP_SYS_INCLUDE_CPPFLAGS),$(CPPFLAGS)) PREPROCESS.cpp = $(CPP) $(VXCPPFLAGS) $(INCLUDES) $< > $@ +#-------------------------------------------------- +# Don't use gcc 2.x for dependency generation + +HDEPENDS_METHOD_2 = MKMF +HDEPENDS_METHOD = $(firstword $(HDEPENDS_METHOD_$(VX_GNU_MAJOR_VERSION)) COMP) + #-------------------------------------------------- # Allow site overrides -include $(CONFIG)/os/CONFIG_SITE.Common.vxWorksCommon From 6e536e1ee04ddd32a0c2a7cf0b316e77d500f4da Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 19 Nov 2018 17:59:27 -0600 Subject: [PATCH 14/34] Fix msi-copy target for cross-build host arch's --- src/ioc/dbtemplate/test/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ioc/dbtemplate/test/Makefile b/src/ioc/dbtemplate/test/Makefile index fb03b54cf..dc73eacd0 100644 --- a/src/ioc/dbtemplate/test/Makefile +++ b/src/ioc/dbtemplate/test/Makefile @@ -17,8 +17,9 @@ TESTS += msi TESTSCRIPTS_HOST += $(TESTS:%=%.t) -TARGETS_HOST += msi-copy$(EXE) -TARGETS += $(TARGETS_$(BUILD_CLASS)) +ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS))) + TARGETS += msi-copy$(EXE) +endif include $(TOP)/configure/RULES From 0f16977caf20c5b7f21746a11daea7c5c86f236c Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Tue, 27 Nov 2018 10:30:35 -0500 Subject: [PATCH 15/34] Make code compatible with ISO C90 --- src/ioc/db/callback.c | 24 ++++++++++++------------ src/ioc/db/dbScan.c | 14 +++++++------- src/libCom/ring/epicsRingBytes.c | 4 ++-- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/ioc/db/callback.c b/src/ioc/db/callback.c index 2b95cc5af..253d2b86b 100644 --- a/src/ioc/db/callback.c +++ b/src/ioc/db/callback.c @@ -106,11 +106,11 @@ int callbackSetQueueSize(int size) int callbackQueueStatus(const int reset, callbackQueueStats *result) { - if (!callbackIsInit) return -1; int ret; + if (!callbackIsInit) return -1; if (result) { - result->size = epicsAtomicGetIntT(&callbackQueueSize); int prio; + result->size = epicsAtomicGetIntT(&callbackQueueSize); for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { epicsRingPointerId qId = callbackQueue[prio].queue; result->numUsed[prio] = epicsRingPointerGetUsed(qId); @@ -136,16 +136,16 @@ void callbackQueuePrintStatus(const int reset) if (callbackQueueStatus(reset, &stats) == -1) { fprintf(stderr, "Callback system not initialized, yet. Please run " "iocInit before using this command.\n"); - return; - } - - printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n"); - int prio; - for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { - double qusage = 100.0 * stats.numUsed[prio] / stats.size; - printf("%8s %15d %10d %6d %6.1f %11d\n", threadNamePrefix[prio], - stats.maxUsed[prio], stats.numUsed[prio], stats.size, - qusage, stats.numOverflow[prio]); + } else { + int prio; + printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n"); + for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { + double qusage = 100.0 * stats.numUsed[prio] / stats.size; + printf("%8s %15d %10d %6d %6.1f %11d\n", + threadNamePrefix[prio], stats.maxUsed[prio], + stats.numUsed[prio], stats.size, qusage, + stats.numOverflow[prio]); + } } } diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index c94632794..e0e14e0d2 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -727,8 +727,8 @@ int scanOnceSetQueueSize(int size) int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result) { - if (!onceQ) return -1; int ret; + if (!onceQ) return -1; if (result) { result->size = epicsRingBytesSize(onceQ) / sizeof(onceEntry); result->numUsed = epicsRingBytesUsedBytes(onceQ) / sizeof(onceEntry); @@ -750,13 +750,13 @@ void scanOnceQueuePrintStatus(const int reset) if (scanOnceQueueStatus(reset, &stats) == -1) { fprintf(stderr, "scanOnce system not initialized, yet. Please run " "iocInit before using this command.\n"); - return; + } else { + double qusage = 100.0 * stats.numUsed / stats.size; + printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n"); + printf("%8s %15d %10d %6d %6.1f %11d\n", "scanOnce", stats.maxUsed, + stats.numUsed, stats.size, qusage, + epicsAtomicGetIntT(&onceQOverruns)); } - - printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n"); - double qusage = 100.0 * stats.numUsed / stats.size; - printf("%8s %15d %10d %6d %6.1f %11d\n", "scanOnce", stats.maxUsed, stats.numUsed, stats.size, qusage, - epicsAtomicGetIntT(&onceQOverruns)); } static void initOnce(void) diff --git a/src/libCom/ring/epicsRingBytes.c b/src/libCom/ring/epicsRingBytes.c index a976d8412..c38e32b77 100644 --- a/src/libCom/ring/epicsRingBytes.c +++ b/src/libCom/ring/epicsRingBytes.c @@ -135,8 +135,8 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( return 0; } if (nbytes) { - memcpy ((void *)&pring->buffer[nextPut], value, nbytes); int curUsed = pring->size - SLOP - freeCount; + memcpy ((void *)&pring->buffer[nextPut], value, nbytes); if (curUsed > epicsAtomicGetIntT(&pring->highWaterMark)) { epicsAtomicSetIntT(&pring->highWaterMark, curUsed); } @@ -152,8 +152,8 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( topCount = size - nextPut; copyCount = (nbytes > topCount) ? topCount : nbytes; if (copyCount) { - memcpy ((void *)&pring->buffer[nextPut], value, copyCount); int curUsed = pring->size - SLOP - freeCount; + memcpy ((void *)&pring->buffer[nextPut], value, copyCount); if (curUsed > epicsAtomicGetIntT(&pring->highWaterMark)) { epicsAtomicSetIntT(&pring->highWaterMark, curUsed); } From 040f9013f461346e924ba10df04a723dd7c55f3b Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Tue, 27 Nov 2018 10:45:35 -0500 Subject: [PATCH 16/34] Remove unneeded epicsAtomic from callbackQueueSize --- src/ioc/db/callback.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/callback.c b/src/ioc/db/callback.c index 253d2b86b..1b6c24906 100644 --- a/src/ioc/db/callback.c +++ b/src/ioc/db/callback.c @@ -100,7 +100,7 @@ int callbackSetQueueSize(int size) fprintf(stderr, "Callback system already initialized\n"); return -1; } - epicsAtomicSetIntT(&callbackQueueSize, size); + callbackQueueSize = size; return 0; } @@ -110,7 +110,7 @@ int callbackQueueStatus(const int reset, callbackQueueStats *result) if (!callbackIsInit) return -1; if (result) { int prio; - result->size = epicsAtomicGetIntT(&callbackQueueSize); + result->size = callbackQueueSize; for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { epicsRingPointerId qId = callbackQueue[prio].queue; result->numUsed[prio] = epicsRingPointerGetUsed(qId); @@ -286,7 +286,7 @@ void callbackInit(void) epicsThreadId tid; callbackQueue[i].semWakeUp = epicsEventMustCreate(epicsEventEmpty); - callbackQueue[i].queue = epicsRingPointerLockedCreate(epicsAtomicGetIntT(&callbackQueueSize)); + callbackQueue[i].queue = epicsRingPointerLockedCreate(callbackQueueSize); if (callbackQueue[i].queue == 0) cantProceed("epicsRingPointerLockedCreate failed for %s\n", threadNamePrefix[i]); From 9e999d2bef5c9341db5f4fa25fc9ec4df3d1844a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 27 Nov 2018 14:11:11 -0600 Subject: [PATCH 17/34] Only create msi-copy for a Host build-arch --- configure/RULES_BUILD | 2 ++ src/ioc/dbtemplate/test/Makefile | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 6fac21753..ddab8e183 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -43,6 +43,7 @@ LOADABLE_LIBRARY += $(LOADABLE_LIBRARY_HOST) OBJS += $(OBJS_HOST) PROD += $(PROD_HOST) SCRIPTS += $(SCRIPTS_HOST) +TARGETS += $(TARGETS_HOST) TESTLIBRARY += $(TESTLIBRARY_HOST) TESTSCRIPTS += $(TESTSCRIPTS_HOST) TESTPROD += $(TESTPROD_HOST) @@ -54,6 +55,7 @@ LOADABLE_LIBRARY += $(LOADABLE_LIBRARY_IOC) OBJS += $(OBJS_IOC) PROD += $(PROD_IOC) SCRIPTS += $(SCRIPTS_IOC) +TARGETS += $(TARGETS_IOC) TESTLIBRARY += $(TESTLIBRARY_IOC) TESTSCRIPTS += $(TESTSCRIPTS_IOC) TESTPROD += $(TESTPROD_IOC) diff --git a/src/ioc/dbtemplate/test/Makefile b/src/ioc/dbtemplate/test/Makefile index dc73eacd0..94dcadca0 100644 --- a/src/ioc/dbtemplate/test/Makefile +++ b/src/ioc/dbtemplate/test/Makefile @@ -18,7 +18,7 @@ TESTS += msi TESTSCRIPTS_HOST += $(TESTS:%=%.t) ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS))) - TARGETS += msi-copy$(EXE) + TARGETS_HOST += msi-copy$(EXE) endif include $(TOP)/configure/RULES From a43b805b6500c71054dd0eb3a3b33b252a44b89f Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 30 Nov 2018 17:19:56 +0100 Subject: [PATCH 18/34] ca/pcas: fix misleading error message (TCP name resolution) --- src/ca/legacy/pcas/generic/casStrmClient.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ca/legacy/pcas/generic/casStrmClient.cc b/src/ca/legacy/pcas/generic/casStrmClient.cc index 5eebfeb0a..5c84cabcd 100644 --- a/src/ca/legacy/pcas/generic/casStrmClient.cc +++ b/src/ca/legacy/pcas/generic/casStrmClient.cc @@ -1373,7 +1373,7 @@ caStatus casStrmClient :: searchAction ( epicsGuard < casClientMutex > & guard ) if ( pChanName[0] == '\0' ) { caServerI::dumpMsg ( this->pHostName, "?", mp, this->ctx.getData(), - "zero length PV name in UDP search request?\n" ); + "zero length PV name in TCP search request?\n" ); return S_cas_success; } @@ -1383,7 +1383,7 @@ caStatus casStrmClient :: searchAction ( epicsGuard < casClientMutex > & guard ) for ( unsigned i = mp->m_postsize-1; pChanName[i] != '\0'; i-- ) { if ( i <= 1 ) { caServerI::dumpMsg ( pHostName, "?", mp, this->ctx.getData(), - "unterminated PV name in UDP search request?\n" ); + "unterminated PV name in TCP search request?\n" ); return S_cas_success; } } From d436561cb22fd847d187956178d78effd690adf7 Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Thu, 29 Nov 2018 19:12:48 -0500 Subject: [PATCH 19/34] Add tests for highWaterMark feature --- src/libCom/test/ringBytesTest.c | 34 +++++++++++++++++++------------ src/libCom/test/ringPointerTest.c | 9 +++++++- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/libCom/test/ringBytesTest.c b/src/libCom/test/ringBytesTest.c index 6cef93334..bb91d0201 100644 --- a/src/libCom/test/ringBytesTest.c +++ b/src/libCom/test/ringBytesTest.c @@ -30,7 +30,8 @@ typedef struct info { epicsRingBytesId ring; }info; -static void check(epicsRingBytesId ring, int expectedFree) +static void check(epicsRingBytesId ring, int expectedFree, + int expectedHighWaterMark) { int expectedUsed = RINGSIZE - expectedFree; int expectedEmpty = (expectedUsed == 0); @@ -39,11 +40,14 @@ static void check(epicsRingBytesId ring, int expectedFree) int nUsed = epicsRingBytesUsedBytes(ring); int isEmpty = epicsRingBytesIsEmpty(ring); int isFull = epicsRingBytesIsFull(ring); + int highWaterMark = epicsRingBytesHighWaterMark(ring); testOk(nFree == expectedFree, "Free: %d == %d", nFree, expectedFree); testOk(nUsed == expectedUsed, "Used: %d == %d", nUsed, expectedUsed); testOk(isEmpty == expectedEmpty, "Empty: %d == %d", isEmpty, expectedEmpty); testOk(isFull == expectedFull, "Full: %d == %d", isFull, expectedFull); + testOk(highWaterMark == expectedHighWaterMark, "HighWaterMark: %d == %d", + highWaterMark, expectedHighWaterMark); } MAIN(ringBytesTest) @@ -55,7 +59,7 @@ MAIN(ringBytesTest) char get[RINGSIZE+1]; epicsRingBytesId ring; - testPlan(245); + testPlan(292); pinfo = calloc(1,sizeof(info)); if (!pinfo) { @@ -70,50 +74,54 @@ MAIN(ringBytesTest) if (!ring) { testAbort("epicsRingBytesCreate failed"); } - check(ring, RINGSIZE); + check(ring, RINGSIZE, 0); for (i = 0 ; i < sizeof(put) ; i++) put[i] = i; for(i = 0 ; i < RINGSIZE ; i++) { n = epicsRingBytesPut(ring, put, i); testOk(n==i, "ring put %d", i); - check(ring, RINGSIZE-i); + check(ring, RINGSIZE-i, i); n = epicsRingBytesGet(ring, get, i); testOk(n==i, "ring get %d", i); - check(ring, RINGSIZE); + check(ring, RINGSIZE, i); testOk(memcmp(put,get,i)==0, "get matches write"); } + epicsRingBytesResetHighWaterMark(ring); + for(i = 0 ; i < RINGSIZE ; i++) { n = epicsRingBytesPut(ring, put+i, 1); testOk(n==1, "ring put 1, %d", i); - check(ring, RINGSIZE-1-i); + check(ring, RINGSIZE-1-i, i + 1); } n = epicsRingBytesPut(ring, put+RINGSIZE, 1); testOk(n==0, "put to full ring"); - check(ring, 0); + check(ring, 0, RINGSIZE); for(i = 0 ; i < RINGSIZE ; i++) { n = epicsRingBytesGet(ring, get+i, 1); testOk(n==1, "ring get 1, %d", i); - check(ring, 1+i); + check(ring, 1+i, RINGSIZE); } testOk(memcmp(put,get,RINGSIZE)==0, "get matches write"); n = epicsRingBytesGet(ring, get+RINGSIZE, 1); testOk(n==0, "get from empty ring"); - check(ring, RINGSIZE); + check(ring, RINGSIZE, RINGSIZE); + + epicsRingBytesResetHighWaterMark(ring); n = epicsRingBytesPut(ring, put, RINGSIZE+1); testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); - check(ring, RINGSIZE); + check(ring, RINGSIZE, 0); n = epicsRingBytesPut(ring, put, 1); testOk(n==1, "ring put %d", 1); - check(ring, RINGSIZE-1); + check(ring, RINGSIZE-1, 1); n = epicsRingBytesPut(ring, put, RINGSIZE); testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); - check(ring, RINGSIZE-1); + check(ring, RINGSIZE-1, 1); n = epicsRingBytesGet(ring, get, 1); testOk(n==1, "ring get %d", 1); - check(ring, RINGSIZE); + check(ring, RINGSIZE, 1); epicsRingBytesDelete(ring); epicsEventDestroy(consumerEvent); diff --git a/src/libCom/test/ringPointerTest.c b/src/libCom/test/ringPointerTest.c index 65a349489..d351708b5 100644 --- a/src/libCom/test/ringPointerTest.c +++ b/src/libCom/test/ringPointerTest.c @@ -64,6 +64,7 @@ static void testSingle(void) testOk1(epicsRingPointerGetFree(ring)==rsize); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==0); + testOk1(epicsRingPointerGetHighWaterMark(ring)==0); testOk1(epicsRingPointerPop(ring)==NULL); @@ -75,6 +76,10 @@ static void testSingle(void) testOk1(epicsRingPointerGetFree(ring)==rsize-1); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==1); + testOk1(epicsRingPointerGetHighWaterMark(ring)==1); + + epicsRingPointerResetHighWaterMark(ring); + testOk1(epicsRingPointerGetHighWaterMark(ring)==1); testDiag("Fill it up"); for(i=2; i<2*rsize; i++) { @@ -92,6 +97,7 @@ static void testSingle(void) testOk1(epicsRingPointerGetFree(ring)==0); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==rsize); + testOk1(epicsRingPointerGetHighWaterMark(ring)==rsize); testDiag("Drain it out"); for(i=1; i<2*rsize; i++) { @@ -108,6 +114,7 @@ static void testSingle(void) testOk1(epicsRingPointerGetFree(ring)==rsize); testOk1(epicsRingPointerGetSize(ring)==rsize); testOk1(epicsRingPointerGetUsed(ring)==0); + testOk1(epicsRingPointerGetHighWaterMark(ring)==rsize); testDiag("Fill it up again"); for(i=2; i<2*rsize; i++) { @@ -236,7 +243,7 @@ MAIN(ringPointerTest) { int prio = epicsThreadGetPrioritySelf(); - testPlan(37); + testPlan(42); testSingle(); if (prio) epicsThreadSetPriority(epicsThreadGetIdSelf(), epicsThreadPriorityScanLow); From 10d951e2d79cd48144ce32bcac2c99d44d156f09 Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Thu, 29 Nov 2018 19:14:55 -0500 Subject: [PATCH 20/34] Fix incorrect value for highWaterMark in epicsRingBytes --- src/libCom/ring/epicsRingBytes.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/libCom/ring/epicsRingBytes.c b/src/libCom/ring/epicsRingBytes.c index c38e32b77..a9b13b990 100644 --- a/src/libCom/ring/epicsRingBytes.c +++ b/src/libCom/ring/epicsRingBytes.c @@ -121,7 +121,7 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( { ringPvt *pring = (ringPvt *)id; int nextGet, nextPut, size; - int freeCount, copyCount, topCount; + int freeCount, copyCount, topCount, used; if (pring->lock) epicsSpinLock(pring->lock); nextGet = pring->nextGet; @@ -135,11 +135,7 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( return 0; } if (nbytes) { - int curUsed = pring->size - SLOP - freeCount; memcpy ((void *)&pring->buffer[nextPut], value, nbytes); - if (curUsed > epicsAtomicGetIntT(&pring->highWaterMark)) { - epicsAtomicSetIntT(&pring->highWaterMark, curUsed); - } } nextPut += nbytes; } @@ -152,11 +148,7 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( topCount = size - nextPut; copyCount = (nbytes > topCount) ? topCount : nbytes; if (copyCount) { - int curUsed = pring->size - SLOP - freeCount; memcpy ((void *)&pring->buffer[nextPut], value, copyCount); - if (curUsed > epicsAtomicGetIntT(&pring->highWaterMark)) { - epicsAtomicSetIntT(&pring->highWaterMark, curUsed); - } } nextPut += copyCount; if (nextPut == size) { @@ -168,6 +160,12 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( } pring->nextPut = nextPut; + used = nextPut - nextGet; + if (used < 0) used += pring->size; + if (used > epicsAtomicGetIntT(&pring->highWaterMark)) { + epicsAtomicSetIntT(&pring->highWaterMark, used); + } + if (pring->lock) epicsSpinUnlock(pring->lock); return nbytes; } From 87761ebf290dfa853b2a3fb6b99f8c0da7fe998f Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Mon, 3 Dec 2018 12:01:47 -0500 Subject: [PATCH 21/34] Prevent data race between resetHighWaterMark() and put() --- src/libCom/ring/epicsRingBytes.c | 6 +++--- src/libCom/ring/epicsRingPointer.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libCom/ring/epicsRingBytes.c b/src/libCom/ring/epicsRingBytes.c index a9b13b990..ec9e5094d 100644 --- a/src/libCom/ring/epicsRingBytes.c +++ b/src/libCom/ring/epicsRingBytes.c @@ -121,7 +121,7 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( { ringPvt *pring = (ringPvt *)id; int nextGet, nextPut, size; - int freeCount, copyCount, topCount, used; + int freeCount, copyCount, topCount, used, oldHWM; if (pring->lock) epicsSpinLock(pring->lock); nextGet = pring->nextGet; @@ -162,8 +162,8 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( used = nextPut - nextGet; if (used < 0) used += pring->size; - if (used > epicsAtomicGetIntT(&pring->highWaterMark)) { - epicsAtomicSetIntT(&pring->highWaterMark, used); + while(oldHWM = epicsAtomicGetIntT(&pring->highWaterMark), oldHWM < used) { + epicsAtomicCmpAndSwapIntT(&pring->highWaterMark, oldHWM, used); } if (pring->lock) epicsSpinUnlock(pring->lock); diff --git a/src/libCom/ring/epicsRingPointer.h b/src/libCom/ring/epicsRingPointer.h index b62923883..2b4f1b172 100644 --- a/src/libCom/ring/epicsRingPointer.h +++ b/src/libCom/ring/epicsRingPointer.h @@ -131,8 +131,8 @@ inline bool epicsRingPointer::push(T *p) buffer[next] = p; nextPush = newNext; int used = getUsedNoLock(); - if (used > epicsAtomicGetIntT(&highWaterMark)) { - epicsAtomicSetIntT(&highWaterMark, used); + while(int oldHWM = epicsAtomicGetIntT(&highWaterMark), oldHWM < used) { + epicsAtomicCmpAndSwapIntT(&highWaterMark, oldHWM, used); } if (lock) epicsSpinUnlock(lock); return(true); From 6f919c3991bc264e91a313415a402250bab463ac Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Tue, 4 Dec 2018 12:00:26 -0500 Subject: [PATCH 22/34] Simpler implementation using spin lock rather than atomics --- src/libCom/ring/epicsRingBytes.c | 16 +++++++++------- src/libCom/ring/epicsRingPointer.h | 11 +++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/libCom/ring/epicsRingBytes.c b/src/libCom/ring/epicsRingBytes.c index ec9e5094d..ab048e467 100644 --- a/src/libCom/ring/epicsRingBytes.c +++ b/src/libCom/ring/epicsRingBytes.c @@ -21,7 +21,6 @@ #include #define epicsExportSharedSymbols -#include "epicsAtomic.h" #include "epicsSpin.h" #include "dbDefs.h" #include "epicsRingBytes.h" @@ -121,7 +120,7 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( { ringPvt *pring = (ringPvt *)id; int nextGet, nextPut, size; - int freeCount, copyCount, topCount, used, oldHWM; + int freeCount, copyCount, topCount, used; if (pring->lock) epicsSpinLock(pring->lock); nextGet = pring->nextGet; @@ -162,9 +161,7 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( used = nextPut - nextGet; if (used < 0) used += pring->size; - while(oldHWM = epicsAtomicGetIntT(&pring->highWaterMark), oldHWM < used) { - epicsAtomicCmpAndSwapIntT(&pring->highWaterMark, oldHWM, used); - } + if (used > pring->highWaterMark) pring->highWaterMark = used; if (pring->lock) epicsSpinUnlock(pring->lock); return nbytes; @@ -239,11 +236,16 @@ epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id) epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id) { ringPvt *pring = (ringPvt *)id; - return epicsAtomicGetIntT(&pring->highWaterMark); + return pring->highWaterMark; } epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id) { ringPvt *pring = (ringPvt *)id; - epicsAtomicSetIntT(&pring->highWaterMark, epicsRingBytesUsedBytes(id)); + int used; + if (pring->lock) epicsSpinLock(pring->lock); + used = pring->nextGet - pring->nextPut; + if (used < 0) used += pring->size; + pring->highWaterMark = used; + if (pring->lock) epicsSpinUnlock(pring->lock); } diff --git a/src/libCom/ring/epicsRingPointer.h b/src/libCom/ring/epicsRingPointer.h index 2b4f1b172..68bf8f5a6 100644 --- a/src/libCom/ring/epicsRingPointer.h +++ b/src/libCom/ring/epicsRingPointer.h @@ -23,7 +23,6 @@ * epicsRingPointerLocked uses a spinlock. */ -#include "epicsAtomic.h" #include "epicsSpin.h" #include "shareLib.h" @@ -131,9 +130,7 @@ inline bool epicsRingPointer::push(T *p) buffer[next] = p; nextPush = newNext; int used = getUsedNoLock(); - while(int oldHWM = epicsAtomicGetIntT(&highWaterMark), oldHWM < used) { - epicsAtomicCmpAndSwapIntT(&highWaterMark, oldHWM, used); - } + if (used > highWaterMark) highWaterMark = used; if (lock) epicsSpinUnlock(lock); return(true); } @@ -219,13 +216,15 @@ inline bool epicsRingPointer::isFull() const template inline int epicsRingPointer::getHighWaterMark() const { - return epicsAtomicGetIntT(&highWaterMark); + return highWaterMark; } template inline void epicsRingPointer::resetHighWaterMark() { - epicsAtomicSetIntT(&highWaterMark, getUsed()); + if (lock) epicsSpinLock(lock); + highWaterMark = getUsedNoLock(); + if (lock) epicsSpinUnlock(lock); } #endif /* __cplusplus */ From aab5693b4567934279ecaacad45226b95ae07914 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 5 Dec 2018 00:59:36 -0600 Subject: [PATCH 23/34] Fix for CA link disconnect detection dbCa's doLocked() method must run the callback even when the link is disconnected to allow alarms to be triggered by softDev support. Patch from Sebastian Marsching Also removes testToDo from rec/test/regressTest.c Fixes lp: #1798855 --- src/ioc/db/dbCa.c | 7 ++++++- src/std/rec/test/regressTest.c | 2 -- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 65a8327cf..843fbfc0c 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -703,7 +703,12 @@ static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) caLink *pca; long status; - pcaGetCheck + assert(plink); + if (plink->type != CA_LINK) return -1; + pca = (caLink *)plink->value.pv_link.pvt; + assert(pca); + epicsMutexMustLock(pca->lock); + assert(pca->plink); status = rtn(plink, priv); epicsMutexUnlock(pca->lock); return status; diff --git a/src/std/rec/test/regressTest.c b/src/std/rec/test/regressTest.c index 0c68f7ffb..661463984 100644 --- a/src/std/rec/test/regressTest.c +++ b/src/std/rec/test/regressTest.c @@ -133,10 +133,8 @@ void testCADisconn(void) startRegressTestIoc("badCaLink.db"); testdbPutFieldOk("ai:disconn.PROC", DBF_LONG, 1); - testTodoBegin("lp:1798855"); testdbGetFieldEqual("ai:disconn.SEVR", DBF_LONG, INVALID_ALARM); testdbGetFieldEqual("ai:disconn.STAT", DBF_LONG, LINK_ALARM); - testTodoEnd(); } From 0fae0fcc1781dbd46434ed7062b6372e51266d8c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 29 Nov 2018 14:23:14 -0600 Subject: [PATCH 24/34] Rename various dbd files to dbd.pod --- src/ioc/db/{menuScan.dbd => menuScan.dbd.pod} | 0 src/std/rec/{biRecord.dbd => biRecord.dbd.pod} | 0 src/std/rec/{boRecord.dbd => boRecord.dbd.pod} | 0 src/std/rec/{calcRecord.dbd => calcRecord.dbd.pod} | 0 src/std/rec/{calcoutRecord.dbd => calcoutRecord.dbd.pod} | 0 src/std/rec/{dfanoutRecord.dbd => dfanoutRecord.dbd.pod} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename src/ioc/db/{menuScan.dbd => menuScan.dbd.pod} (100%) rename src/std/rec/{biRecord.dbd => biRecord.dbd.pod} (100%) rename src/std/rec/{boRecord.dbd => boRecord.dbd.pod} (100%) rename src/std/rec/{calcRecord.dbd => calcRecord.dbd.pod} (100%) rename src/std/rec/{calcoutRecord.dbd => calcoutRecord.dbd.pod} (100%) rename src/std/rec/{dfanoutRecord.dbd => dfanoutRecord.dbd.pod} (100%) diff --git a/src/ioc/db/menuScan.dbd b/src/ioc/db/menuScan.dbd.pod similarity index 100% rename from src/ioc/db/menuScan.dbd rename to src/ioc/db/menuScan.dbd.pod diff --git a/src/std/rec/biRecord.dbd b/src/std/rec/biRecord.dbd.pod similarity index 100% rename from src/std/rec/biRecord.dbd rename to src/std/rec/biRecord.dbd.pod diff --git a/src/std/rec/boRecord.dbd b/src/std/rec/boRecord.dbd.pod similarity index 100% rename from src/std/rec/boRecord.dbd rename to src/std/rec/boRecord.dbd.pod diff --git a/src/std/rec/calcRecord.dbd b/src/std/rec/calcRecord.dbd.pod similarity index 100% rename from src/std/rec/calcRecord.dbd rename to src/std/rec/calcRecord.dbd.pod diff --git a/src/std/rec/calcoutRecord.dbd b/src/std/rec/calcoutRecord.dbd.pod similarity index 100% rename from src/std/rec/calcoutRecord.dbd rename to src/std/rec/calcoutRecord.dbd.pod diff --git a/src/std/rec/dfanoutRecord.dbd b/src/std/rec/dfanoutRecord.dbd.pod similarity index 100% rename from src/std/rec/dfanoutRecord.dbd rename to src/std/rec/dfanoutRecord.dbd.pod From 313afc4a4c8ed024e721822aca8ce446320f2424 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 29 Nov 2018 16:07:14 -0600 Subject: [PATCH 25/34] Fix HTMLS generation from IOC menu*.dbd.pod files --- src/ioc/db/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index 83cd2ce4a..5092092e9 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -58,7 +58,7 @@ DBDINC += menuScan DBDINC += dbCommon dbMenusPod = $(notdir $(wildcard ../db/menu*.dbd.pod)) -HTMLS += $(patsubst %.dbd.pod,%.html,$(menusPod)) +HTMLS += $(patsubst %.dbd.pod,%.html,$(dbMenusPod)) dbCore_SRCS += dbLock.c dbCore_SRCS += dbAccess.c From 444cac337cc716141ae25a9aae59c2f4ba836520 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 29 Nov 2018 16:08:12 -0600 Subject: [PATCH 26/34] Add POD text for menuScan --- src/ioc/db/menuScan.dbd.pod | 39 ++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/menuScan.dbd.pod b/src/ioc/db/menuScan.dbd.pod index 17fedd59b..e716e79da 100644 --- a/src/ioc/db/menuScan.dbd.pod +++ b/src/ioc/db/menuScan.dbd.pod @@ -1,11 +1,48 @@ #************************************************************************* -# Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +# Copyright (c) 2018 UChicago Argonne LLC, 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 is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* + +=head1 Menu menuScan + +This menu is used for the C field of all record types. + +The set of periodic scan rates may be modified for an individual IOC by +copying the F file from Base into the IOC's source +directory and changing it to contain the desired scan rates. + +The scan periods are extracted from the choice strings at runtime, which +must be expressed as a number with any of the following units appended: + +=over 4 + + second + seconds + minute + minutes + hour + hours + Hertz + Hz + +=back + +At IOC start-up a separate scan thread will be created for each period, +with thread priority increasing further down the list, so faster periods +should appear after slower ones. + +Scan rates that cannot be achieved will generate a warning message from +the C command. + + +=menu menuScan + +=cut + menu(menuScan) { choice(menuScanPassive,"Passive") choice(menuScanEvent,"Event") From 6664ccfc6494040cac45a00489fb1ab71a23969d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 29 Nov 2018 16:35:53 -0600 Subject: [PATCH 27/34] Wiki to POD conversions for bi, bo, calc, calcout and dfanout These still need going through to update and edit. Conversions by Tony Pietryla, Argonne. --- src/std/rec/biRecord.dbd.pod | 357 ++++++++++++++ src/std/rec/boRecord.dbd.pod | 429 +++++++++++++++++ src/std/rec/calcRecord.dbd.pod | 626 ++++++++++++++++++++++++- src/std/rec/calcoutRecord.dbd.pod | 743 +++++++++++++++++++++++++++++- src/std/rec/dfanoutRecord.dbd.pod | 250 ++++++++++ 5 files changed, 2403 insertions(+), 2 deletions(-) diff --git a/src/std/rec/biRecord.dbd.pod b/src/std/rec/biRecord.dbd.pod index fb3588fb1..a711435ba 100644 --- a/src/std/rec/biRecord.dbd.pod +++ b/src/std/rec/biRecord.dbd.pod @@ -6,7 +6,164 @@ # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* + +=title Binary Input Record (bi) + +This record type is normally used to obtain a binary value of 0 or 1. Most +device support modules obtain values from hardware and place the value in +RVAL. For these devices, record processing sets VAL = (0,1) if RVAL is (0, +not 0). Device support modules may optionally read a value directly from +VAL. + +Soft device modules are provided to obtain input via database or channel +access links via dbPutField or dbPutLink requests. Two soft device support +modules are provided: C and C. The first +allows VAL to be an arbitrary unsigned short integer. The second reads the +value into RVAL just like normal hardware modules. + +=head2 Parameter Fields + +The binary input's fields fall into the following categories: + +=over + +=item * +scan Parameters + +=item * +read and convert parameters + +=item * +operator display parameters + +=item * +alarm parameters + +=item * +run-time parameters + +=back + +=recordtype bi + +=cut + recordtype(bi) { + +=head3 Scan Parameters + +The binary input record has the standard fields for specifying under what +circumstances the record will be processed. These fields are listed in +L. In addition, L explains how these +fields are used. Note that I/O event scanning is only supported for those +card types that interrupt. + +=fields SCAN + +=head3 Read and Convert Parameters + +The read and convert fields determine where the binary input gets its +input from and how to convert the raw signal to engineering units. The INP +field contains the address from where device support retrieves the value. +If the binary input record gets its value from hardware, the address of the +card must be entered in the INP field, and the name of the device support +module must be entered in the DTYP field. See L

for +information on the format of the hardware address. Be aware that the format +differs between types of cards. You can see a list of device support +modules currently supported at the user's local site by using C +utility (R3.13). + +For records that specify C or C device +support routines, the INP field can be a channel or a database link, or a +constant. If a constant, VAL can be changed directly by dbPuts. See +L
for information on the format of database and +channel access addresses. Also, see L in +this chapter for information on soft device support. + +If the record gets its values from hardware or uses the C +device support, the device support routines place the value in the RVAL +field which is then converted using the process described in the next +section. + +=fields INP, DTYP, ZNAM, ONAM, RVAL, VAL + +=head3 Conversion Fields + +The VAL field is set equal to (0,1) if the RVAL field is (0, not 0), unless +the device support module reads a value directly into VAL or the +C device support is used. The value can also be fetched as one of +the strings specified in the ZNAM or ONAM fields. The ZNAM field has a +string that corresponds to the 0 state, so when the value is fetched as +this string, C will return a 0. The ONAM field hold the +string that corresponds to the 1 state, so when the value is fetched as +this string, C returns a 1. + +=fields ZNAM, ONAM + +=head3 Operator Display Parameters + +These parameters are used to present meaningful data to the operator. The +C record support routine can retrieve the state string +corresponding to the VAL's state. If the value is 1, C will +return the string in the ONAM field; and if 0, C will return +the ZNAM string. + +See L for more on the record name (NAME) +and description (DESC) fields. + +=fields ZNAM, ONAM, NAME, DESC + +=head3 Alarm Parameters + +These parameters are used to determine if the binary input is in alarm +condition and to determine the severity of that condition. The possible +alarm conditions for binary inputs are the SCAN, READ state alarms, and the +change of state alarm. The SCAN and READ alarms are called by the device +supprt routines. + +The user can choose the severity of each state in the ZSV and OSV fields. +The possible values for these fields are C, C, and +C. The ZSV field holds the severity for the zero state; OSV, for +the one state. COSV causes an alarm whenever the state changes between +0 and 1 and the severity is configured as MINOR or MAJOR. + +See L for a complete explanation of the discrete alarm +states. L lists other fields related to alarms that are +common to all record types. + +=fields ZSV, OSV, COSV + +=head3 Run-time Parameters and Simulation Mode Parameters + +These parameters are used by the run-time code for processing the binary +input. They are not configured using a database configuration tool. + +ORAW is used to determine if monitors should be triggered for RVAL at the same +time they are triggered for VAL. + +MASK is given a value by ithe device support routines. This value is used to +manipulate the record's value, but is only the concern of the hardware device +support routines. + +The LALM fields holds the value of the last occurence of the change of +state alarm. It is used to implement the change of state alarm, and thus +only has meaning if COSV is MAJOR or MINOR. + +The MSLT field is used by the C record support routine to +determine if archive and value change monitors are invoked. They are if MSLT +is not equal to VAL. + +=fields ORAW, MASK, LALM, MLST + +The following fields are used to operate the binary input in simulation +mode. See L for more information on +these fields. + +=fields SIOL, SVAL, SIML, SIMM, SIMS + + +=cut + include "dbCommon.dbd" field(INP,DBF_INLINK) { prompt("Input Specification") @@ -104,4 +261,204 @@ recordtype(bi) { interest(2) menu(menuAlarmSevr) } + +=head2 Record Support + +=head3 Record Support Routines + +=head2 C + +This routine initializes SIMM with the value of SIML if SIML type is a +CONSTANT link or creates a channel access link if SIML type is PV_LINK. +SVAL is likewise initialized if SIOL is a CONSTANT or PV_LINK. + +This routine next checks to see that device support is available and a +device support routine is defined. If neither exist, an error is issued and +processing is terminated. + +If device support includes C, it is called. + +=head2 C + +See next section. + +=head2 C + +Fills in the values of struct valueDes so that they refer to VAL. + +=head2 C + +Retrieves ASCII string corresponding to VAL. + +=head2 C + +Retrieves ASCII strings for ZNAM and ONAM. + +=head2 C + +Check if string matches ZNAM or ONAM, and if it does, sets VAL. + +=head2 Record Processing + +Routine process implements the following algorithm: + +=over 1 + +=item 1. +Check to see that the appropriate device support module exists. If it +doesn't, an error message is issued and processing is terminated with +the PACT field still set to TRUE. This ensures that processes will no +longer be called for this record. Thus error storms will not occur. + +=item 2. +C is called. See L for details. + +=item 3. +If PACT has been changed to TRUE, the device support read routine has +started but has not completed reading a new input value. In this case, the +processing routine merely returns, leaving PACT TRUE. + +=item 4. +Convert. + +=back + +=over 1 + +=item * +status = read_bi + +=item * +PACT = TRUE + +=item * +TIME = tslocaltime + +=item * +if status is 0, then set VAL=(0,1) if RVAL is (0, not 0) and UDF = False. + +=item * +if status is 2, set status = 0 + +=back + +=over 1 + +=item 5. +Check alarms: This routine checks to see if the new VAL causes the alarm +status and severity to change. If so, NSEV, NSTA and LALM are set. Note +that if VAL is greater than 1, no checking is performed. + +=item 6. +Check if monitors should be invoked: + +=back + +=over 1 + +=item * +Alarm monitors are invoked if the alarm status or severity has changed. + +=item * +Archive and value change monitors are invoked if MSLT is not equal to VAL. + +=item * +Monitors for RVAL are checked whenever other monitors are invoked. + +=item * +NSEV and NSTA are reset to 0. + +=back + +=over 1 + +=item 7. +Scan forward link if necessary, set PACT FALSE, and return. + +=back + +=head2 Device Support + +=head3 Fields of Interest to Device Support + +Each binary input record must have an associated set of device support +routines. The primary resposibility of the device support routines is to +obtain a new raw input value whenever C is called. The device +support routines are primarily interested in the following fields: + +=fields PACT, DPVT, UDF, NSEV, NSTA, VAL, INP, RVAL, MASK + +=head3 Device Support routines + +Device support consists of the following routines: + +=head2 C + +Not currently used. + +=head2 C + +This routine is called once during IOC initialization. + +=head2 C + +This routine is optional. If provided, it is called by the record support +C routine. + +=head2 C + +This routine is called by the C system each time the record is +added or deleted from an I/O event scan list. C has the value (0,1) if +the record is being (added to, deleted from) and I/O event list. It must be +provided for any device type that can use the ioEvent scanner. + +=head2 C + +This routine must provide a new input value. It returns the following +values: + +=over + +=item 0: +Success. A new raw value is placed in RVAL. The record support module +forces VAL to be (0,1) if RVAL is (0, not 0). + +=item 2: +Success, but don't modify VAL. + +=item Other: +Error. + +=back + +=head3 Device Support for Soft Records + +Two soft device support modules, Soft Channel and Raw Soft Channel, are +provided for input records not related to actual hardware devices. The INP +link type must be either CONSTANT, DB_LINK, or CA_LINK. + +=head3 Soft Channel + +C always returns a value of 2, which means that no conversion is +performed. + +If the INP link type is CONSTANT, then the constant value is stored in VAL +by C, and the UDF is set to FALSE. VAL can be changed via +C requests. If the INP link type is PV_LINK, the C is +called by C. + +C calls C to read the current value of VAL. +See L for details. + +If the return status of C is zero, then C sets +UDF to FALSE. The status of C is returned. + +=head3 Raw Soft Channel + +This module is like the previous except that values are read into RVAL. + +C returns a value of 0. Thus the record processing routine will +force VAL to be 0 or 1. + +=cut } diff --git a/src/std/rec/boRecord.dbd.pod b/src/std/rec/boRecord.dbd.pod index fd002c368..cbeee1abf 100644 --- a/src/std/rec/boRecord.dbd.pod +++ b/src/std/rec/boRecord.dbd.pod @@ -6,7 +6,210 @@ # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* + +=title Binary Output Record (bo) + +The normal use for this record type is to store a simple bit (0 or 1) value +to be sent to a Digital Output module. It can also be used to write binary +values into other records via database or channel access links. This record +can implement both latched and momentary binary outputs depending on how +the HIGH field is configured. + +=head2 Parameter Fields + +The binary output's fields fall into the following categories: + +=over 1 + +=item * +scan parameters + +=item * +convert and write parameters + +=item * +operator display parameters + +=item * +alarm parameters + +=item * +run-time parameters + +=back + +=recordtype bo + +=cut + recordtype(bo) { + +=head3 Scan Parameters + +The binary output record has the standard fields for specifying under what +circumstances the record will be processed. The fields are listed in +L. In addition, L explains how these +fields are used. Note that I/O event scanning is only supported for those card +types that interrupt. + +=fields SCAN + +=head3 Desired Output Parameters + +The binary output record must specify where its desired output originates. +The desired output needs to be in engineering units. + +The first field that determines where the desired output originates is the +output mode select (OMSL) field, which can have two possible values: +C or C. If C is specified, the value +in the VAL field can be set externally via dbPuts at run-time. If +C is specified, the VAL field's value is obtained from the +address specified in the desired output location (DOL) field which can be a +database link, a channel access link, or a constant. To achieve continuous +control, a database link to a control algorithm record should be entered in +the DOL field. + +L
presents more information on database addresses +and links. L explaines the effect of database +linkage on scanning. + +=fields DOL, OMSL + +=head3 Convert and Write Parameters + +These parameters are used to determine where the binary output writes to +and how to convert the engineering units to a raw signal. After VAL is set +and forced to be either 1 or 0, as the result of either a dbPut or a new +value being retrieved from the link in the DOL field, then what happens +next depends on which device support routine is used and how the HIGH field +is configured. + +If the C device support routine is specified, then the device +support routine writes the VAL field's value to the address specified in +the OUT field. Otherwise, RVAL is the value written by the device support +routines after being converted. + +If VAL is equal to 0, then the record processing routine sets RVAL equal to +zero. When VAL is not equal to 0, then RVAL is set equal to the value +contained in the MASK field. (MASK is set by the device support routines +and is of no concern to the user.) Also, when VAL is not 0 and after RVAL is +set equal to MASK, the record processing routine checks to see if the HIGH +field is greater than 0. If it is, then the routine will process the record +again with VAL set to 0 after the number of seconds specified by HIGH. +Thus, HIGH implements a momentary output which changes the state of the +device back to 0 after I number of seconds. + +=fields DTYP, OUT, VAL, RVAL, HIGH, ZNAM, ONAM + +=head3 Conversion Parameters + +The ZNAM field has the string that corresponds to the 0 state, and the ONAM +field holds the string that corresponds to the 1 state. These fields, other +than being used to tell the operator what each state represents, are used +to perform conversions if the value fetched by DOL is a string. If it is, +VAL is set to the state which corresponds to that string. For instance, if the +value fetched is the string "Off" and the ZNAM string is "Off," then VAL is +set to 0. + +After VAL is set, if VAL is equal to 0, then the record processing routine +sets RVAL equal to zero. When VAL is not equal to 0, then RVAL is set equal +to the value contained in the MASK field. (Mask is set by the device +support routines and is of no concern to the user.) Also when VAL is equal +to 1 and after RVAL is set equal to MASK, the record processing routine checks +to see if the HIGH field is greater than 0. If it is, then the routine +processes the record again with VAL=0 after the number of seconds specified +by HIGH. Thus, HIGH implements a latched output which changes the state of +the device or link to 1, then changes it back to 0 after I number of seconds. + +=fields ZNAM, ONAM, HIGH + +=head3 Output Specification + +The OUT field specifies where the binary output record writes its output. +It must specify the address of an I/O card if the record sends its output +to hardware, and the DTYP field must contain the corresponding device +support module. Be aware that the address format differs according to the +I/O bus used. See L
for information on the format of +hardware addresses. You can see a list of device support modules currently +supported at the user's local site by using the C utility in R3.13. + +Otherwise, if the record is configured to use the soft device support +modules, then it can be either a database link, a channel access link, or a +constant. Be aware that nothing will be written when OUT is a constant. See +L
for information on the format of the database and +channel access addresses. Also, see L in +this chapter for more on output to other records. + +=head3 Operator Display Parameters + +These parameters are used to present meaningful data to the operator, The +C record support routine can retrieve the state string +corresponding to the VAL's state. So, if the value is 1, C +will return the string in the ONAM field: and if 0, C will +return the ZNAM string. + +See L for more on the record name (NAME) +and description (DESC) fields. + +=fields ZNAM, ONAM, NAME, DESC + +=head3 Alarm Parameters + +These parameters are used to determine the binary output's alarm condition +and to determine the severity of that condition. The possible alarm +conditions for binary outputs are the SCAN, READ, INVALID and state alarms. +The user can configure the state alarm conditions using these fields. + +The possible values for these fields are C, C, and +C. The ZSV holds the severity for the zero state; OSV for the one +state. COSV is used to cause an alarm whenever the state changes between +states (0-1, 1-0) and its severity is configured as MINOR or MAJOR. + +See L for more information on the IVOA and +IVOV fields. L lists other fields related to alarms that are +common to all record types. + +=fields ZSV, OSV, COSV, IVOA, IVOV + +=head3 Run-Time and Simulation Mode Parameters + +These parameters are used by the run-time code for processiong the binary +output. They are not configurable using a configuration tool. They +represent the current state of the binary output. + +ORAW is used to determine if monitors should be triggered for RVAL at the +same time they are triggered for VAL. + +MASK is given a value by the device support routines and should not concern +the user. + +The RBV field is also set by device support. It is the actual read back +value obtained from the hardware itself or from the associated device +driver. + +The ORBV field is used to decide if monitors should be triggered +for RBV at the same time monitors are triggered for changes in VAL. + +The LALM field holds the value of the last occurrence of the change of +state alarm. It is used to implement the change of state alarm, and thus +only has meaning if COSV is MINOR or MAJOR. + +The MLST is used by the C record support routine to determine if +archive and value change monitors are invoked. They are if MLST is not +equal to VAL. + +The WPDT field is a private field for honoring seconds to hold HIGH. + +=fields ORAW, MASK, RBV, ORBV, LALM, MLST, RPVT, WDPT + +The following fields are used to operate the binary output in the +simulation mode. See L for more +information on these fields. + +=fields SIOL, SIML, SIMM, SIMS + +=cut + include "dbCommon.dbd" field(VAL,DBF_ENUM) { prompt("Current Value") @@ -149,6 +352,232 @@ recordtype(bo) { promptgroup("50 - Output") interest(2) } + + +=head2 Record Support + +=head3 Record Support Routines + +=head2 C + +This routine initializes SIMM if SIML is a constant or creates a channel +access link if SIML is PV_LINK. If SIOL is a PV_LINK a channel access link +is created. + +This routine next checks to see that device support is available. The +routine next checks to see if the device support write routine is defined. + +If either device support or the device support write routine does not +exist, and error message is issued and processing is terminated. + +If DOL is a constant, then VAL is initialized to 1 if its value is nonzero +or initialzed to 0 if DOL is zero, and UDF is set to FALSE. + +If device support includes C, it is called. VAL is set using +RVAL, and UDF is set to FALSE. + +=head2 C + +See next section. + +=head2 C + +Fills in the values of struct valueDes so that they refer to VAL. + +=head2 C + +Retrieves ASCII string corresponding to VAL. + +=head2 C + +Retrieves ASCII strings for ZNAM and ONAM. + +=head2 C + +Checks if string matches ZNAM or ONAM, and if it does, sets VAL. + +=head2 Record Processing + +Routine process implements the following algorithm: + +=over 1 + +=item 1. +Check to see that the appropriate device support module exists. If it +doesn't, an error message is issued and processing is terminated with +the PACT field still set to TRUE. This ensures that processes will no +longer be called for this record. Thus error storms will not occur. + +=item 2. +If PACT is FALSE + +=back + +=over + +=item * +If DOL is DB_LINK and OMSL is CLOSED_LOOP + +=over + +=item * +get values from DOL + +=item * +check for link alarm + +=item * +force VAL to be 0 or 1 + +=item * +if MASK is defined + +=over + +=item * +if VAL is 0 set RVAL = 0 + +=back + +=item * +else set RVAL = MASK + +=back + +=back + +=over + +=item 3. +Check alarms: This routine checks to see if the new VAL causes the alarm +status and severity to change. If so, NSEV, NSTA, and LALM are set. + +=item 4. +Check severity and write the new value. See L +for more information on how INVALID alarms affect output. + +=item 5. +If PACT has been changed to TRUE, the device support write output routine +has started but has not completed writing the new value. in this case, the +processing routine merely returns, leaving PACT TRUE. + +=item 6. +Check WAIT. If VAL is 1 and WAIT is greater than 0, process again with a +VAL=0 after WAIT seconds. + +=item 7. +Check to see if monitors should be invoked. + +=back + +=over 1 + +=item * +Alarm monitors are invoked if the alarm status or severity has changed. + +=item * +Archive and value change monitors are invoked if MLST is not equal to VAL. + +=item * +Monitors for RVAL and for RBV are checked whenever other monitors are +invoked. + +=item * +NSEV and NSTA are reset to 0. + +=back + +=over + +=item 8 +Scan forward link if necessary, set PACT FALSE, and return + +=back + +=head2 Device support + +=head3 Fields Of Interest To Device Support + +Each binary output record must have an associated set of device support +routines. The primary responsibility of the device support routines is to +write a new value whenever C is called. The device support routines +are primarily interested in the following fields: + +=fields PACT, DPVT, NSEV, NSTA, VAL, OUT, RVAL, MASK, RBV + +=head3 Decive Support Routines + +Device support consists of the following routines: + +=head2 C + +Not currently used. + +=head2 C + +This routine is called once during IOC initialization. + +=head2 C + +This routine is optional. If provided, it is called by record support +C routine. It should determine MASK if it is needed. + +=over + +=item * +0: Success. RVAL modified (VAL will be set accordingly) + +=item * +2: Success. VAL modified + +=item * +other: Error + +=back + +=head2 C + +This routine is called by the ioEventScan system each time the record is +added or deleted from an I/O event scan list. C has the value (0,1) if +the record is being (added to, deleted from) an I/O event list. It must be +provided for any device type that can use the ioEvent scanner. + +=head2 C + +This routine must output a new value. It returns the following values: + +=over + +=item * +0: Success + +=item * +other: Error. + +=back + +=head2 Device Support For Soft Records + +Two soft device support modules C and C are +provided for output records not related to actual hardware devices. The OUT +link type must be either CONSTANT, DB_LINK, or CA_LINK. + +=head3 Soft Channel + +This module writes the current value of VAL. + +If the OUT link type is PV_LINK, then C is called by +C. C always returns a value of 2, which means +that no conversion will ever be attempted. C calls +C to write the current value of VAL. See L +for details. + +=head3 Raw Soft Channel + +This module is like the previous except that it writes the current value of +RVAL + +=cut } variable(boHIGHprecision, int) diff --git a/src/std/rec/calcRecord.dbd.pod b/src/std/rec/calcRecord.dbd.pod index e7eb0eee3..c50667fb3 100644 --- a/src/std/rec/calcRecord.dbd.pod +++ b/src/std/rec/calcRecord.dbd.pod @@ -4,9 +4,517 @@ # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. +# in file LICENSE that is included with this distribution. #************************************************************************* + +=title Calculation Record (calc) + +The calculation or "Calc" record is used to perform algebraic, relational, +and logical operations on values retrieved from other records. The result +of its operations can then be accessed by another record so that it can +then be used. + +=head2 Parameter Fields + +The fields in the record fall into the following categories: + +=over 1 + +=item * +scan parameters + +=item * +read parameters + +=item * +expression parameters + +=item * +operator display parameters + +=item * +alarm parameters + +=item * +monitor parameters + +=item * +run-time parameters + +=back + +=recordtype calc + +=cut + recordtype(calc) { + +=head3 Scan Parameters + +The Calc record has the standard fields for specifying under what +circumstances the record will be processed. These fields are listed in +L. In addition, L explains how these +fields are used. Since the Calc record supports no direct interfaces to +hardware, it cannot be scanned on I/O interrupt, so its SCAN field cannot +be C. + +=fields SCAN + + +=head3 Read Parameters + +The read parameters for the Calc record consist of 12 input links INPA, +INPB, ... INPL. The fields can be database links, channel access links, or +constants. If they are links, they must specify another record's field or a +channel access link. If they are constants, they will be initialized with +the value they are configured with and can be changed via C. They +cannot be hardware addresses. + +See L
for information on how to specify database +links. + +=fields INPA, INPB, INPC, INPD, INPE, INPF, INPG, INPH, INPI, INPJ, INPK, INPL + +=head3 Expression + +At the core of the Calc record lies the CALC and RPCL fields. The CALC field +contains the infix expresion which the record routine will use when it +processes the record. The resulting value is placed in the VAL field and +can be accessed from there. The CALC expression is actually converted to +opcode and stored as Reverse Polish Notation in the RPCL field. It is this +expression which is actually used to calculate VAL. The Reverse Polish +expression is evaluated more efficiently during run-time than an infix +expression. CALC can be changed at run-time, and a special record routine +calls a function to convert it to Reverse Polish Notation. + +The infix expressions that can be used are very similar to the C expression +syntax, but with some additions and subtle differences in operator meaning +and precedence. The string may contain a series of expressions separated by +a semi-colon character ";" any one of which may actually provide the +calculation result; however, all of the other expressions included must +assign their result to a variable. All alphabetic elements described below +are case independent, so upper and lower case letters may be used and mixed +in the variable and function names as desired. Spaces may be used anywhere +within an expression except between characters that make up a single +expression element. + +The range of expressions supported by the calculation record are separated +into literals, constants, operands, algebraic operators, trigonometric operators, +relational operators, logical operators, the assignment operator, +parentheses and commas, and the question mark or '?:' operator. + +=fields CALC, RPCL + +=head3 Literals + +=over 1 + +=item * +Standard double precision floating point numbers + +=item * +Inf: Infinity + +=item * +Nan: Not a Number + +=back + +=head3 Constants + +=over 1 + +=item * +PI: returns the mathematical constant E + +=item * +D2R: evaluates to E/180 which, when used as a multiplier, converts an +angle from degrees to radians + +=item * +R2D: evaluates to 180/E which as a multiplier converts an angle from +radians to degrees + +=back + +=head3 Operands + +The expression uses the values retrieved from the INPx links as operands, +though constants can be used as operands too. These values retrieved from +the input links are stored in the A-L fields. The values to be used in the +expression are simply referenced by the field letter. For instance, the +value obtained from INPA link is stored in the field A, and the value +obtained from INPB is stored in field B. The field names can be included in +the expression which will operate on their respective values, as in A+B. +Also, the RNDM nullary function can be included as an operand in the +expression in order to generate a random number between 0 and 1. + +=fields A, B, C, D, E, F, G, H, I, J, K, L + +The keyword VAL returns the current contents of the VAL field (which can be +written to by a CA put, so it might I be the result from the last time +the expression was evaluated). + +=head3 Algebraic Operators + +=over 1 + +=item * +ABS: Absolute value (unary) + +=item * +SQR: Square root (unary) + +=item * +MIN: Minimum (any number of args) + +=item * +MAX: Maximum (any number of args) + +=item * +FINITE: returns non-zero if none of the arguments are NaN or Inf (any +number of args) + +=item * +ISNAN: returns non-zero if any of the arguments is NaN or Inf (any number +of args) + +=item * +CEIL: Ceiling (unary) + +=item * +FLOOR: Floor (unary) + +=item * +LOG: Log base 10 (unary) + +=item * +LOGE: Natural log (unary) + +=item * +LN: Natural log (unary) + +=item * +EXP: Exponential function (unary) + +=item * +^ : Exponential (binary) + +=item * +** : Exponential (binary) + +=item * ++ : Addition (binary) + +=item * +- : Subtraction (binary) + +=item * +* : Multiplication (binary) + +=item * +/ : Division (binary) + +=item * +% : Modulo (binary) + +=item * +NOT: Negate (unary) + +=back + +=head3 Trigonometric Operators + +=over 1 + +=item * +SIN: Sine + +=item * +SINH: Hyperbolic sine + +=item * +ASIN: Arc sine + +=item * +COS: Cosine + +=item * +COSH: Hyperbolic cosine + +=item * +ACOS: Arc cosine + +=item * +TAN: Tangent + +=item * +TANH: Hyperbolic tangent + +=item * +ATAN: Arc tangent + +=back + +=head3 Relational Operators + +=over 1 + +=item * +>= : Greater than or equal to + +=item * +> : Greater than + +=item * +<= : Less than or equal to + +=item * +< : Less than + +=item * +# : Not equal to + +=item * += : Equal to + +=back + +=head3 Logical Operators + +=over 1 + +=item * +&& : And + +=item * +|| : Or + +=item * +! : Not + +=back + +=head3 Bitwise Operators + +=over 1 + +=item * +| : Bitwise Or + +=item * +& : Bitwise And + +=item * +OR : Bitwise Or + +=item * +AND : Bitwise And + +=item * +XOR : Bitwise Exclusive Or + +=item * +~ : One's Complement + +=item * +<< : Left shift + +=item * +>> : Right shift + +=back + +=head3 Assignment Operator + +=over 1 + +=item * +:= : assigns a value (right hand side) to a variable (i.e. field) + +=back + +=head3 Parantheses, Comma, and Semicolon + +The open and close parentheses are supported. Nested parentheses are +supported. + +The comma is supported when used to separate the arguments of a binary +function. + +The semicolon is used to separate expressions. Although only one +traditional calculation expression is allowed, multiple assignment +expressions are allowed. + +=head3 Conditional Expression + +The C language's question mark operator is supported. The format is: +C + +=head3 Expression Examples + +=head3 Algebraic + +C + +=over 1 + +=item * +Result is A + B + 10 + +=back + +=head3 Relational + +C<(A + B) < (C + D)> + +=over 1 + +=item * +Result is 1 if (A + B) < (C + D) + +=item * +Result is 0 if (A + B) >= (C + D) + +=back + +=head3 Question Mark + +C<(A + B) < (C + D) ? E : F + L + 10> + +=over 1 + +=item * +Result is E if (A + B) < (C + D) + +=item * +Result is F + L + 10 if (A + B) >= (C + D) + +=back + +Prior to Base 3.14.9 it was legal to omit the : and the second (else) part +of the conditional, like this: + +C<(A + B)<(C + D) ? E> + +=over 1 + +=item +Result is E if (A + B)<(C + D) + +=item +Result is unchanged if (A + B)>=(C + D) + +From 3.14.9 onwards, this expresion must be written as +C<(A + B) < (C + D) ? E : VAL> + +=back + +=head3 Logical + +C + +=over 1 + +=item * +Causes the following to occur: + +=over 1 + +=item * +Convert A to integer + +=item * +Convert B to integer + +=item * +Bitwise And A and B + +=item * +Convert result to floating point + +=back + +=back + +=head3 Assignment + +C + +=over 1 + +=item * +Causes the Calc record to output the successive values of a sine curve in +1 degree intervals. + +=back + +=head3 Operator Display Parameters + +These parameters are used to present meaningful data to the operator. These +fields are used to display VAL and other parameters of the calculation +record either textually or graphically. + +The EGU field contains a string of up to 16 characters which is supplied by +the user and which describes the values being operated upon. The string is +retrieved whenever the routine C is called. The EGU string is +solely for an operator's sake and does not have to be used. + +The HOPR and LOPR fields only refer to the limits of the VAL, HIHI, HIGH, +LOW and LOLO fields. PREC controls the precision of the VAL field. + +See L for more on the record name (NAME) +and description (DESC) fields. + +=fields EGU, PREC, HOPR, LOPR, NAME, DESC + +=head3 Alarm Parameters + +The possible alarm conditions for the Calc record are the SCAN, READ, +Calculation, and limit alarms. The SCAN and READ alarms are called by the +record support routines. The Calculation alarm is called by the record +processing routine when the CALC expression is an invalid one, upon which +an error message is generated. + +The following alarm parameters which are configured by the user, define the +limit alarms for the VAL field and the severity corresponding to those +conditions. + +The HYST field defines an alarm deadband for each limit. See L +for a complete explanation of alarms of these fields. L +lists other fields related to alarms that are common to all record types. + +=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST + +=head3 Monitor Parameters + +These paramaeters are used to determine when to send monitors for the value +fields. These monitors are sent when the value field exceeds the last +monitored field by the appropriate deadband, the ADEL for archiver monitors +and the MDEL field for all other types of monitors. If these fields have a +value of zero, everytime the value changes, monitors are triggered; if they have a +value of -1, everytime the record is scanned, monitors are triggered. See +L for a complete explanation of monitors. + +=fields ADEL, MDEL + +=head3 Run-time Parameters + +These fields are not configurable using a configuration tool and none are +modifiable at run-time. They are used to process the record. + +The LALM field is used to implement the hysteresis factor for the alarm +limits. + +The LA-LL fields are used to decide when to trigger monitors for the +corresponding fields. For instance, if LA does not equal the value A, +monitors for A are triggered. The MLST and ALST fields are used in the same +manner for the VAL field. + +=fields LALM, ALST, MLST, LA, LB, LC, LD, LE, LF, LG, LH, LI, LJ, LK, LL + +=cut + include "dbCommon.dbd" field(VAL,DBF_DOUBLE) { prompt("Result") @@ -321,4 +829,120 @@ recordtype(calc) { interest(4) extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") } + +=head2 Record Support + +=head3 Record Support Routines + +=head2 C + +For each constant input link, the corresponding value field is initialized +with the constant value if the input link is CONSTANT or a channel access +link is created if the input link is a PV_LINK. + +A routine postfix is called to convert the infix expression in CALC to +Reverse Polish Notation. The result is stored in RPCL. + +=head2 C + +See next section. + +=head2 C + +This is called if CALC is changed. C calls postfix. + +=head2 C + +Fills in the values of struct valueDes so that the refer to VAL. + +=head2 C + +Retrieves EGU. + +=head2 C + +Retrieves PREC. + +=head2 C + +Sets the upper display and lower display limits for a field. If the field +is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else +if the field has upper and lower limits defined they will be used, else the +upper and lower maximum values for the field will be used. + +=head2 C + +Sets the upper control and the lower control limits for a field. If the +field is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and +LOPR, else if the field has upper and lower limits defined they will be +used, else the upper and lower maximum values for the field type will be +used. + +=head2 C + +Sets the following values: + +=over 1 + +upper_alarm_limit = HIHI + +upper_warning_limit = HIGH + +lower_warning_limit = LOW + +lower_alarm_limit = LOLO + +=back + +=head3 Record Processing + +Routine process implements the following algorithm: + +=over 1 + +=item 1. +Fetch all arguments. + +=item 2. +Call routine C, which calculates VAL from the postfix version of +the expression given in CALC. If C returns success UDF is set to +FALSE. + +=item 3. +Check alarms. This routine checks to see if the new VAL causes the alarm +status and severity to change. If so, NSEV, NSTA, and LALM are set. It also +honors the alarm hysteresis factor (HYST). Thus the value must change by +at least HYST before the alarm status and severity changes. + +=item 4. +Check to see if monitors should be invoked. + +=back + +=over 1 + +=item * +Alarm monitors are invoked if the alarm status or severity has changed. + +=item * +Archive and values change monitors are invoked if ADEL and MDEL conditions +are met. + +=item * +Monitors for A-L are checked whenever other monitors are invoked. + +=item * +NSEV and NSTA are reset to 0. + +=back + +=over + +=item 5. +Scan forward link if necessary, set PACT FALSE, and return. + +=back + +=cut + } diff --git a/src/std/rec/calcoutRecord.dbd.pod b/src/std/rec/calcoutRecord.dbd.pod index 5bf2e54de..6289f9043 100644 --- a/src/std/rec/calcoutRecord.dbd.pod +++ b/src/std/rec/calcoutRecord.dbd.pod @@ -4,8 +4,58 @@ # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. +# in file LICENSE that is included with this distribution. #************************************************************************* + +=title Calculation Output Record (calcout) + +The Calculation Output or "Calcout" record is similar to the Calc record +with the added feature of having outputs (an "output link" and an "output +event") which are conditionally executed based on the result of the +calculation. This feature allows conditional branching to be implemented +within an EPICS database (e.g. process Record_A only if Record_B has a +value of 0). The Calcout record is also similar to the Wait record (with +additional features) but uses EPICS standard INLINK and OUTLINK fields +rather than the DBF_STRING fields used in the Wait record. For new +databases, it is recommended that the Calcout record be used instead of the +Wait record. + +=head2 Parameter Fields + +The fields in this record fall into these categories: + +=over 1 + +=item * +scan parameters + +=item * +read parameters + +=item * +expression parameters + +=item * +output parameters + +=item * +operator display parameters + +=item * +alarm parameters + +=item * +monitor parameters + +=item * +run-time parameters + +=back + +=recordtype calcout + +=cut + menu(calcoutOOPT) { choice(calcoutOOPT_Every_Time,"Every Time") choice(calcoutOOPT_On_Change,"On Change") @@ -24,7 +74,556 @@ menu(calcoutINAV) { choice(calcoutINAV_LOC,"Local PV") choice(calcoutINAV_CON,"Constant") } + recordtype(calcout) { + +=head3 Scan Parameters + +The Calcout record has the standard fields for specifying under what +circumstances the record will be processed. The fields are listed in +L. In addition, L explains how these +fields are used. Since the Calcout record supports no direct interfaces to +hardware, it cannot be scanned on I/O interrupt, so its SCAN field cannot +be C. + +=head3 Read Parameters + +The read parameters for the Calcout record consists of 12 input links INPA, +INPB, ... INPL. The fields can be database links, channel access links, or +constants. If they are links, they must specify another record's field. If +they are constants, they will be initialized with the value they are +configured with and can be changed via C. These fields cannot be +hardware addresses. In addition, the Calcout record contains the INAV, +INBV, ... INLV fields which indicate the status of the link fields, for +example, whether or not the specified PV was found and a link to it +established. See L for an explanation of these +fields. + +See L
for information on how to specify database +links. + +=fields INPA, INPB, INPC, INPD, INPE, INPF, INPG, INPH, INPI, INPJ, INPK, INPL + +=head3 Expression + +Like the Calc record, the Calcout record has a CALC field in which the +developer can enter an infix expression which the record routine will +evaluate when it processes the record. The resulting value is placed in the +VAL field. This value can then be used by the OOPT field (see +L) to determine whether or not to write to the output +link or post an output event. It can also be the value that is written to +the output link. The CALC expression is actually converted to opcode and +stored in Reverse Polish Notation in the RPCL field. It is this expression +which is actually used to calculate VAL. The Reverse Polish expression is +evaluated more efficiently during run-time than an infix expression. CALC +can be changes at run-time, and a special record routine will call a +function to convert it to Reverse Polish Notation. + +The infix expressions that can be used are very similar to the C expression +syntax, but with some additions and subtle differences in operator meaning +and precedence. The string may contain a series of expressions separated by +a semi-colon character ';' any one of which may actually provide the +calculation result; however all of the other expressions included must +assign their result to a variable. All alphabetic elements described below +are case independent, so upper and lower case letters may be used and mixed +in the variable and function names as desired. Spaces may be used anywhere +within an expression except between the characters that make up a single +expression element. + +The range of expressions supported by the calculation record are separated into +literals, constants, operands, algebraic operators, trigonometric operators, +relational operators, logical operator, the assignment operator, +parentheses and commas, and the question mark or '?:' operator. + +=fields CALC, VAL, RPCL + +=head3 Literals + +=over 1 + +=item * +Standard double precision floating point numbers + +=item * +Inf: Infinity + +=item * +Nan: Not a Number + +=back + +=head3 Constants + +=over + +=item * +PI: returns the mathematical constant E + +=item * +D2R: evaluates to E/180 which, when used as a multiplier, converts an +angle from degrees to radians + +=item * +R2D: evaluates to 180/E which, when used as a multiplier, converts an +angle from radians to degrees + +=back + +=head3 Operands + +The expression can use the values retrieved from the INPx links as +operands, though constants can be used as operands too. These values +retrieved from the input links are stored in the A-L fields. The values to +be used in the expression are simple references by the field letter. For +instance, the value obtained from the INPA link is stored in field A, and +the values obtained from the INPB link is stored in the field B. The names +can be included in the expression will operate on their respective values, +as in A+B. + +=fields A, B, C, D, E, F, G, H, I, J, K, L + +The keyword VAL returns the current contents of the expression's result +field, i.e. the VAL field for the CALC expression and the OVAL field for +the OCAL expression. (These fields can be written to by CA put, so it might +I be the result from the last time the expression was evaluated). + +=head3 Algebraic Operations + +=over 1 + +=item * +ABS: Absolute value (unary) + +=item * +SQR: Square root (unary) + +=item * +MIN: Minimum (any number of args) + +=item * +MAX: Maximum (any number of args) + +=item * +FINITE: returns non-zero if none of the arguments are NaN or Inf (any +number of args) + +=item * +ISNAN: returns non-zero if any of the arguments is NaN or Inf (any number +of args) + +=item * +CEIL: Ceiling (unary) + +=item * +FLOOR: Floor (unary) + +=item * +LOG: Log base 10 (unary) + +=item * +LOGE: Natural log (unary) + +=item * +LN: Natural log (unary) + +=item * +EXP: Exponential function (unary) + +=item * +^ : Exponential (binary) + +=item * +** : Exponential (binary) + +=item * ++ : Addition (binary) + +=item * +- : Subtraction (binary) + +=item * +* : Multiplication (binary) + +=item * +/ : Division (binary) + +=item * +% : Modulo (binary) + +=item * +NOT: Negate (unary) + +=back + +=head3 Trigonometric Operators + +=over 1 + +=item * +SIN: Sine + +=item * +SINH: Hyperbolic sine + +=item * +ASIN: Arc sine + +=item * +COS: Cosine + +=item * +COSH: Hyperbolic cosine + +=item * +ACOS: Arc cosine + +=item * +TAN: Tangent + +=item * +TANH: Hyperbolic tangent + +=item * +ATAN: Arc tangent + +=back + +=head3 Relational Operators + +=over 1 + +=item * +>= : Greater than or equal to + +=item * +> : Greater than + +=item * +<= : Less than or equal to + +=item * +< : Less than + +=item * +# : Not equal to + +=item * += : Equal to + +=back + +=head3 Logical Operators + +=over 1 + +=item * +&& : And + +=item * +|| : Or + +=item * +! : Not + +=back + +=head3 Bitwise Operators + +=over 1 + +=item * +| : Bitwise Or + +=item * +& : Bitwise And + +=item * +OR : Bitwise Or + +=item * +AND : Bitwise And + +=item * +XOR : Bitwise Exclusive Or + +=item * +~ : One's Complement + +=item * +<< : Left shift + +=item * +>> : Right shift + +=back + +=head3 Assignment Operator + +=over 1 + +=item * +:= : assigns a value (right hand side) to a variable (i.e. field) + +=back + +=head3 Parentheses and Comma + +The open and close parentheses are supported. Nested parentheses are +supported. + +The comma is supported when used to separate the arguments of a binary +function. + +=head3 Conditional Expression + +The C language's question mark operator is supported. The format is: +C + +=head3 Expression Examples + +=head3 Algebraic + +C + +=over 1 + +=item * +Result is A + B + 10 + +=back + +=head3 Relational + +C<(A + B) < (C + D)> + +=over 1 + +=item * +Result is 1 if (A + B) < (C + D) + +=item * +Result is 0 if (A + B) >= (C + D) + +=back + +=head3 Question Mark + +C<(A + B) < (C + D) ? E : F + L + 10> + +=over 1 + +=item * +Result is E if (A + B) < (C + D) + +=item * +Result is F + L + 10 if (A + B) >= (C + D) + +=back + +=head3 Logical + +C + +=over 1 + +=item * +Causes the following to occur: + +=over 1 + +=item * +Convert A to integer + +=item * +Convert B to integer + +=item * +Bitwise And A and B + +=item * +Convert result to floating point + +=back + +=back + +=head3 Output Parameters + +These parameters specify and control the output capabilities of the Calcout +record. They determine when to write the output, where to write it, and what +the output will be. The OUT link specifies the Process Variable to which +the result will be written. + +=head4 Menu calcoutOOPT + +The OOPT field determines the condition that causes the output link to be +written to. It's a menu field that has six choices: + +=menu calcoutOOPT + +=over + +=item * +C -- write output every time record is processed. + +=item * +C -- write output every time VAL changes, i.e., every time the +result of the expression changes. + +=item * +C -- when record is preocessed, write output if VAL is zero. + +=item * +C -- when record is processed, write output if VAL is +non-zero. + +=item * +C -- when record is processed, write output only if VAL +is zero and the last value was non-zero. + +=item * +C -- when record is processed, write output only if +VAL is non-zero and last value was zero. + +=back + +=head4 Menu calcoutDOPT + +The DOPT field determines what data is written to the output link when the +output is executed. The field is a menu field with two options: + +=menu calcoutDOPT + +If C is specified, when the record writes its +output it will write the result of the expression in the CALC field, that +is, it will write the value of the VAL field. If C is specified, +the record will instead write the result of the expresion in the OCAL +field, which is contained in the OVAL field. The OCAL field is exactly like +the CALC field and has the same fuctionality it can contain the string +representation of an expression which is evaluated at run-time. Thus, if +necessary, the record can use the result of the CALC expression to +determine if data should be written and can use the result of the OCAL +expression as the data to write. + +If the OEVT field specifies a non-zero integer and the condition in the +OOPT field is met, the record will post a corresponding event. If the ODLY +field is non-zero, the record pauses for the specified number of seconds +before executing the OUT link or posting the output event. During this +waiting period the record is "active" and will not be processed again until +the wait is over. The field DLYA is equal to 1 during the delay period. The +resolution of the delay entry system dependent. + +The IVOA field specifies what action to take with the OUT link if the +Calcout record eneters an INVALID alarm status. The options are +C, C, and C. +If the IVOA field is C, the data entered into the +IVOV field is written to the OUT link if the record alarm severity is +INVALID. + +=fields OUT, OOPT, DOPT, OCAL, OVAL, OEVT, ODLY, IVOA, IVOV + +=head3 Operator Display Parameter + +These parameters are used to present meaningful data to the operator. Some +are also meant to represent the status of the record at run-time. + +The EGU field contains a string of up to 16 characters which is supplied by +the user and which describes the values being operated upon. The string is +retrieved whenever the routine C is called. The EGU string is +solely for an operator's sake and does not have to be used. + +The HOPR and LOPR fields on;y refer to the limits if the VAL, HIHI, HIGH, +LOW, and LOLO fields. PREC controls the precision of the VAL field. + +=head4 Menu calcoutINAV + +The INAV-INLV fields indicate the status of the link to the PVs specified +in the INPA-INPL fields, respectfully. These field can have four possible +values: + +=menu calcoutINAV + +=over 1 + +=item * +C -- the PV wasn't found on this IOC and a Channel Access link +hasn't been established. + +=item * +C -- the PV wasn't found on this IOC and a Channel Access link +has been established. + +=item * +C -- the PV was found on this IOC. + +=item * +C -- the corresponding link field is a constant. + +=back + +The OUTV field indicates the status of the OUT link. If has the same +possible values as the INAV-INLV fields. + +The CLCV and OLCV fields indicate the validity of the expression in the +CALC and OCAL fields respectfully. If the expression in invalid, the field +is set to one. + +The DYLA field is set to one during the delay specified in ODLY. + +See L for more information on the record +name (NAME) and description (DESC) fields. + +=fields EGU, PREC, HOPR, LOPR, INAV, INBV, INCV, INDV, INEV, INFV, INGV, INHV, INIV, INJV, INKV, INLV, OUTV, CLCV, OCLV, DLYA, NAME, DESC + +=head3 Alarm Parameters + +The possible alarm conditions for the Calcout record are the SCAN, READ, +Calculation, and limit alarms. The SCAN and READ alarms are called by the +record support routines. The Calculation alarm is called by the record +processing routine when the CALC expression is an invalid one, upon which +an error message is generated. + +The following alarm parametersi, which are configured by the user, define the +limit alarms for the VAL field and the severity corresponding to those +conditions. + +The HYST field defines an alarm deadband for each limit. See +L for a complete explanation of alarms and these +fields. C lists other fields related to alarms that are +common to all record types. + +=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST + +=head3 Monitor Parameters + +These parameters are used to determine when to send monitors for the value +fields. These monitors are sent when the value field exceeds the last +monitored field by the appropriate deadband, the ADEL for archiver monitors +and the MDEL field for all aother types of monitors. If these fields have a +value of zero, every time the value changes, monitors are triggered; if +they have a value of -1, every time the record is scanned, monitors are +triggered. See L for a complete explanation of +monitors. + +=fields ADEL, MDEL + +=head3 Run-time Parameters + +These fields are not configurable using a configuration tool and none are +modifiable at run-time. They are used to process the record. + +The LALM field is used to implement the hysteresis factor for the alarm +limits. + +The LA-LL fields are used to decide when to trigger monitors for the +corresponding fields. For instance, if LA does not equal the value for A, +monitors for A are triggered. The MLST and ALST fields are used in the same +manner for the VAL field. + +=fields LALM, ALST, MLST, LA, LB, LC, LD, LE, LF, LG, LH, LI, LJ, LK, LL + +=cut + include "dbCommon.dbd" field(RPVT,DBF_NOACCESS) { prompt("Record Private") @@ -524,6 +1123,148 @@ recordtype(calcout) { interest(4) extra("char orpc[INFIX_TO_POSTFIX_SIZE(80)]") } + +=head2 Record Support + +=head3 Record Support Routines + +=head2 C + +For each constant input link, the corresponding value field is initialized +with the constant value if the input link is CONSTANT or a channel access +link is created if the input link is PV_LINK. + +A routine postfix is called to convert the infix expression in CALC and +OCAL to Reverse Polish Notation. The result is stored in RPCL and ORPC, +respectively. + +=head2 C + +See next section. + +=head2 C + +This is called id CALC or OCAL is changed. C calls postfix. + +=head2 C + +Fills in the values of struct valueDes so that they refer to VAL. + +=head2 C + +Retrieves EGU. + +=head2 C + +Retrieves PREC. + +=head2 C + +Sets the upper display and lower display limits for a field. If the field +is VAL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else +if the field has upper and lower limits defined they will be used, else the +upper and lower macimum values for the field type will be used. + +=head2 C + +Sets the upper control and lower control limits for a field. If the VAL, +HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, else if the +field has upper and lower limits defimed they will be used, else the upper +and lower maximum values for the field will be used. + +=head2 C + +Sets the following values: + +=over + +upper_alarm_limit = HIHI + +upper_warning_limit = HIGH + +lower warning_limit = LOW + +lower_alarm_limit = LOLO + +=back + +=head3 Record Processing + +=head2 C + +The C routine implements the following algorithm: + +=over + +=item 1. +Fetch all arguments. + +=item 2. +Call routine C, which calculates VAL from the prefix version +of the expression given in CALC. If C returns success, UDF +is set to FALSE. + +=item 3. +Check alarms. This routine checks to see if the new VAL causes the alarm +status and severity to change. If so, NSEV, NSTA and LALM are set. If also +honors the alarm hysteresis factor (HYST). Thus the value must change by at +least HYST before the alarm status and severity changes. + +=item 4. +Determin if the Output Execution Option (OOPT) is met. If it met, either +execute the output link (and output event) immediately (if ODLY = 0), or +schedule a callback after the specified interval. See the explanation for +the C routine below. + +=item 5. +Check to see if monitors should be invoked. + +=over + +=item * +Alarm monitors are invoked if the alarm status or severity has changed. + +=item * +Archive and value change monitors are invoked if ADEL and MDEL conditions +are met. + +=item * +Monitors for A-L are checked whenever other monitors are invoked. + +=item * +NSEV and NSTA are reset to 0 + +=back + +=item 6. +If no output delay was specified, scan forwark link if necessaru, set PACT +FALSE, and return. + +=back + +=head2 C + +=over + +=item 1. +If DOPT field specifies the use of OCAL, call the routine C +for the postfix version of the expression in OCAL. Otherwise, use VAL. + +=item 2. +If the Alarm Severity is INVALID, follow the option as designated by the +field IVOA. + +=item 3. +The Alarm Severity is not INVALID or IVOA specifies "Continue Normally", +put the value of OVAL to the OUT link and post the event in OEVT (if +non-zero). + +=item 4. +If an output delay was implemented, process the forwark link. + +=back + +=cut } variable(calcoutODLYprecision, int) diff --git a/src/std/rec/dfanoutRecord.dbd.pod b/src/std/rec/dfanoutRecord.dbd.pod index c2eb42a75..0d0487666 100644 --- a/src/std/rec/dfanoutRecord.dbd.pod +++ b/src/std/rec/dfanoutRecord.dbd.pod @@ -6,12 +6,175 @@ # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* + +=title Data Fanout Record (dfanout) + +The Data Fanout or "dfanout" record is used to forward data to up to +eight other records. It's similar to the fanout record except that the +capability to forward data has been added to it. If has no associated +device support. + +=head2 Parameter Fields + +The fields in this record can be classified into the following categories: + +=over + +=item * +scan parameters + +=item * +desired output parameters + +=item * +write parameters + +=item * +operator display parameters + +=item * +alarm parameters + +=item * +monitor parameters + +=item * +run-time and simulation mode parameters + +=back + +=recordtype dfanout + +=cut + menu(dfanoutSELM) { choice(dfanoutSELM_All,"All") choice(dfanoutSELM_Specified,"Specified") choice(dfanoutSELM_Mask,"Mask") } + recordtype(dfanout) { + +=head3 Scan Parameters + +The data fanout record has the standard fields for specifying under what +circumstances it will be processed. These fields are listed in +L. In addition, L explains how these +fields are used. Since the data fanout record supports no direct interfaces +to hardware, it cannot be scanned on I/O interrupt, so its SCAN field +cannot be C. + +=head3 Desired Output Parameters + +The data fanout record must specify where the desired output value +originates, i.e., the data which is to be fowarded to the records in its +output links. The output mode select (OMSL) field determines whether the +output originates from another record or from run-time database access. +When set to C, the desired output is retrieved from the link +specified in the desired output (DOL) field, which can specify either a +database or a channel access link, and placed into the VAL field. When set +to C, the desired output can be written to the VAL field via +dbPuts at run-time. + +The DOL field can also be a constant in which case the VAL field is +initialized to the constant value. + +Note that there are no conversion parameters, so the desired output value +undergoes no conversions before it is sent out to the output links. + +=fields DOL, OMSL, VAL + +=head3 Write Parameters + +The OUTA-OUTH fields specify where VAL is to be sent. Each field that is to +forward data must specify an address to another record. See +L
for information on specifying links. + +The SELL, SELM, and SELN fields specify which output links are to be +used. + +=head4 Menu dfanoutSELM + +SELM is a menu, with three choices: + +=menu dfanoutSELM + +If SELM=="All", then all output links are used, and the values of +SELL and SELN are ignored. + +If SELM=="Specified", then the value of SELN is used to specify a single +link which will be used. If SELN==0, then no link will be used; if SELN==1, +then OUTA will be used, and so on. + +SELN can either have its value set directly, or have its values retrieved +from another EPICS PV. If SELL is a valid PV link, then SELN will be set to +the values of the linked PV. + +If SELM=="Mask", then SELN will be treated as a bit mask. If bit one of +SELN is set, then OUTA will be used, if bit two is set, OUTB will be used. +Thus if SELN==5, OUTC and OUTA will be used. + +=fields OUTA, OUTB, OUTC, OUTD, OUTE, OUTF, OUTG, OUTH + +=head3 Operator Display Parameters + +These parameters are used to present meaningful data to the operator. They +display the value and other parameters of the data fanout record either +textually or graphically. + +The EGU field can contain a string of up to 16 characters describing the +value on the VAL field. + +The HOPR and LOPR fields determine the upper and lower display limits for +graphic displays and the upper and lower control limits for control +displays. They apply to the VAL, HIHI, HIGH, LOW, and LOLO fields. The +record support routines C or C +retrieve HOPR and LOPR. + +See L for more on the record name (NAME) +and description (DESC) fields. + +=fields EGU, HOPR, LOPR, NAME, DESC + +=head3 Alarm Parameters + +The possible alarm conditions for data fanouts are the SCAN, READ, INVALID, +and limit alarms. The SCAN and READ alarms are called by the record +routines. The limit alarms are configured by the user in the HIHI, LOLO, +HIGH, and LOW fields using floating point values. The limit alarms apply +only to the VAL field. The severity for each of these limits is specified +in the corresponding field (HHSV, LLSV, HSV, LSV) and can be either +NO_ALARM, MINOR, or MAJOR. In the hysteresis field (HYST) can be entered a +number which serves as the deadband on the limit alarms. + +See L for a complete explanation of alarms and these +fields. L lists other fields related to alarms that are +common to all record types. + +=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST + +=head3 Monitor Parameters + +These parameters are used to determine when to send monitors placed on the +VAL field. These monitors are sent when the value field exceeds the last +monitored fields by the specified deadband, ADEL for archivers monitors and +MDEL for all other types of monitors. If these fields have a value of zero, +everytime the value changes, a monitor will be triggered; if they have a +value of -1, everytime the record is scanned, monitors are triggered. See +L for a complete explanation of monitors. + +=fields ADEL, MDEL + +=head3 Run-Time Parameters and Simulation Mode Parameters + +These parameters are used by the run-time code for processing the data +fanout record. Ther are not configurable. They are used to implement the +hysteresis factors for monitor callbacks. + +=fields LALM, ALST, MLST + +=cut + include "dbCommon.dbd" field(VAL,DBF_DOUBLE) { prompt("Desired Output") @@ -201,4 +364,91 @@ recordtype(dfanout) { special(SPC_NOMOD) interest(3) } + +=head2 Record Support + +=head3 Record Support Routines + +=head2 C + +This routine initializes all output links that are defined. Then it initializes +DOL if DOL is a constant or a PV_LINK. When initializing the output links +and the DOL link, a non-zero value is returned if an error occurs. + +=head2 C + +See next section. + +=head2 C + +This routine fills in the members of C with the VAL fields +value and characteristics. + +=head2 C + +The routine copies the string specified in the EGU field to the location +specified by a pointer which is passed to the routine. + +=head2 C + +If the referenced field is VAL, HIHI, HIGH, LOW, or LOLO, this routine sets +the C member of the C structure to the +HOPR and the C member to the LOPR. If the referenced +field is not one of the above fields, then C +routine is called. + +=head2 C + +Same as the C routine except that it uses the +C structure. + +=head2 C + +This sets the members of the C structure to the specified +alarm limits if the referenced field is VAL: + +=over + +upper_alarm_limit = HIHI + +upper_warning_limit = HIGH + +lower_warning_limit = LOW + +lower_alarm_limit = LOLO + +=back + +If the referenced field is not VAL, the C routine +is called. + +=head3 Record Processing + +=over + +=item 1. +The C routine first retrieves a value for DOL and places it in +VAL if OMSL is set to colsed loop mode. If an error occurs, then UDF is set +to FALSE. + +=item 2. +PACT is set TRUE + +=item 3. +VAL is then sent to all the records specified in the OUTA-OUTH fields by +calling C for each link. + +=item 4. +Alarms are checked and monitors are called if conditions apply. + +=item 5. +The data fanout's own forward link is then processed. + +=item 6. +PACT is set FALSE, and the C routine returns. A -1 is returned +if there was an error writing values to one of the output links. + +=back + +=cut } From ee90dffd40afe5ff1b8b83cba172603d9f5b99a5 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 6 Dec 2018 16:33:20 -0600 Subject: [PATCH 28/34] Add flush to the iocsh errlog command --- src/libCom/iocsh/libComRegister.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libCom/iocsh/libComRegister.c b/src/libCom/iocsh/libComRegister.c index d8429fa9f..bf644624e 100644 --- a/src/libCom/iocsh/libComRegister.c +++ b/src/libCom/iocsh/libComRegister.c @@ -218,6 +218,7 @@ static const iocshFuncDef errlogFuncDef = {"errlog",1,errlogArgs}; static void errlogCallFunc(const iocshArgBuf *args) { errlogPrintfNoConsole("%s\n", args[0].sval); + errlogFlush(); } /* iocLogPrefix */ From 49f5527cd7bc74949910f8955f855bfe1b45788b Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 6 Dec 2018 16:35:30 -0600 Subject: [PATCH 29/34] iocsh: Add protection if realloc() fails --- src/libCom/iocsh/iocsh.cpp | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/libCom/iocsh/iocsh.cpp b/src/libCom/iocsh/iocsh.cpp index ef646158e..03d4c28b4 100644 --- a/src/libCom/iocsh/iocsh.cpp +++ b/src/libCom/iocsh/iocsh.cpp @@ -599,12 +599,12 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) macPushScope(handle); macInstallMacros(handle, defines); + wasOkToBlock = epicsThreadIsOkToBlock(); + epicsThreadSetOkToBlock(1); + /* * Read commands till EOF or exit */ - argc = 0; - wasOkToBlock = epicsThreadIsOkToBlock(); - epicsThreadSetOkToBlock(1); for (;;) { /* * Read a line @@ -676,15 +676,15 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) redirect = NULL; for (;;) { if (argc >= argvCapacity) { - char **av; - argvCapacity += 50; - av = (char **)realloc (argv, argvCapacity * sizeof *argv); - if (av == NULL) { + int newCapacity = argvCapacity + 20; + char **newv = (char **)realloc (argv, newCapacity * sizeof *argv); + if (newv == NULL) { fprintf (epicsGetStderr(), "Out of memory!\n"); argc = -1; break; } - argv = av; + argv = newv; + argvCapacity = newCapacity; } c = line[icin++]; if (c == '\0') @@ -833,16 +833,14 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) break; } if (iarg >= argBufCapacity) { - void *np; - - argBufCapacity += 20; - np = realloc (argBuf, argBufCapacity * sizeof *argBuf); - if (np == NULL) { + int newCapacity = argBufCapacity + 20; + void *newBuf = realloc(argBuf, newCapacity * sizeof *argBuf); + if (newBuf == NULL) { fprintf (epicsGetStderr(), "Out of memory!\n"); - argBufCapacity -= 20; break; } - argBuf = (iocshArgBuf *)np; + argBuf = (iocshArgBuf *) newBuf; + argBufCapacity = newCapacity; } if (piocshFuncDef->arg[iarg]->type == iocshArgArgv) { argBuf[iarg].aval.ac = argc-iarg; @@ -887,7 +885,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) errlogFlush(); if (readlineContext) epicsReadlineEnd(readlineContext); - epicsThreadSetOkToBlock( wasOkToBlock); + epicsThreadSetOkToBlock(wasOkToBlock); return 0; } From e53244df1f9b8d7838de7f612788ad0a6961a070 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 7 Dec 2018 11:45:12 -0600 Subject: [PATCH 30/34] Cherry-pick Dirk's dbState NULL checks from the 7.0 branch Prevent segfaults in iocsh --- src/ioc/db/dbState.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ioc/db/dbState.c b/src/ioc/db/dbState.c index 6de7735cd..60819a023 100644 --- a/src/ioc/db/dbState.c +++ b/src/ioc/db/dbState.c @@ -37,6 +37,9 @@ dbStateId dbStateFind(const char *name) ELLNODE *node; dbStateId id; + if (!name) + return NULL; + for (node = ellFirst(&states); node; node = ellNext(node)) { id = CONTAINER(node, dbState, node); if (strcmp(id->name, name) == 0) @@ -49,6 +52,9 @@ dbStateId dbStateCreate(const char *name) { dbStateId id; + if (!name) + return NULL; + if ((id = dbStateFind(name))) return id; From 64d9d1a4c9885847981669032701b3724635ccad Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 7 Dec 2018 13:16:06 -0600 Subject: [PATCH 31/34] Fix field links to local menu anchors Anchor IDs are different for XHTML vs HTML generation. --- src/tools/dbdToHtml.pl | 49 ++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/tools/dbdToHtml.pl b/src/tools/dbdToHtml.pl index d30779dcf..636292a52 100644 --- a/src/tools/dbdToHtml.pl +++ b/src/tools/dbdToHtml.pl @@ -124,6 +124,33 @@ if ($opt_D) { # Output dependencies only open my $out, '>', $opt_o or die "Can't create $opt_o: $!\n"; +my $podHtml; +my $idify; + +if ($::XHTML) { + $podHtml = Pod::Simple::XHTML->new(); + $podHtml->html_doctype(<< '__END_DOCTYPE'); + + +__END_DOCTYPE + $podHtml->html_header_tags($podHtml->html_header_tags . + "\n"); + + $idify = sub { + my $title = shift; + return $podHtml->idify($title, 1); + } +} else { # Fall back to HTML + $podHtml = Pod::Simple::HTML->new(); + $podHtml->html_css('style.css'); + + $idify = sub { + my $title = shift; + return Pod::Simple::HTML::esc($podHtml->section_escape($title)); + } +} + # Parse the Pod text from the root DBD object my $pod = join "\n", '=for html
', '', map { @@ -153,22 +180,6 @@ my $pod = join "\n", '=for html
', '', } $dbd->pod, '=for html
', ''; -my $podHtml; - -if ($::XHTML) { - $podHtml = Pod::Simple::XHTML->new(); - $podHtml->html_doctype(<< '__END_DOCTYPE'); - - -__END_DOCTYPE - $podHtml->html_header_tags($podHtml->html_header_tags . - "\n"); -} else { # Fall back to HTML - $podHtml = Pod::Simple::HTML->new(); - $podHtml->html_css('style.css'); -} - $podHtml->force_title(encode_entities($title)); $podHtml->perldoc_url_prefix(''); $podHtml->perldoc_url_postfix('.html'); @@ -246,7 +257,7 @@ sub fieldTableRow { if ($type eq 'MENU') { my $mn = $fld->attribute('menu'); my $menu = $dbd->menu($mn); - my $url = $menu ? "#Menu_$mn" : "${mn}.html"; + my $url = $menu ? '#' . &$idify("Menu $mn") : "${mn}.html"; $html .= " (
$mn)"; } $html .= ''; @@ -374,6 +385,8 @@ can be found in the aai and aSub record types. If you look at the L file you'll see that the POD there starts by documenting a record-specific menu definition. The "menu" keyword generates a -table that lists all the choices found in the named menu. +table that lists all the choices found in the named menu. Any MENU fields in the +field tables that refer to a locally-defined menu will generate a link to a +document section which must be titled "Menu [menuName]". =cut From 9b385480d035443944dfb880098166b84e2dd973 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 7 Dec 2018 13:51:35 -0600 Subject: [PATCH 32/34] Rename new *QueuePrintStatus() and *QueueStatus iocsh commands to *Show Also added symbol decorations for Windows builds. --- src/ioc/db/callback.c | 2 +- src/ioc/db/callback.h | 2 +- src/ioc/db/dbIocRegister.c | 36 ++++++++++++++++++------------------ src/ioc/db/dbScan.c | 2 +- src/ioc/db/dbScan.h | 2 +- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/ioc/db/callback.c b/src/ioc/db/callback.c index 1b6c24906..fe6db815b 100644 --- a/src/ioc/db/callback.c +++ b/src/ioc/db/callback.c @@ -130,7 +130,7 @@ int callbackQueueStatus(const int reset, callbackQueueStats *result) return ret; } -void callbackQueuePrintStatus(const int reset) +void callbackQueueShow(const int reset) { callbackQueueStats stats; if (callbackQueueStatus(reset, &stats) == -1) { diff --git a/src/ioc/db/callback.h b/src/ioc/db/callback.h index dd13cdb81..720cf337d 100644 --- a/src/ioc/db/callback.h +++ b/src/ioc/db/callback.h @@ -81,7 +81,7 @@ epicsShareFunc void callbackRequestProcessCallbackDelayed( CALLBACK *pCallback, int Priority, void *pRec, double seconds); epicsShareFunc int callbackSetQueueSize(int size); epicsShareFunc int callbackQueueStatus(const int reset, callbackQueueStats *result); -void callbackQueuePrintStatus(const int reset); +epicsShareFunc void callbackQueueShow(const int reset); epicsShareFunc int callbackParallelThreads(int count, const char *prio); #ifdef __cplusplus diff --git a/src/ioc/db/dbIocRegister.c b/src/ioc/db/dbIocRegister.c index cecf75608..c40af92c1 100644 --- a/src/ioc/db/dbIocRegister.c +++ b/src/ioc/db/dbIocRegister.c @@ -296,15 +296,15 @@ static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args) scanOnceSetQueueSize(args[0].ival); } -/* scanOnceQueueStatus */ -static const iocshArg scanOnceQueueStatusArg0 = { "reset",iocshArgInt}; -static const iocshArg * const scanOnceQueueStatusArgs[1] = - {&scanOnceQueueStatusArg0}; -static const iocshFuncDef scanOnceQueueStatusFuncDef = - {"scanOnceQueueStatus",1,scanOnceQueueStatusArgs}; -static void scanOnceQueueStatusCallFunc(const iocshArgBuf *args) +/* scanOnceQueueShow */ +static const iocshArg scanOnceQueueShowArg0 = { "reset",iocshArgInt}; +static const iocshArg * const scanOnceQueueShowArgs[1] = + {&scanOnceQueueShowArg0}; +static const iocshFuncDef scanOnceQueueShowFuncDef = + {"scanOnceQueueShow",1,scanOnceQueueShowArgs}; +static void scanOnceQueueShowCallFunc(const iocshArgBuf *args) { - scanOnceQueuePrintStatus(args[0].ival); + scanOnceQueueShow(args[0].ival); } /* scanppl */ @@ -346,15 +346,15 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args) callbackSetQueueSize(args[0].ival); } -/* callbackQueueStatus */ -static const iocshArg callbackQueueStatusArg0 = { "reset", iocshArgInt}; -static const iocshArg * const callbackQueueStatusArgs[1] = - {&callbackQueueStatusArg0}; -static const iocshFuncDef callbackQueueStatusFuncDef = - {"callbackQueueStatus",1,callbackQueueStatusArgs}; -static void callbackQueueStatusCallFunc(const iocshArgBuf *args) +/* callbackQueueShow */ +static const iocshArg callbackQueueShowArg0 = { "reset", iocshArgInt}; +static const iocshArg * const callbackQueueShowArgs[1] = + {&callbackQueueShowArg0}; +static const iocshFuncDef callbackQueueShowFuncDef = + {"callbackQueueShow",1,callbackQueueShowArgs}; +static void callbackQueueShowCallFunc(const iocshArgBuf *args) { - callbackQueuePrintStatus(args[0].ival); + callbackQueueShow(args[0].ival); } /* callbackParallelThreads */ @@ -463,14 +463,14 @@ void dbIocRegister(void) iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc); iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc); - iocshRegister(&scanOnceQueueStatusFuncDef,scanOnceQueueStatusCallFunc); + iocshRegister(&scanOnceQueueShowFuncDef,scanOnceQueueShowCallFunc); iocshRegister(&scanpplFuncDef,scanpplCallFunc); iocshRegister(&scanpelFuncDef,scanpelCallFunc); iocshRegister(&postEventFuncDef,postEventCallFunc); iocshRegister(&scanpiolFuncDef,scanpiolCallFunc); iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc); - iocshRegister(&callbackQueueStatusFuncDef,callbackQueueStatusCallFunc); + iocshRegister(&callbackQueueShowFuncDef,callbackQueueShowCallFunc); iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc); /* Needed before callback system is initialized */ diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c index e0e14e0d2..9224ed504 100644 --- a/src/ioc/db/dbScan.c +++ b/src/ioc/db/dbScan.c @@ -744,7 +744,7 @@ int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result) return ret; } -void scanOnceQueuePrintStatus(const int reset) +void scanOnceQueueShow(const int reset) { scanOnceQueueStats stats; if (scanOnceQueueStatus(reset, &stats) == -1) { diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h index 830d3a8dc..622103c3b 100644 --- a/src/ioc/db/dbScan.h +++ b/src/ioc/db/dbScan.h @@ -65,7 +65,7 @@ epicsShareFunc int scanOnce(struct dbCommon *); epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr); epicsShareFunc int scanOnceSetQueueSize(int size); epicsShareFunc int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result); -void scanOnceQueuePrintStatus(const int reset); +epicsShareFunc void scanOnceQueueShow(const int reset); /*print periodic lists*/ epicsShareFunc int scanppl(double rate); From 84b7612036666372ae37b519a47099273f4aa286 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 10 Dec 2018 00:25:27 -0600 Subject: [PATCH 33/34] Fix for lp: #1801145 recordtype defined after use in device Add proper equals() method for DBD::Recfield and DBD::Recordtype. In Parser::parse_recordtype() check for and re-use a declaration (i.e. an empty recordtype object) when parsing a later definition of the same recordtype. --- src/tools/DBD/Parser.pm | 9 +++++++-- src/tools/DBD/Recfield.pm | 13 ++++++++++++- src/tools/DBD/Recordtype.pm | 16 +++++++++++++--- src/tools/test/Recordtype.plt | 19 ++++++++++++++++++- 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/tools/DBD/Parser.pm b/src/tools/DBD/Parser.pm index 113ef42a8..7caed9e4b 100644 --- a/src/tools/DBD/Parser.pm +++ b/src/tools/DBD/Parser.pm @@ -60,7 +60,7 @@ sub ParseDBD { my $rtyp = $dbd->recordtype($1); if (!defined $rtyp) { $rtyp = DBD::Recordtype->new($1); - warn "Device using undefined record type '$1', place-holder created\n"; + warn "Device using unknown record type '$1', declaration created\n"; $dbd->add($rtyp); } $rtyp->add_device(DBD::Device->new($2, $3, $4)); @@ -174,7 +174,12 @@ sub parse_breaktable { sub parse_recordtype { my ($dbd, $name) = @_; pushContext("recordtype($name)"); - my $rtyp = DBD::Recordtype->new($name); + # Re-use a matching declaration record type if one exists + my $rtyp = $dbd->recordtype($name); + if (!defined($rtyp) || $rtyp->fields) { + # Earlier record type is not a declaration, don't re-use it + $rtyp = DBD::Recordtype->new($name); + } while(1) { parseCommon($rtyp); if (m/\G field \s* \( \s* $RXstr \s* , \s* $RXstr \s* \) \s* \{/oxgc) { diff --git a/src/tools/DBD/Recfield.pm b/src/tools/DBD/Recfield.pm index e1d7a7d12..196dedf4b 100644 --- a/src/tools/DBD/Recfield.pm +++ b/src/tools/DBD/Recfield.pm @@ -130,7 +130,18 @@ sub attribute { } sub equals { - dieContext("Record field objects are not comparable"); + my ($l, $r) = @_; + return 1 if $l eq $r; + return 0 if + $l->{NAME} ne $r->{NAME} || + $l->{DBF_TYPE} ne $r->{DBF_TYPE}; + my ($la, $ra) = ($l->{ATTR_INDEX}, $r->{ATTR_INDEX}); + my @keys = sort keys %$la; + return 0 if join(',', @keys) ne join(',', sort keys %$ra); + foreach my $k (@keys) { + return 0 if $la->{$k} ne $ra->{$k}; + } + return 1; } sub check_valid { diff --git a/src/tools/DBD/Recordtype.pm b/src/tools/DBD/Recordtype.pm index e4b0d5481..649c6a21b 100644 --- a/src/tools/DBD/Recordtype.pm +++ b/src/tools/DBD/Recordtype.pm @@ -104,9 +104,19 @@ sub pod { sub equals { my ($new, $known) = @_; - return 0 if ! $known->fields; - return 1 if ! $new->fields; - dieContext("Duplicate definition of record type '$known->{NAME}'"); + return 1 if $new eq $known; + return 0 if $new->{NAME} ne $known->{NAME}; + return 1 if ! $new->fields; # Later declarations always match + # NB: Definition after declaration is handled in parse_recordtype() + my @nf = @{$new->{FIELD_LIST}}; + my @kf = @{$known->{FIELD_LIST}}; + return 0 if scalar @nf != scalar @kf; + while (@nf) { + my $nf = shift @nf; + my $kf = shift @kf; + return 0 if ! $nf->equals($kf); + } + return 1; } sub toDeclaration { diff --git a/src/tools/test/Recordtype.plt b/src/tools/test/Recordtype.plt index c5384b7a9..efe1e70b2 100644 --- a/src/tools/test/Recordtype.plt +++ b/src/tools/test/Recordtype.plt @@ -2,7 +2,7 @@ use lib '../..'; -use Test::More tests => 17; +use Test::More tests => 23; use DBD::Recordtype; use DBD::Recfield; @@ -13,6 +13,11 @@ isa_ok $rtyp, 'DBD::Recordtype'; is $rtyp->name, 'test', 'Record name'; is $rtyp->fields, 0, 'No fields yet'; +is $rtyp->equals($rtyp), 1, 'A declaration == itself'; + +my $rt2 = DBD::Recordtype->new('test'); +is $rtyp->equals($rt2), 1, 'A declaration == a different declaration'; + my $fld1 = DBD::Recfield->new('NAME', 'DBF_STRING'); $fld1->add_attribute("size", "41"); $fld1->check_valid; @@ -26,6 +31,18 @@ is $rtyp->fields, 1, 'First field added'; $rtyp->add_field($fld2); is $rtyp->fields, 2, 'Second field added'; +is $rtyp->equals($rtyp), 1, 'A definition == itself'; +is $rt2->equals($rtyp), 1, 'A declaration == a definition'; + +$rt2->add_field($fld1); +my $fld3 = DBD::Recfield->new('DTYP', 'DBF_DEVICE'); +$fld3->check_valid; +$rt2->add_field($fld3); +is $rt2->equals($rtyp), 1, 'Identical definitions are equal'; + +$fld3->add_attribute("pp", "TRUE"); +is $rt2->equals($rtyp), 0, 'Different definitions are not equal'; + my @fields = $rtyp->fields; is_deeply \@fields, [$fld1, $fld2], 'Field list'; From c59a18600a41efede86d2ada5e492fe5679c8342 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 11 Dec 2018 16:47:25 -0600 Subject: [PATCH 34/34] Add release notes for the QueueShow additions --- documentation/RELEASE_NOTES.html | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index afb7787d3..482e99517 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -19,6 +19,15 @@ --> +

Status reporting for the callback and scanOnce task queues

+ +

Two new iocsh commands and some associated underlying APIs have been added to +show the state of the queues that feed the three callback tasks and the scanOnce +task, including a high-water mark which can optionally be reset. The new iocsh +commands are callbackQueueShow and scanOnceQueueShow; both +take an optional integer argument which must be non-zero to reset the +high-water mark.

+

Support for event codes greater than or equal to NUM_TIME_EVENTS

Event numbers greater than or equal to NUM_TIME_EVENTS are now allowed if