diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index eef4d6745..d3a66968f 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -76,11 +76,14 @@ COMMON_DIR = ../O.Common IOCS_APPL_TOP = $(shell $(FULLPATHNAME) $(INSTALL_LOCATION)) #------------------------------------------------------- -# Make echo output - suppress echoing if make's '-s' flag is set +# Silencing the build - suppress messages during 'make -s' NOP = : -ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo) -QUIET_FLAG := $(if $(findstring s,$(MAKEFLAGS)),-q,) -QUESTION_FLAG := $(if $(findstring q,$(MAKEFLAGS)),-i,) +ECHO = @$(if $(findstring s,$(MFLAGS)),$(NOP),echo) +QUIET_FLAG := $(if $(findstring s,$(MFLAGS)),-q,) + +#------------------------------------------------------- +# Convert 'make -q' flag into '-i' for genVersionHeader.pl +QUESTION_FLAG := $(if $(findstring q,$(MFLAGS)),-i,) #------------------------------------------------------- ifdef T_A @@ -90,7 +93,7 @@ INSTALL_SHRLIB = $(INSTALL_LOCATION_LIB)/$(T_A) INSTALL_TCLLIB = $(INSTALL_LOCATION_LIB)/$(T_A) INSTALL_BIN = $(INSTALL_LOCATION_BIN)/$(T_A) -#Directories for libraries +# Directories for libraries SHRLIB_SEARCH_DIRS = $(INSTALL_LIB) #------------------------------------------------------- diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 21a838790..2dc9dd501 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) @@ -360,14 +362,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) diff --git a/configure/os/CONFIG.Common.RTEMS b/configure/os/CONFIG.Common.RTEMS index 214ba794d..8425e6ef9 100644 --- a/configure/os/CONFIG.Common.RTEMS +++ b/configure/os/CONFIG.Common.RTEMS @@ -76,7 +76,7 @@ CPPFLAGS += $($(BUILD_CLASS)_CPPFLAGS) $(POSIX_CPPFLAGS) $(OPT_CPPFLAGS)\ $(USR_CPPFLAGS) $(CMD_CPPFLAGS) $(ARCH_DEP_CPPFLAGS) $(OP_SYS_CPPFLAGS)\ $(OP_SYS_INCLUDE_CPPFLAGS) $(CODE_CPPFLAGS) -ECHO = @$(if $(findstring s,$(patsubst T_A=%,,$(MAKEFLAGS))),$(NOP),echo) +ECHO = @$(if $(findstring s,$(MFLAGS)),$(NOP),echo) #-------------------------------------------------- # Although RTEMS uses gcc, it wants to use gcc its own way diff --git a/configure/os/CONFIG.Common.vxWorksCommon b/configure/os/CONFIG.Common.vxWorksCommon index 50f626959..f28f0e327 100644 --- a/configure/os/CONFIG.Common.vxWorksCommon +++ b/configure/os/CONFIG.Common.vxWorksCommon @@ -146,8 +146,10 @@ SHRLIB_CFLAGS = SHRLIB_LDFLAGS = #-------------------------------------------------- -# Earlier versions of gcc don't understand -MF -HDEPENDS_COMPFLAGS = -MM > $@ +# Don't use gcc 2.x for dependency generation + +HDEPENDS_METHOD_2 = MKMF +HDEPENDS_METHOD = $(firstword $(HDEPENDS_METHOD_$(VX_GNU_MAJOR_VERSION)) COMP) #-------------------------------------------------- # osithead use default stack, YES or NO override diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 63f8880a7..a61d5218e 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -112,6 +112,25 @@ be happy to try and answer them!

+

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 +supported by the registered event time provider, which must provide its own +advancing timestamp validation for such events.

+ +

Time events numbered 0 through (NUM_TIME_EVENTS-1) are still validated by +code in epicsGeneralTime.c that checks 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 diff --git a/modules/ca/src/tools/caget.c b/modules/ca/src/tools/caget.c index 00056709e..75225d07f 100644 --- a/modules/ca/src/tools/caget.c +++ b/modules/ca/src/tools/caget.c @@ -34,6 +34,7 @@ #include #include #include +#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/modules/ca/src/tools/cainfo.c b/modules/ca/src/tools/cainfo.c index ad580f473..fc18ccd3f 100644 --- a/modules/ca/src/tools/cainfo.c +++ b/modules/ca/src/tools/cainfo.c @@ -23,6 +23,7 @@ #include #include +#include "epicsVersion.h" #include #include @@ -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/modules/ca/src/tools/camonitor.c b/modules/ca/src/tools/camonitor.c index 307dad8d6..a3fdecd55 100644 --- a/modules/ca/src/tools/camonitor.c +++ b/modules/ca/src/tools/camonitor.c @@ -26,6 +26,7 @@ #include #include #include +#include "epicsVersion.h" #include #include @@ -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/modules/ca/src/tools/caput.c b/modules/ca/src/tools/caput.c index 5e4d10e23..79ffef8c3 100644 --- a/modules/ca/src/tools/caput.c +++ b/modules/ca/src/tools/caput.c @@ -37,6 +37,7 @@ #include #include #include +#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" @@ -281,11 +283,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; diff --git a/modules/database/src/ioc/db/Makefile b/modules/database/src/ioc/db/Makefile index c6f33556a..2dc4fec0d 100644 --- a/modules/database/src/ioc/db/Makefile +++ b/modules/database/src/ioc/db/Makefile @@ -61,7 +61,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 diff --git a/modules/database/src/ioc/db/callback.c b/modules/database/src/ioc/db/callback.c index ae074141c..fe6db815b 100644 --- a/modules/database/src/ioc/db/callback.c +++ b/modules/database/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; @@ -103,6 +104,51 @@ int callbackSetQueueSize(int size) return 0; } +int callbackQueueStatus(const int reset, callbackQueueStats *result) +{ + int ret; + if (!callbackIsInit) return -1; + if (result) { + int prio; + result->size = callbackQueueSize; + 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 callbackQueueShow(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"); + } 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]); + } + } +} + int callbackParallelThreads(int count, const char *prio) { if (callbackIsInit) { @@ -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/modules/database/src/ioc/db/callback.h b/modules/database/src/ioc/db/callback.h index fa626d1d0..720cf337d 100644 --- a/modules/database/src/ioc/db/callback.h +++ b/modules/database/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); +epicsShareFunc void callbackQueueShow(const int reset); epicsShareFunc int callbackParallelThreads(int count, const char *prio); #ifdef __cplusplus diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c index 65a8327cf..843fbfc0c 100644 --- a/modules/database/src/ioc/db/dbCa.c +++ b/modules/database/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/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c index 92be08c77..9792708cb 100644 --- a/modules/database/src/ioc/db/dbChannel.c +++ b/modules/database/src/ioc/db/dbChannel.c @@ -20,6 +20,7 @@ #include "cantProceed.h" #include "epicsAssert.h" #include "epicsString.h" +#include "epicsStdio.h" #include "errlog.h" #include "freeList.h" #include "gpHash.h" diff --git a/modules/database/src/ioc/db/dbIocRegister.c b/modules/database/src/ioc/db/dbIocRegister.c index 4d0b88cd9..c40af92c1 100644 --- a/modules/database/src/ioc/db/dbIocRegister.c +++ b/modules/database/src/ioc/db/dbIocRegister.c @@ -296,6 +296,17 @@ static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args) scanOnceSetQueueSize(args[0].ival); } +/* 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) +{ + scanOnceQueueShow(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); } +/* 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) +{ + callbackQueueShow(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(&scanOnceQueueShowFuncDef,scanOnceQueueShowCallFunc); iocshRegister(&scanpplFuncDef,scanpplCallFunc); iocshRegister(&scanpelFuncDef,scanpelCallFunc); iocshRegister(&postEventFuncDef,postEventCallFunc); iocshRegister(&scanpiolFuncDef,scanpiolCallFunc); iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc); + iocshRegister(&callbackQueueShowFuncDef,callbackQueueShowCallFunc); iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc); /* Needed before callback system is initialized */ diff --git a/modules/database/src/ioc/db/dbScan.c b/modules/database/src/ioc/db/dbScan.c index e5c78fea1..9224ed504 100644 --- a/modules/database/src/ioc/db/dbScan.c +++ b/modules/database/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) +{ + int ret; + if (!onceQ) return -1; + 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 scanOnceQueueShow(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"); + } 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)); + } +} + static void initOnce(void) { if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) { diff --git a/modules/database/src/ioc/db/dbScan.h b/modules/database/src/ioc/db/dbScan.h index d483a0c30..622103c3b 100644 --- a/modules/database/src/ioc/db/dbScan.h +++ b/modules/database/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); +epicsShareFunc void scanOnceQueueShow(const int reset); /*print periodic lists*/ epicsShareFunc int scanppl(double rate); diff --git a/modules/database/src/ioc/db/menuScan.dbd b/modules/database/src/ioc/db/menuScan.dbd.pod similarity index 50% rename from modules/database/src/ioc/db/menuScan.dbd rename to modules/database/src/ioc/db/menuScan.dbd.pod index 17fedd59b..e716e79da 100644 --- a/modules/database/src/ioc/db/menuScan.dbd +++ b/modules/database/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") diff --git a/modules/database/src/std/rec/biRecord.dbd b/modules/database/src/std/rec/biRecord.dbd deleted file mode 100644 index 1646d5df1..000000000 --- a/modules/database/src/std/rec/biRecord.dbd +++ /dev/null @@ -1,134 +0,0 @@ -#************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne -# National Laboratory. -# Copyright (c) 2002 The Regents of the University of California, as -# Operator of Los Alamos National Laboratory. -# EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. -#************************************************************************* -recordtype(bi) { - include "dbCommon.dbd" - field(INP,DBF_INLINK) { - prompt("Input Specification") - promptgroup("40 - Input") - interest(1) - } - field(VAL,DBF_ENUM) { - prompt("Current Value") - promptgroup("40 - Input") - asl(ASL0) - pp(TRUE) - } - field(ZSV,DBF_MENU) { - prompt("Zero Error Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(OSV,DBF_MENU) { - prompt("One Error Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(COSV,DBF_MENU) { - prompt("Change of State Svr") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(ZNAM,DBF_STRING) { - prompt("Zero Name") - promptgroup("80 - Display") - pp(TRUE) - interest(1) - size(26) - prop(YES) - } - field(ONAM,DBF_STRING) { - prompt("One Name") - promptgroup("80 - Display") - pp(TRUE) - interest(1) - size(26) - prop(YES) - } - field(RVAL,DBF_ULONG) { - prompt("Raw Value") - pp(TRUE) - } - field(ORAW,DBF_ULONG) { - prompt("prev Raw Value") - special(SPC_NOMOD) - interest(3) - } - field(MASK,DBF_ULONG) { - prompt("Hardware Mask") - special(SPC_NOMOD) - interest(1) - } - field(LALM,DBF_USHORT) { - prompt("Last Value Alarmed") - special(SPC_NOMOD) - interest(3) - } - field(MLST,DBF_USHORT) { - prompt("Last Value Monitored") - special(SPC_NOMOD) - interest(3) - } - field(SIOL,DBF_INLINK) { - prompt("Simulation Input Link") - promptgroup("90 - Simulate") - interest(1) - } - field(SVAL,DBF_ULONG) { - prompt("Simulation Value") - } - field(SIML,DBF_INLINK) { - prompt("Simulation Mode Link") - promptgroup("90 - Simulate") - interest(1) - } - field(SIMM,DBF_MENU) { - prompt("Simulation Mode") - special(SPC_MOD) - interest(1) - menu(menuSimm) - } - field(SIMS,DBF_MENU) { - prompt("Simulation Mode Severity") - promptgroup("90 - Simulate") - interest(2) - menu(menuAlarmSevr) - } - field(OLDSIMM,DBF_MENU) { - prompt("Prev. Simulation Mode") - special(SPC_NOMOD) - interest(4) - menu(menuSimm) - } - field(SSCN,DBF_MENU) { - prompt("Sim. Mode Scan") - promptgroup("90 - Simulate") - interest(1) - menu(menuScan) - initial("65535") - } - field(SDLY,DBF_DOUBLE) { - prompt("Sim. Mode Async Delay") - promptgroup("90 - Simulate") - interest(2) - initial("-1.0") - } - %#include "callback.h" - field(SIMPVT,DBF_NOACCESS) { - prompt("Sim. Mode Private") - special(SPC_NOMOD) - interest(4) - extra("CALLBACK *simpvt") - } -} diff --git a/modules/database/src/std/rec/biRecord.dbd.pod b/modules/database/src/std/rec/biRecord.dbd.pod new file mode 100644 index 000000000..fbf343ad4 --- /dev/null +++ b/modules/database/src/std/rec/biRecord.dbd.pod @@ -0,0 +1,491 @@ +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE 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") + promptgroup("40 - Input") + interest(1) + } + field(VAL,DBF_ENUM) { + prompt("Current Value") + promptgroup("40 - Input") + asl(ASL0) + pp(TRUE) + } + field(ZSV,DBF_MENU) { + prompt("Zero Error Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(OSV,DBF_MENU) { + prompt("One Error Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(COSV,DBF_MENU) { + prompt("Change of State Svr") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(ZNAM,DBF_STRING) { + prompt("Zero Name") + promptgroup("80 - Display") + pp(TRUE) + interest(1) + size(26) + prop(YES) + } + field(ONAM,DBF_STRING) { + prompt("One Name") + promptgroup("80 - Display") + pp(TRUE) + interest(1) + size(26) + prop(YES) + } + field(RVAL,DBF_ULONG) { + prompt("Raw Value") + pp(TRUE) + } + field(ORAW,DBF_ULONG) { + prompt("prev Raw Value") + special(SPC_NOMOD) + interest(3) + } + field(MASK,DBF_ULONG) { + prompt("Hardware Mask") + special(SPC_NOMOD) + interest(1) + } + field(LALM,DBF_USHORT) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_USHORT) { + prompt("Last Value Monitored") + special(SPC_NOMOD) + interest(3) + } + field(SIOL,DBF_INLINK) { + prompt("Simulation Input Link") + promptgroup("90 - Simulate") + interest(1) + } + field(SVAL,DBF_ULONG) { + prompt("Simulation Value") + } + field(SIML,DBF_INLINK) { + prompt("Simulation Mode Link") + promptgroup("90 - Simulate") + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + special(SPC_MOD) + interest(1) + menu(menuSimm) + } + field(SIMS,DBF_MENU) { + prompt("Simulation Mode Severity") + promptgroup("90 - Simulate") + interest(2) + menu(menuAlarmSevr) + } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } + +=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/modules/database/src/std/rec/boRecord.dbd b/modules/database/src/std/rec/boRecord.dbd deleted file mode 100644 index 346f0f22c..000000000 --- a/modules/database/src/std/rec/boRecord.dbd +++ /dev/null @@ -1,182 +0,0 @@ -#************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne -# National Laboratory. -# Copyright (c) 2002 The Regents of the University of California, as -# Operator of Los Alamos National Laboratory. -# EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. -#************************************************************************* -recordtype(bo) { - include "dbCommon.dbd" - field(VAL,DBF_ENUM) { - prompt("Current Value") - promptgroup("50 - Output") - asl(ASL0) - pp(TRUE) - } - field(OMSL,DBF_MENU) { - prompt("Output Mode Select") - promptgroup("50 - Output") - interest(1) - menu(menuOmsl) - } - field(DOL,DBF_INLINK) { - prompt("Desired Output Loc") - promptgroup("40 - Input") - interest(1) - } - field(OUT,DBF_OUTLINK) { - prompt("Output Specification") - promptgroup("50 - Output") - interest(1) - } - field(HIGH,DBF_DOUBLE) { - prompt("Seconds to Hold High") - promptgroup("30 - Action") - interest(1) - } - field(ZNAM,DBF_STRING) { - prompt("Zero Name") - promptgroup("80 - Display") - pp(TRUE) - interest(1) - size(26) - prop(YES) - } - field(ONAM,DBF_STRING) { - prompt("One Name") - promptgroup("80 - Display") - pp(TRUE) - interest(1) - size(26) - prop(YES) - } - field(RVAL,DBF_ULONG) { - prompt("Raw Value") - pp(TRUE) - } - field(ORAW,DBF_ULONG) { - prompt("prev Raw Value") - special(SPC_NOMOD) - interest(3) - } - field(MASK,DBF_ULONG) { - prompt("Hardware Mask") - special(SPC_NOMOD) - interest(1) - } - field(RPVT,DBF_NOACCESS) { - prompt("Record Private") - special(SPC_NOMOD) - interest(4) - extra("void * rpvt") - } - field(WDPT,DBF_NOACCESS) { - prompt("Watch Dog Timer ID") - special(SPC_NOMOD) - interest(4) - extra("void * wdpt") - } - field(ZSV,DBF_MENU) { - prompt("Zero Error Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(OSV,DBF_MENU) { - prompt("One Error Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(COSV,DBF_MENU) { - prompt("Change of State Sevr") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(RBV,DBF_ULONG) { - prompt("Readback Value") - special(SPC_NOMOD) - } - field(ORBV,DBF_ULONG) { - prompt("Prev Readback Value") - special(SPC_NOMOD) - interest(3) - } - field(MLST,DBF_USHORT) { - prompt("Last Value Monitored") - special(SPC_NOMOD) - interest(3) - } - field(LALM,DBF_USHORT) { - prompt("Last Value Alarmed") - special(SPC_NOMOD) - interest(3) - } - field(SIOL,DBF_OUTLINK) { - prompt("Simulation Output Link") - promptgroup("90 - Simulate") - interest(1) - } - field(SIML,DBF_INLINK) { - prompt("Simulation Mode Link") - promptgroup("90 - Simulate") - interest(1) - } - field(SIMM,DBF_MENU) { - prompt("Simulation Mode") - special(SPC_MOD) - interest(1) - menu(menuYesNo) - } - field(SIMS,DBF_MENU) { - prompt("Simulation Mode Severity") - promptgroup("90 - Simulate") - interest(2) - menu(menuAlarmSevr) - } - field(OLDSIMM,DBF_MENU) { - prompt("Prev. Simulation Mode") - special(SPC_NOMOD) - interest(4) - menu(menuSimm) - } - field(SSCN,DBF_MENU) { - prompt("Sim. Mode Scan") - promptgroup("90 - Simulate") - interest(1) - menu(menuScan) - initial("65535") - } - field(SDLY,DBF_DOUBLE) { - prompt("Sim. Mode Async Delay") - promptgroup("90 - Simulate") - interest(2) - initial("-1.0") - } - %#include "callback.h" - field(SIMPVT,DBF_NOACCESS) { - prompt("Sim. Mode Private") - special(SPC_NOMOD) - interest(4) - extra("CALLBACK *simpvt") - } - field(IVOA,DBF_MENU) { - prompt("INVALID outpt action") - promptgroup("50 - Output") - interest(2) - menu(menuIvoa) - } - field(IVOV,DBF_USHORT) { - prompt("INVALID output value") - promptgroup("50 - Output") - interest(2) - } -} - -variable(boHIGHprecision, int) -variable(boHIGHlimit, double) diff --git a/modules/database/src/std/rec/boRecord.dbd.pod b/modules/database/src/std/rec/boRecord.dbd.pod new file mode 100644 index 000000000..fe0a98e6e --- /dev/null +++ b/modules/database/src/std/rec/boRecord.dbd.pod @@ -0,0 +1,611 @@ +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE 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") + promptgroup("50 - Output") + asl(ASL0) + pp(TRUE) + } + field(OMSL,DBF_MENU) { + prompt("Output Mode Select") + promptgroup("50 - Output") + interest(1) + menu(menuOmsl) + } + field(DOL,DBF_INLINK) { + prompt("Desired Output Loc") + promptgroup("40 - Input") + interest(1) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + promptgroup("50 - Output") + interest(1) + } + field(HIGH,DBF_DOUBLE) { + prompt("Seconds to Hold High") + promptgroup("30 - Action") + interest(1) + } + field(ZNAM,DBF_STRING) { + prompt("Zero Name") + promptgroup("80 - Display") + pp(TRUE) + interest(1) + size(26) + prop(YES) + } + field(ONAM,DBF_STRING) { + prompt("One Name") + promptgroup("80 - Display") + pp(TRUE) + interest(1) + size(26) + prop(YES) + } + field(RVAL,DBF_ULONG) { + prompt("Raw Value") + pp(TRUE) + } + field(ORAW,DBF_ULONG) { + prompt("prev Raw Value") + special(SPC_NOMOD) + interest(3) + } + field(MASK,DBF_ULONG) { + prompt("Hardware Mask") + special(SPC_NOMOD) + interest(1) + } + field(RPVT,DBF_NOACCESS) { + prompt("Record Private") + special(SPC_NOMOD) + interest(4) + extra("void * rpvt") + } + field(WDPT,DBF_NOACCESS) { + prompt("Watch Dog Timer ID") + special(SPC_NOMOD) + interest(4) + extra("void * wdpt") + } + field(ZSV,DBF_MENU) { + prompt("Zero Error Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(OSV,DBF_MENU) { + prompt("One Error Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(COSV,DBF_MENU) { + prompt("Change of State Sevr") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(RBV,DBF_ULONG) { + prompt("Readback Value") + special(SPC_NOMOD) + } + field(ORBV,DBF_ULONG) { + prompt("Prev Readback Value") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_USHORT) { + prompt("Last Value Monitored") + special(SPC_NOMOD) + interest(3) + } + field(LALM,DBF_USHORT) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(SIOL,DBF_OUTLINK) { + prompt("Simulation Output Link") + promptgroup("90 - Simulate") + interest(1) + } + field(SIML,DBF_INLINK) { + prompt("Simulation Mode Link") + promptgroup("90 - Simulate") + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + special(SPC_MOD) + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Simulation Mode Severity") + promptgroup("90 - Simulate") + interest(2) + menu(menuAlarmSevr) + } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } + field(IVOA,DBF_MENU) { + prompt("INVALID outpt action") + promptgroup("50 - Output") + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_USHORT) { + prompt("INVALID output value") + 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) +variable(boHIGHlimit, double) diff --git a/modules/database/src/std/rec/calcRecord.dbd b/modules/database/src/std/rec/calcRecord.dbd deleted file mode 100644 index e7eb0eee3..000000000 --- a/modules/database/src/std/rec/calcRecord.dbd +++ /dev/null @@ -1,324 +0,0 @@ -#************************************************************************* -# Copyright (c) 2007 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. -#************************************************************************* -recordtype(calc) { - include "dbCommon.dbd" - field(VAL,DBF_DOUBLE) { - prompt("Result") - promptgroup("50 - Output") - asl(ASL0) - } - field(CALC,DBF_STRING) { - prompt("Calculation") - promptgroup("30 - Action") - special(SPC_CALC) - pp(TRUE) - size(80) - initial("0") - } - field(INPA,DBF_INLINK) { - prompt("Input A") - promptgroup("41 - Input A-F") - interest(1) - } - field(INPB,DBF_INLINK) { - prompt("Input B") - promptgroup("41 - Input A-F") - interest(1) - } - field(INPC,DBF_INLINK) { - prompt("Input C") - promptgroup("41 - Input A-F") - interest(1) - } - field(INPD,DBF_INLINK) { - prompt("Input D") - promptgroup("41 - Input A-F") - interest(1) - } - field(INPE,DBF_INLINK) { - prompt("Input E") - promptgroup("41 - Input A-F") - interest(1) - } - field(INPF,DBF_INLINK) { - prompt("Input F") - promptgroup("41 - Input A-F") - interest(1) - } - field(INPG,DBF_INLINK) { - prompt("Input G") - promptgroup("42 - Input G-L") - interest(1) - } - field(INPH,DBF_INLINK) { - prompt("Input H") - promptgroup("42 - Input G-L") - interest(1) - } - field(INPI,DBF_INLINK) { - prompt("Input I") - promptgroup("42 - Input G-L") - interest(1) - } - field(INPJ,DBF_INLINK) { - prompt("Input J") - promptgroup("42 - Input G-L") - interest(1) - } - field(INPK,DBF_INLINK) { - prompt("Input K") - promptgroup("42 - Input G-L") - interest(1) - } - field(INPL,DBF_INLINK) { - prompt("Input L") - promptgroup("42 - Input G-L") - interest(1) - } - field(EGU,DBF_STRING) { - prompt("Engineering Units") - promptgroup("80 - Display") - interest(1) - size(16) - prop(YES) - } - field(PREC,DBF_SHORT) { - prompt("Display Precision") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(HOPR,DBF_DOUBLE) { - prompt("High Operating Rng") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(LOPR,DBF_DOUBLE) { - prompt("Low Operating Range") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(HIHI,DBF_DOUBLE) { - prompt("Hihi Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(LOLO,DBF_DOUBLE) { - prompt("Lolo Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(HIGH,DBF_DOUBLE) { - prompt("High Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(LOW,DBF_DOUBLE) { - prompt("Low Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(HHSV,DBF_MENU) { - prompt("Hihi Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(LLSV,DBF_MENU) { - prompt("Lolo Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(HSV,DBF_MENU) { - prompt("High Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(LSV,DBF_MENU) { - prompt("Low Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(AFTC, DBF_DOUBLE) { - prompt("Alarm Filter Time Constant") - promptgroup("70 - Alarm") - interest(1) - } - field(AFVL, DBF_DOUBLE) { - prompt("Alarm Filter Value") - special(SPC_NOMOD) - interest(3) - } - field(HYST,DBF_DOUBLE) { - prompt("Alarm Deadband") - promptgroup("70 - Alarm") - interest(1) - } - field(ADEL,DBF_DOUBLE) { - prompt("Archive Deadband") - promptgroup("80 - Display") - interest(1) - } - field(MDEL,DBF_DOUBLE) { - prompt("Monitor Deadband") - promptgroup("80 - Display") - interest(1) - } - field(A,DBF_DOUBLE) { - prompt("Value of Input A") - pp(TRUE) - } - field(B,DBF_DOUBLE) { - prompt("Value of Input B") - pp(TRUE) - } - field(C,DBF_DOUBLE) { - prompt("Value of Input C") - pp(TRUE) - } - field(D,DBF_DOUBLE) { - prompt("Value of Input D") - pp(TRUE) - } - field(E,DBF_DOUBLE) { - prompt("Value of Input E") - pp(TRUE) - } - field(F,DBF_DOUBLE) { - prompt("Value of Input F") - pp(TRUE) - } - field(G,DBF_DOUBLE) { - prompt("Value of Input G") - pp(TRUE) - } - field(H,DBF_DOUBLE) { - prompt("Value of Input H") - pp(TRUE) - } - field(I,DBF_DOUBLE) { - prompt("Value of Input I") - pp(TRUE) - } - field(J,DBF_DOUBLE) { - prompt("Value of Input J") - pp(TRUE) - } - field(K,DBF_DOUBLE) { - prompt("Value of Input K") - pp(TRUE) - } - field(L,DBF_DOUBLE) { - prompt("Value of Input L") - pp(TRUE) - } - field(LA,DBF_DOUBLE) { - prompt("Prev Value of A") - special(SPC_NOMOD) - interest(3) - } - field(LB,DBF_DOUBLE) { - prompt("Prev Value of B") - special(SPC_NOMOD) - interest(3) - } - field(LC,DBF_DOUBLE) { - prompt("Prev Value of C") - special(SPC_NOMOD) - interest(3) - } - field(LD,DBF_DOUBLE) { - prompt("Prev Value of D") - special(SPC_NOMOD) - interest(3) - } - field(LE,DBF_DOUBLE) { - prompt("Prev Value of E") - special(SPC_NOMOD) - interest(3) - } - field(LF,DBF_DOUBLE) { - prompt("Prev Value of F") - special(SPC_NOMOD) - interest(3) - } - field(LG,DBF_DOUBLE) { - prompt("Prev Value of G") - special(SPC_NOMOD) - interest(3) - } - field(LH,DBF_DOUBLE) { - prompt("Prev Value of H") - special(SPC_NOMOD) - interest(3) - } - field(LI,DBF_DOUBLE) { - prompt("Prev Value of I") - special(SPC_NOMOD) - interest(3) - } - field(LJ,DBF_DOUBLE) { - prompt("Prev Value of J") - special(SPC_NOMOD) - interest(3) - } - field(LK,DBF_DOUBLE) { - prompt("Prev Value of K") - special(SPC_NOMOD) - interest(3) - } - field(LL,DBF_DOUBLE) { - prompt("Prev Value of L") - special(SPC_NOMOD) - interest(3) - } - field(LALM,DBF_DOUBLE) { - prompt("Last Value Alarmed") - special(SPC_NOMOD) - interest(3) - } - field(ALST,DBF_DOUBLE) { - prompt("Last Value Archived") - special(SPC_NOMOD) - interest(3) - } - field(MLST,DBF_DOUBLE) { - prompt("Last Val Monitored") - special(SPC_NOMOD) - interest(3) - } - %#include "postfix.h" - field(RPCL,DBF_NOACCESS) { - prompt("Reverse Polish Calc") - special(SPC_NOMOD) - interest(4) - extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") - } -} diff --git a/modules/database/src/std/rec/calcRecord.dbd.pod b/modules/database/src/std/rec/calcRecord.dbd.pod new file mode 100644 index 000000000..c50667fb3 --- /dev/null +++ b/modules/database/src/std/rec/calcRecord.dbd.pod @@ -0,0 +1,948 @@ +#************************************************************************* +# Copyright (c) 2007 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. +#************************************************************************* + +=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") + promptgroup("50 - Output") + asl(ASL0) + } + field(CALC,DBF_STRING) { + prompt("Calculation") + promptgroup("30 - Action") + special(SPC_CALC) + pp(TRUE) + size(80) + initial("0") + } + field(INPA,DBF_INLINK) { + prompt("Input A") + promptgroup("41 - Input A-F") + interest(1) + } + field(INPB,DBF_INLINK) { + prompt("Input B") + promptgroup("41 - Input A-F") + interest(1) + } + field(INPC,DBF_INLINK) { + prompt("Input C") + promptgroup("41 - Input A-F") + interest(1) + } + field(INPD,DBF_INLINK) { + prompt("Input D") + promptgroup("41 - Input A-F") + interest(1) + } + field(INPE,DBF_INLINK) { + prompt("Input E") + promptgroup("41 - Input A-F") + interest(1) + } + field(INPF,DBF_INLINK) { + prompt("Input F") + promptgroup("41 - Input A-F") + interest(1) + } + field(INPG,DBF_INLINK) { + prompt("Input G") + promptgroup("42 - Input G-L") + interest(1) + } + field(INPH,DBF_INLINK) { + prompt("Input H") + promptgroup("42 - Input G-L") + interest(1) + } + field(INPI,DBF_INLINK) { + prompt("Input I") + promptgroup("42 - Input G-L") + interest(1) + } + field(INPJ,DBF_INLINK) { + prompt("Input J") + promptgroup("42 - Input G-L") + interest(1) + } + field(INPK,DBF_INLINK) { + prompt("Input K") + promptgroup("42 - Input G-L") + interest(1) + } + field(INPL,DBF_INLINK) { + prompt("Input L") + promptgroup("42 - Input G-L") + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units") + promptgroup("80 - Display") + interest(1) + size(16) + prop(YES) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Rng") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(HIHI,DBF_DOUBLE) { + prompt("Hihi Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(LOLO,DBF_DOUBLE) { + prompt("Lolo Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(HIGH,DBF_DOUBLE) { + prompt("High Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(LOW,DBF_DOUBLE) { + prompt("Low Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(HHSV,DBF_MENU) { + prompt("Hihi Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(LLSV,DBF_MENU) { + prompt("Lolo Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(HSV,DBF_MENU) { + prompt("High Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(LSV,DBF_MENU) { + prompt("Low Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(AFTC, DBF_DOUBLE) { + prompt("Alarm Filter Time Constant") + promptgroup("70 - Alarm") + interest(1) + } + field(AFVL, DBF_DOUBLE) { + prompt("Alarm Filter Value") + special(SPC_NOMOD) + interest(3) + } + field(HYST,DBF_DOUBLE) { + prompt("Alarm Deadband") + promptgroup("70 - Alarm") + interest(1) + } + field(ADEL,DBF_DOUBLE) { + prompt("Archive Deadband") + promptgroup("80 - Display") + interest(1) + } + field(MDEL,DBF_DOUBLE) { + prompt("Monitor Deadband") + promptgroup("80 - Display") + interest(1) + } + field(A,DBF_DOUBLE) { + prompt("Value of Input A") + pp(TRUE) + } + field(B,DBF_DOUBLE) { + prompt("Value of Input B") + pp(TRUE) + } + field(C,DBF_DOUBLE) { + prompt("Value of Input C") + pp(TRUE) + } + field(D,DBF_DOUBLE) { + prompt("Value of Input D") + pp(TRUE) + } + field(E,DBF_DOUBLE) { + prompt("Value of Input E") + pp(TRUE) + } + field(F,DBF_DOUBLE) { + prompt("Value of Input F") + pp(TRUE) + } + field(G,DBF_DOUBLE) { + prompt("Value of Input G") + pp(TRUE) + } + field(H,DBF_DOUBLE) { + prompt("Value of Input H") + pp(TRUE) + } + field(I,DBF_DOUBLE) { + prompt("Value of Input I") + pp(TRUE) + } + field(J,DBF_DOUBLE) { + prompt("Value of Input J") + pp(TRUE) + } + field(K,DBF_DOUBLE) { + prompt("Value of Input K") + pp(TRUE) + } + field(L,DBF_DOUBLE) { + prompt("Value of Input L") + pp(TRUE) + } + field(LA,DBF_DOUBLE) { + prompt("Prev Value of A") + special(SPC_NOMOD) + interest(3) + } + field(LB,DBF_DOUBLE) { + prompt("Prev Value of B") + special(SPC_NOMOD) + interest(3) + } + field(LC,DBF_DOUBLE) { + prompt("Prev Value of C") + special(SPC_NOMOD) + interest(3) + } + field(LD,DBF_DOUBLE) { + prompt("Prev Value of D") + special(SPC_NOMOD) + interest(3) + } + field(LE,DBF_DOUBLE) { + prompt("Prev Value of E") + special(SPC_NOMOD) + interest(3) + } + field(LF,DBF_DOUBLE) { + prompt("Prev Value of F") + special(SPC_NOMOD) + interest(3) + } + field(LG,DBF_DOUBLE) { + prompt("Prev Value of G") + special(SPC_NOMOD) + interest(3) + } + field(LH,DBF_DOUBLE) { + prompt("Prev Value of H") + special(SPC_NOMOD) + interest(3) + } + field(LI,DBF_DOUBLE) { + prompt("Prev Value of I") + special(SPC_NOMOD) + interest(3) + } + field(LJ,DBF_DOUBLE) { + prompt("Prev Value of J") + special(SPC_NOMOD) + interest(3) + } + field(LK,DBF_DOUBLE) { + prompt("Prev Value of K") + special(SPC_NOMOD) + interest(3) + } + field(LL,DBF_DOUBLE) { + prompt("Prev Value of L") + special(SPC_NOMOD) + interest(3) + } + field(LALM,DBF_DOUBLE) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(ALST,DBF_DOUBLE) { + prompt("Last Value Archived") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_DOUBLE) { + prompt("Last Val Monitored") + special(SPC_NOMOD) + interest(3) + } + %#include "postfix.h" + field(RPCL,DBF_NOACCESS) { + prompt("Reverse Polish Calc") + special(SPC_NOMOD) + 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/modules/database/src/std/rec/calcoutRecord.dbd b/modules/database/src/std/rec/calcoutRecord.dbd deleted file mode 100644 index 5bf2e54de..000000000 --- a/modules/database/src/std/rec/calcoutRecord.dbd +++ /dev/null @@ -1,530 +0,0 @@ -#************************************************************************* -# Copyright (c) 2007 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. -#************************************************************************* -menu(calcoutOOPT) { - choice(calcoutOOPT_Every_Time,"Every Time") - choice(calcoutOOPT_On_Change,"On Change") - choice(calcoutOOPT_When_Zero,"When Zero") - choice(calcoutOOPT_When_Non_zero,"When Non-zero") - choice(calcoutOOPT_Transition_To_Zero,"Transition To Zero") - choice(calcoutOOPT_Transition_To_Non_zero,"Transition To Non-zero") -} -menu(calcoutDOPT) { - choice(calcoutDOPT_Use_VAL,"Use CALC") - choice(calcoutDOPT_Use_OVAL,"Use OCAL") -} -menu(calcoutINAV) { - choice(calcoutINAV_EXT_NC,"Ext PV NC") - choice(calcoutINAV_EXT,"Ext PV OK") - choice(calcoutINAV_LOC,"Local PV") - choice(calcoutINAV_CON,"Constant") -} -recordtype(calcout) { - include "dbCommon.dbd" - field(RPVT,DBF_NOACCESS) { - prompt("Record Private") - special(SPC_NOMOD) - interest(4) - extra("struct rpvtStruct *rpvt") - } - field(VAL,DBF_DOUBLE) { - prompt("Result") - promptgroup("50 - Output") - asl(ASL0) - } - field(PVAL,DBF_DOUBLE) { - prompt("Previous Value") - } - field(CALC,DBF_STRING) { - prompt("Calculation") - promptgroup("30 - Action") - special(SPC_CALC) - pp(TRUE) - size(80) - initial("0") - } - field(CLCV,DBF_LONG) { - prompt("CALC Valid") - interest(1) - } - field(INPA,DBF_INLINK) { - prompt("Input A") - special(SPC_MOD) - promptgroup("41 - Input A-F") - interest(1) - } - field(INPB,DBF_INLINK) { - prompt("Input B") - special(SPC_MOD) - promptgroup("41 - Input A-F") - interest(1) - } - field(INPC,DBF_INLINK) { - prompt("Input C") - special(SPC_MOD) - promptgroup("41 - Input A-F") - interest(1) - } - field(INPD,DBF_INLINK) { - prompt("Input D") - special(SPC_MOD) - promptgroup("41 - Input A-F") - interest(1) - } - field(INPE,DBF_INLINK) { - prompt("Input E") - special(SPC_MOD) - promptgroup("41 - Input A-F") - interest(1) - } - field(INPF,DBF_INLINK) { - prompt("Input F") - special(SPC_MOD) - promptgroup("41 - Input A-F") - interest(1) - } - field(INPG,DBF_INLINK) { - prompt("Input G") - special(SPC_MOD) - promptgroup("42 - Input G-L") - interest(1) - } - field(INPH,DBF_INLINK) { - prompt("Input H") - special(SPC_MOD) - promptgroup("42 - Input G-L") - interest(1) - } - field(INPI,DBF_INLINK) { - prompt("Input I") - special(SPC_MOD) - promptgroup("42 - Input G-L") - interest(1) - } - field(INPJ,DBF_INLINK) { - prompt("Input J") - special(SPC_MOD) - promptgroup("42 - Input G-L") - interest(1) - } - field(INPK,DBF_INLINK) { - prompt("Input K") - special(SPC_MOD) - promptgroup("42 - Input G-L") - interest(1) - } - field(INPL,DBF_INLINK) { - prompt("Input L") - special(SPC_MOD) - promptgroup("42 - Input G-L") - interest(1) - } - field(OUT,DBF_OUTLINK) { - prompt("Output Specification") - special(SPC_MOD) - promptgroup("50 - Output") - interest(1) - } - field(INAV,DBF_MENU) { - prompt("INPA PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INBV,DBF_MENU) { - prompt("INPB PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INCV,DBF_MENU) { - prompt("INPC PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INDV,DBF_MENU) { - prompt("INPD PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INEV,DBF_MENU) { - prompt("INPE PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INFV,DBF_MENU) { - prompt("INPF PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INGV,DBF_MENU) { - prompt("INPG PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INHV,DBF_MENU) { - prompt("INPH PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INIV,DBF_MENU) { - prompt("INPI PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INJV,DBF_MENU) { - prompt("INPJ PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INKV,DBF_MENU) { - prompt("INPK PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(INLV,DBF_MENU) { - prompt("INPL PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - initial("1") - } - field(OUTV,DBF_MENU) { - prompt("OUT PV Status") - special(SPC_NOMOD) - interest(1) - menu(calcoutINAV) - } - field(OOPT,DBF_MENU) { - prompt("Output Execute Opt") - promptgroup("50 - Output") - interest(1) - menu(calcoutOOPT) - } - field(ODLY,DBF_DOUBLE) { - prompt("Output Execute Delay") - promptgroup("50 - Output") - asl(ASL0) - interest(1) - } - field(DLYA,DBF_USHORT) { - prompt("Output Delay Active") - special(SPC_NOMOD) - asl(ASL0) - } - field(DOPT,DBF_MENU) { - prompt("Output Data Opt") - promptgroup("30 - Action") - interest(1) - menu(calcoutDOPT) - } - field(OCAL,DBF_STRING) { - prompt("Output Calculation") - promptgroup("30 - Action") - special(SPC_CALC) - pp(TRUE) - size(80) - initial("0") - } - field(OCLV,DBF_LONG) { - prompt("OCAL Valid") - interest(1) - } - field(OEVT,DBF_STRING) { - prompt("Event To Issue") - promptgroup("30 - Action") - special(SPC_MOD) - asl(ASL0) - size(40) - } - %#include "dbScan.h" - field(EPVT, DBF_NOACCESS) { - prompt("Event private") - special(SPC_NOMOD) - interest(4) - extra("EVENTPVT epvt") - } - field(IVOA,DBF_MENU) { - prompt("INVALID output action") - promptgroup("50 - Output") - interest(2) - menu(menuIvoa) - } - field(IVOV,DBF_DOUBLE) { - prompt("INVALID output value") - promptgroup("50 - Output") - interest(2) - } - field(EGU,DBF_STRING) { - prompt("Engineering Units") - promptgroup("80 - Display") - interest(1) - size(16) - prop(YES) - } - field(PREC,DBF_SHORT) { - prompt("Display Precision") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(HOPR,DBF_DOUBLE) { - prompt("High Operating Rng") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(LOPR,DBF_DOUBLE) { - prompt("Low Operating Range") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(HIHI,DBF_DOUBLE) { - prompt("Hihi Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(LOLO,DBF_DOUBLE) { - prompt("Lolo Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(HIGH,DBF_DOUBLE) { - prompt("High Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(LOW,DBF_DOUBLE) { - prompt("Low Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(HHSV,DBF_MENU) { - prompt("Hihi Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(LLSV,DBF_MENU) { - prompt("Lolo Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(HSV,DBF_MENU) { - prompt("High Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(LSV,DBF_MENU) { - prompt("Low Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(HYST,DBF_DOUBLE) { - prompt("Alarm Deadband") - promptgroup("70 - Alarm") - interest(1) - } - field(ADEL,DBF_DOUBLE) { - prompt("Archive Deadband") - promptgroup("80 - Display") - interest(1) - } - field(MDEL,DBF_DOUBLE) { - prompt("Monitor Deadband") - promptgroup("80 - Display") - interest(1) - } - field(A,DBF_DOUBLE) { - prompt("Value of Input A") - pp(TRUE) - } - field(B,DBF_DOUBLE) { - prompt("Value of Input B") - pp(TRUE) - } - field(C,DBF_DOUBLE) { - prompt("Value of Input C") - pp(TRUE) - } - field(D,DBF_DOUBLE) { - prompt("Value of Input D") - pp(TRUE) - } - field(E,DBF_DOUBLE) { - prompt("Value of Input E") - pp(TRUE) - } - field(F,DBF_DOUBLE) { - prompt("Value of Input F") - pp(TRUE) - } - field(G,DBF_DOUBLE) { - prompt("Value of Input G") - pp(TRUE) - } - field(H,DBF_DOUBLE) { - prompt("Value of Input H") - pp(TRUE) - } - field(I,DBF_DOUBLE) { - prompt("Value of Input I") - pp(TRUE) - } - field(J,DBF_DOUBLE) { - prompt("Value of Input J") - pp(TRUE) - } - field(K,DBF_DOUBLE) { - prompt("Value of Input K") - pp(TRUE) - } - field(L,DBF_DOUBLE) { - prompt("Value of Input L") - pp(TRUE) - } - field(OVAL,DBF_DOUBLE) { - prompt("Output Value") - asl(ASL0) - } - field(LA,DBF_DOUBLE) { - prompt("Prev Value of A") - special(SPC_NOMOD) - interest(3) - } - field(LB,DBF_DOUBLE) { - prompt("Prev Value of B") - special(SPC_NOMOD) - interest(3) - } - field(LC,DBF_DOUBLE) { - prompt("Prev Value of C") - special(SPC_NOMOD) - interest(3) - } - field(LD,DBF_DOUBLE) { - prompt("Prev Value of D") - special(SPC_NOMOD) - interest(3) - } - field(LE,DBF_DOUBLE) { - prompt("Prev Value of E") - special(SPC_NOMOD) - interest(3) - } - field(LF,DBF_DOUBLE) { - prompt("Prev Value of F") - special(SPC_NOMOD) - interest(3) - } - field(LG,DBF_DOUBLE) { - prompt("Prev Value of G") - special(SPC_NOMOD) - interest(3) - } - field(LH,DBF_DOUBLE) { - prompt("Prev Value of H") - special(SPC_NOMOD) - interest(3) - } - field(LI,DBF_DOUBLE) { - prompt("Prev Value of I") - special(SPC_NOMOD) - interest(3) - } - field(LJ,DBF_DOUBLE) { - prompt("Prev Value of J") - special(SPC_NOMOD) - interest(3) - } - field(LK,DBF_DOUBLE) { - prompt("Prev Value of K") - special(SPC_NOMOD) - interest(3) - } - field(LL,DBF_DOUBLE) { - prompt("Prev Value of L") - special(SPC_NOMOD) - interest(3) - } - field(POVL,DBF_DOUBLE) { - prompt("Prev Value of OVAL") - asl(ASL0) - } - field(LALM,DBF_DOUBLE) { - prompt("Last Value Alarmed") - special(SPC_NOMOD) - interest(3) - } - field(ALST,DBF_DOUBLE) { - prompt("Last Value Archived") - special(SPC_NOMOD) - interest(3) - } - field(MLST,DBF_DOUBLE) { - prompt("Last Val Monitored") - special(SPC_NOMOD) - interest(3) - } - %#include "postfix.h" - field(RPCL,DBF_NOACCESS) { - prompt("Reverse Polish Calc") - special(SPC_NOMOD) - interest(4) - extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") - } - field(ORPC,DBF_NOACCESS) { - prompt("Reverse Polish OCalc") - special(SPC_NOMOD) - interest(4) - extra("char orpc[INFIX_TO_POSTFIX_SIZE(80)]") - } -} - -variable(calcoutODLYprecision, int) -variable(calcoutODLYlimit, double) diff --git a/modules/database/src/std/rec/calcoutRecord.dbd.pod b/modules/database/src/std/rec/calcoutRecord.dbd.pod new file mode 100644 index 000000000..6289f9043 --- /dev/null +++ b/modules/database/src/std/rec/calcoutRecord.dbd.pod @@ -0,0 +1,1271 @@ +#************************************************************************* +# Copyright (c) 2007 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. +#************************************************************************* + +=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") + choice(calcoutOOPT_When_Zero,"When Zero") + choice(calcoutOOPT_When_Non_zero,"When Non-zero") + choice(calcoutOOPT_Transition_To_Zero,"Transition To Zero") + choice(calcoutOOPT_Transition_To_Non_zero,"Transition To Non-zero") +} +menu(calcoutDOPT) { + choice(calcoutDOPT_Use_VAL,"Use CALC") + choice(calcoutDOPT_Use_OVAL,"Use OCAL") +} +menu(calcoutINAV) { + choice(calcoutINAV_EXT_NC,"Ext PV NC") + choice(calcoutINAV_EXT,"Ext PV OK") + 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") + special(SPC_NOMOD) + interest(4) + extra("struct rpvtStruct *rpvt") + } + field(VAL,DBF_DOUBLE) { + prompt("Result") + promptgroup("50 - Output") + asl(ASL0) + } + field(PVAL,DBF_DOUBLE) { + prompt("Previous Value") + } + field(CALC,DBF_STRING) { + prompt("Calculation") + promptgroup("30 - Action") + special(SPC_CALC) + pp(TRUE) + size(80) + initial("0") + } + field(CLCV,DBF_LONG) { + prompt("CALC Valid") + interest(1) + } + field(INPA,DBF_INLINK) { + prompt("Input A") + special(SPC_MOD) + promptgroup("41 - Input A-F") + interest(1) + } + field(INPB,DBF_INLINK) { + prompt("Input B") + special(SPC_MOD) + promptgroup("41 - Input A-F") + interest(1) + } + field(INPC,DBF_INLINK) { + prompt("Input C") + special(SPC_MOD) + promptgroup("41 - Input A-F") + interest(1) + } + field(INPD,DBF_INLINK) { + prompt("Input D") + special(SPC_MOD) + promptgroup("41 - Input A-F") + interest(1) + } + field(INPE,DBF_INLINK) { + prompt("Input E") + special(SPC_MOD) + promptgroup("41 - Input A-F") + interest(1) + } + field(INPF,DBF_INLINK) { + prompt("Input F") + special(SPC_MOD) + promptgroup("41 - Input A-F") + interest(1) + } + field(INPG,DBF_INLINK) { + prompt("Input G") + special(SPC_MOD) + promptgroup("42 - Input G-L") + interest(1) + } + field(INPH,DBF_INLINK) { + prompt("Input H") + special(SPC_MOD) + promptgroup("42 - Input G-L") + interest(1) + } + field(INPI,DBF_INLINK) { + prompt("Input I") + special(SPC_MOD) + promptgroup("42 - Input G-L") + interest(1) + } + field(INPJ,DBF_INLINK) { + prompt("Input J") + special(SPC_MOD) + promptgroup("42 - Input G-L") + interest(1) + } + field(INPK,DBF_INLINK) { + prompt("Input K") + special(SPC_MOD) + promptgroup("42 - Input G-L") + interest(1) + } + field(INPL,DBF_INLINK) { + prompt("Input L") + special(SPC_MOD) + promptgroup("42 - Input G-L") + interest(1) + } + field(OUT,DBF_OUTLINK) { + prompt("Output Specification") + special(SPC_MOD) + promptgroup("50 - Output") + interest(1) + } + field(INAV,DBF_MENU) { + prompt("INPA PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INBV,DBF_MENU) { + prompt("INPB PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INCV,DBF_MENU) { + prompt("INPC PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INDV,DBF_MENU) { + prompt("INPD PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INEV,DBF_MENU) { + prompt("INPE PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INFV,DBF_MENU) { + prompt("INPF PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INGV,DBF_MENU) { + prompt("INPG PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INHV,DBF_MENU) { + prompt("INPH PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INIV,DBF_MENU) { + prompt("INPI PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INJV,DBF_MENU) { + prompt("INPJ PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INKV,DBF_MENU) { + prompt("INPK PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(INLV,DBF_MENU) { + prompt("INPL PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + initial("1") + } + field(OUTV,DBF_MENU) { + prompt("OUT PV Status") + special(SPC_NOMOD) + interest(1) + menu(calcoutINAV) + } + field(OOPT,DBF_MENU) { + prompt("Output Execute Opt") + promptgroup("50 - Output") + interest(1) + menu(calcoutOOPT) + } + field(ODLY,DBF_DOUBLE) { + prompt("Output Execute Delay") + promptgroup("50 - Output") + asl(ASL0) + interest(1) + } + field(DLYA,DBF_USHORT) { + prompt("Output Delay Active") + special(SPC_NOMOD) + asl(ASL0) + } + field(DOPT,DBF_MENU) { + prompt("Output Data Opt") + promptgroup("30 - Action") + interest(1) + menu(calcoutDOPT) + } + field(OCAL,DBF_STRING) { + prompt("Output Calculation") + promptgroup("30 - Action") + special(SPC_CALC) + pp(TRUE) + size(80) + initial("0") + } + field(OCLV,DBF_LONG) { + prompt("OCAL Valid") + interest(1) + } + field(OEVT,DBF_STRING) { + prompt("Event To Issue") + promptgroup("30 - Action") + special(SPC_MOD) + asl(ASL0) + size(40) + } + %#include "dbScan.h" + field(EPVT, DBF_NOACCESS) { + prompt("Event private") + special(SPC_NOMOD) + interest(4) + extra("EVENTPVT epvt") + } + field(IVOA,DBF_MENU) { + prompt("INVALID output action") + promptgroup("50 - Output") + interest(2) + menu(menuIvoa) + } + field(IVOV,DBF_DOUBLE) { + prompt("INVALID output value") + promptgroup("50 - Output") + interest(2) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units") + promptgroup("80 - Display") + interest(1) + size(16) + prop(YES) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Rng") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(HIHI,DBF_DOUBLE) { + prompt("Hihi Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(LOLO,DBF_DOUBLE) { + prompt("Lolo Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(HIGH,DBF_DOUBLE) { + prompt("High Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(LOW,DBF_DOUBLE) { + prompt("Low Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(HHSV,DBF_MENU) { + prompt("Hihi Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(LLSV,DBF_MENU) { + prompt("Lolo Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(HSV,DBF_MENU) { + prompt("High Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(LSV,DBF_MENU) { + prompt("Low Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(HYST,DBF_DOUBLE) { + prompt("Alarm Deadband") + promptgroup("70 - Alarm") + interest(1) + } + field(ADEL,DBF_DOUBLE) { + prompt("Archive Deadband") + promptgroup("80 - Display") + interest(1) + } + field(MDEL,DBF_DOUBLE) { + prompt("Monitor Deadband") + promptgroup("80 - Display") + interest(1) + } + field(A,DBF_DOUBLE) { + prompt("Value of Input A") + pp(TRUE) + } + field(B,DBF_DOUBLE) { + prompt("Value of Input B") + pp(TRUE) + } + field(C,DBF_DOUBLE) { + prompt("Value of Input C") + pp(TRUE) + } + field(D,DBF_DOUBLE) { + prompt("Value of Input D") + pp(TRUE) + } + field(E,DBF_DOUBLE) { + prompt("Value of Input E") + pp(TRUE) + } + field(F,DBF_DOUBLE) { + prompt("Value of Input F") + pp(TRUE) + } + field(G,DBF_DOUBLE) { + prompt("Value of Input G") + pp(TRUE) + } + field(H,DBF_DOUBLE) { + prompt("Value of Input H") + pp(TRUE) + } + field(I,DBF_DOUBLE) { + prompt("Value of Input I") + pp(TRUE) + } + field(J,DBF_DOUBLE) { + prompt("Value of Input J") + pp(TRUE) + } + field(K,DBF_DOUBLE) { + prompt("Value of Input K") + pp(TRUE) + } + field(L,DBF_DOUBLE) { + prompt("Value of Input L") + pp(TRUE) + } + field(OVAL,DBF_DOUBLE) { + prompt("Output Value") + asl(ASL0) + } + field(LA,DBF_DOUBLE) { + prompt("Prev Value of A") + special(SPC_NOMOD) + interest(3) + } + field(LB,DBF_DOUBLE) { + prompt("Prev Value of B") + special(SPC_NOMOD) + interest(3) + } + field(LC,DBF_DOUBLE) { + prompt("Prev Value of C") + special(SPC_NOMOD) + interest(3) + } + field(LD,DBF_DOUBLE) { + prompt("Prev Value of D") + special(SPC_NOMOD) + interest(3) + } + field(LE,DBF_DOUBLE) { + prompt("Prev Value of E") + special(SPC_NOMOD) + interest(3) + } + field(LF,DBF_DOUBLE) { + prompt("Prev Value of F") + special(SPC_NOMOD) + interest(3) + } + field(LG,DBF_DOUBLE) { + prompt("Prev Value of G") + special(SPC_NOMOD) + interest(3) + } + field(LH,DBF_DOUBLE) { + prompt("Prev Value of H") + special(SPC_NOMOD) + interest(3) + } + field(LI,DBF_DOUBLE) { + prompt("Prev Value of I") + special(SPC_NOMOD) + interest(3) + } + field(LJ,DBF_DOUBLE) { + prompt("Prev Value of J") + special(SPC_NOMOD) + interest(3) + } + field(LK,DBF_DOUBLE) { + prompt("Prev Value of K") + special(SPC_NOMOD) + interest(3) + } + field(LL,DBF_DOUBLE) { + prompt("Prev Value of L") + special(SPC_NOMOD) + interest(3) + } + field(POVL,DBF_DOUBLE) { + prompt("Prev Value of OVAL") + asl(ASL0) + } + field(LALM,DBF_DOUBLE) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(ALST,DBF_DOUBLE) { + prompt("Last Value Archived") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_DOUBLE) { + prompt("Last Val Monitored") + special(SPC_NOMOD) + interest(3) + } + %#include "postfix.h" + field(RPCL,DBF_NOACCESS) { + prompt("Reverse Polish Calc") + special(SPC_NOMOD) + interest(4) + extra("char rpcl[INFIX_TO_POSTFIX_SIZE(80)]") + } + field(ORPC,DBF_NOACCESS) { + prompt("Reverse Polish OCalc") + special(SPC_NOMOD) + 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) +variable(calcoutODLYlimit, double) diff --git a/modules/database/src/std/rec/dfanoutRecord.dbd b/modules/database/src/std/rec/dfanoutRecord.dbd deleted file mode 100644 index c2eb42a75..000000000 --- a/modules/database/src/std/rec/dfanoutRecord.dbd +++ /dev/null @@ -1,204 +0,0 @@ -#************************************************************************* -# Copyright (c) 2002 The University of Chicago, as Operator of Argonne -# National Laboratory. -# Copyright (c) 2002 The Regents of the University of California, as -# Operator of Los Alamos National Laboratory. -# EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. -#************************************************************************* -menu(dfanoutSELM) { - choice(dfanoutSELM_All,"All") - choice(dfanoutSELM_Specified,"Specified") - choice(dfanoutSELM_Mask,"Mask") -} -recordtype(dfanout) { - include "dbCommon.dbd" - field(VAL,DBF_DOUBLE) { - prompt("Desired Output") - promptgroup("40 - Input") - asl(ASL0) - pp(TRUE) - } - field(SELM,DBF_MENU) { - prompt("Select Mechanism") - promptgroup("30 - Action") - interest(1) - menu(dfanoutSELM) - } - field(SELN,DBF_USHORT) { - prompt("Link Selection") - interest(1) - initial("1") - } - field(SELL,DBF_INLINK) { - prompt("Link Selection Loc") - promptgroup("30 - Action") - interest(1) - } - field(OUTA,DBF_OUTLINK) { - prompt("Output Spec A") - promptgroup("50 - Output") - interest(1) - } - field(OUTB,DBF_OUTLINK) { - prompt("Output Spec B") - promptgroup("50 - Output") - interest(1) - } - field(OUTC,DBF_OUTLINK) { - prompt("Output Spec C") - promptgroup("50 - Output") - interest(1) - } - field(OUTD,DBF_OUTLINK) { - prompt("Output Spec D") - promptgroup("50 - Output") - interest(1) - } - field(OUTE,DBF_OUTLINK) { - prompt("Output Spec E") - promptgroup("50 - Output") - interest(1) - } - field(OUTF,DBF_OUTLINK) { - prompt("Output Spec F") - promptgroup("50 - Output") - interest(1) - } - field(OUTG,DBF_OUTLINK) { - prompt("Output Spec G") - promptgroup("50 - Output") - interest(1) - } - field(OUTH,DBF_OUTLINK) { - prompt("Output Spec H") - promptgroup("50 - Output") - interest(1) - } - field(DOL,DBF_INLINK) { - prompt("Desired Output Loc") - promptgroup("40 - Input") - interest(1) - } - field(OMSL,DBF_MENU) { - prompt("Output Mode Select") - promptgroup("50 - Output") - interest(1) - menu(menuOmsl) - } - field(EGU,DBF_STRING) { - prompt("Engineering Units") - promptgroup("80 - Display") - interest(1) - size(16) - prop(YES) - } - field(PREC,DBF_SHORT) { - prompt("Display Precision") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(HOPR,DBF_DOUBLE) { - prompt("High Operating Range") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(LOPR,DBF_DOUBLE) { - prompt("Low Operating Range") - promptgroup("80 - Display") - interest(1) - prop(YES) - } - field(HIHI,DBF_DOUBLE) { - prompt("Hihi Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(LOLO,DBF_DOUBLE) { - prompt("Lolo Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(HIGH,DBF_DOUBLE) { - prompt("High Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(LOW,DBF_DOUBLE) { - prompt("Low Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - } - field(HHSV,DBF_MENU) { - prompt("Hihi Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(LLSV,DBF_MENU) { - prompt("Lolo Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(HSV,DBF_MENU) { - prompt("High Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(LSV,DBF_MENU) { - prompt("Low Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - prop(YES) - menu(menuAlarmSevr) - } - field(HYST,DBF_DOUBLE) { - prompt("Alarm Deadband") - promptgroup("70 - Alarm") - interest(1) - } - field(ADEL,DBF_DOUBLE) { - prompt("Archive Deadband") - promptgroup("80 - Display") - interest(1) - } - field(MDEL,DBF_DOUBLE) { - prompt("Monitor Deadband") - promptgroup("80 - Display") - interest(1) - } - field(LALM,DBF_DOUBLE) { - prompt("Last Value Alarmed") - special(SPC_NOMOD) - interest(3) - } - field(ALST,DBF_DOUBLE) { - prompt("Last Value Archived") - special(SPC_NOMOD) - interest(3) - } - field(MLST,DBF_DOUBLE) { - prompt("Last Val Monitored") - special(SPC_NOMOD) - interest(3) - } -} diff --git a/modules/database/src/std/rec/dfanoutRecord.dbd.pod b/modules/database/src/std/rec/dfanoutRecord.dbd.pod new file mode 100644 index 000000000..0d0487666 --- /dev/null +++ b/modules/database/src/std/rec/dfanoutRecord.dbd.pod @@ -0,0 +1,454 @@ +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE 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") + promptgroup("40 - Input") + asl(ASL0) + pp(TRUE) + } + field(SELM,DBF_MENU) { + prompt("Select Mechanism") + promptgroup("30 - Action") + interest(1) + menu(dfanoutSELM) + } + field(SELN,DBF_USHORT) { + prompt("Link Selection") + interest(1) + initial("1") + } + field(SELL,DBF_INLINK) { + prompt("Link Selection Loc") + promptgroup("30 - Action") + interest(1) + } + field(OUTA,DBF_OUTLINK) { + prompt("Output Spec A") + promptgroup("50 - Output") + interest(1) + } + field(OUTB,DBF_OUTLINK) { + prompt("Output Spec B") + promptgroup("50 - Output") + interest(1) + } + field(OUTC,DBF_OUTLINK) { + prompt("Output Spec C") + promptgroup("50 - Output") + interest(1) + } + field(OUTD,DBF_OUTLINK) { + prompt("Output Spec D") + promptgroup("50 - Output") + interest(1) + } + field(OUTE,DBF_OUTLINK) { + prompt("Output Spec E") + promptgroup("50 - Output") + interest(1) + } + field(OUTF,DBF_OUTLINK) { + prompt("Output Spec F") + promptgroup("50 - Output") + interest(1) + } + field(OUTG,DBF_OUTLINK) { + prompt("Output Spec G") + promptgroup("50 - Output") + interest(1) + } + field(OUTH,DBF_OUTLINK) { + prompt("Output Spec H") + promptgroup("50 - Output") + interest(1) + } + field(DOL,DBF_INLINK) { + prompt("Desired Output Loc") + promptgroup("40 - Input") + interest(1) + } + field(OMSL,DBF_MENU) { + prompt("Output Mode Select") + promptgroup("50 - Output") + interest(1) + menu(menuOmsl) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units") + promptgroup("80 - Display") + interest(1) + size(16) + prop(YES) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(HOPR,DBF_DOUBLE) { + prompt("High Operating Range") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(LOPR,DBF_DOUBLE) { + prompt("Low Operating Range") + promptgroup("80 - Display") + interest(1) + prop(YES) + } + field(HIHI,DBF_DOUBLE) { + prompt("Hihi Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(LOLO,DBF_DOUBLE) { + prompt("Lolo Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(HIGH,DBF_DOUBLE) { + prompt("High Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(LOW,DBF_DOUBLE) { + prompt("Low Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + } + field(HHSV,DBF_MENU) { + prompt("Hihi Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(LLSV,DBF_MENU) { + prompt("Lolo Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(HSV,DBF_MENU) { + prompt("High Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(LSV,DBF_MENU) { + prompt("Low Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + prop(YES) + menu(menuAlarmSevr) + } + field(HYST,DBF_DOUBLE) { + prompt("Alarm Deadband") + promptgroup("70 - Alarm") + interest(1) + } + field(ADEL,DBF_DOUBLE) { + prompt("Archive Deadband") + promptgroup("80 - Display") + interest(1) + } + field(MDEL,DBF_DOUBLE) { + prompt("Monitor Deadband") + promptgroup("80 - Display") + interest(1) + } + field(LALM,DBF_DOUBLE) { + prompt("Last Value Alarmed") + special(SPC_NOMOD) + interest(3) + } + field(ALST,DBF_DOUBLE) { + prompt("Last Value Archived") + special(SPC_NOMOD) + interest(3) + } + field(MLST,DBF_DOUBLE) { + prompt("Last Val Monitored") + 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 +} diff --git a/modules/database/src/tools/DBD/Parser.pm b/modules/database/src/tools/DBD/Parser.pm index 6065486c8..4b845b0d1 100644 --- a/modules/database/src/tools/DBD/Parser.pm +++ b/modules/database/src/tools/DBD/Parser.pm @@ -97,7 +97,7 @@ sub ParseDBD { my $rtyp = $dbd->recordtype($record_type); if (!defined $rtyp) { $rtyp = DBD::Recordtype->new($record_type); - warn "Device using undefined record type '$record_type', place-holder created\n"; + warn "Device using unknown record type '$record_type', declaration created\n"; $dbd->add($rtyp); } $rtyp->add_device(DBD::Device->new($link_type, $dset, $choice)); @@ -218,7 +218,12 @@ sub parse_breaktable { sub parse_recordtype { my ($dbd, $record_type) = @_; pushContext("recordtype($record_type)"); - my $rtyp = DBD::Recordtype->new($record_type); + # Re-use a matching declaration record type if one exists + my $rtyp = $dbd->recordtype($record_type); + if (!defined($rtyp) || $rtyp->fields) { + # Earlier record type is not a declaration, don't re-use it + $rtyp = DBD::Recordtype->new($record_type); + } while(1) { parseCommon($rtyp); if (m/\G field \s* \( \s* $RXstr \s* , \s* $RXstr \s* \) \s* \{/xgc) { diff --git a/modules/database/src/tools/DBD/Recfield.pm b/modules/database/src/tools/DBD/Recfield.pm index f7bfbae46..3e109d2d1 100644 --- a/modules/database/src/tools/DBD/Recfield.pm +++ b/modules/database/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/modules/database/src/tools/DBD/Recordtype.pm b/modules/database/src/tools/DBD/Recordtype.pm index e4b0d5481..649c6a21b 100644 --- a/modules/database/src/tools/DBD/Recordtype.pm +++ b/modules/database/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/modules/database/src/tools/dbdToHtml.pl b/modules/database/src/tools/dbdToHtml.pl index c51c793b5..e9711f757 100644 --- a/modules/database/src/tools/dbdToHtml.pl +++ b/modules/database/src/tools/dbdToHtml.pl @@ -127,6 +127,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 { @@ -156,22 +183,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'); @@ -249,7 +260,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 .= ''; @@ -377,6 +388,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 diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile index 6b468e106..185c2c0c1 100644 --- a/modules/database/test/std/rec/Makefile +++ b/modules/database/test/std/rec/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 TARGETS += $(COMMON_DIR)/simmTest.dbd diff --git a/modules/database/test/std/rec/badCaLink.db b/modules/database/test/std/rec/badCaLink.db new file mode 100644 index 000000000..c71bc9d2f --- /dev/null +++ b/modules/database/test/std/rec/badCaLink.db @@ -0,0 +1,4 @@ +record(ai, "ai:disconn") { + field(INP , "invalid CA") + field(UDF , "0") +} diff --git a/modules/database/test/std/rec/regressTest.c b/modules/database/test/std/rec/regressTest.c index 15010cebf..661463984 100644 --- a/modules/database/test/std/rec/regressTest.c +++ b/modules/database/test/std/rec/regressTest.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -123,12 +124,26 @@ 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); + testdbGetFieldEqual("ai:disconn.SEVR", DBF_LONG, INVALID_ALARM); + testdbGetFieldEqual("ai:disconn.STAT", DBF_LONG, LINK_ALARM); +} + MAIN(regressTest) { - testPlan(31); + testPlan(34); testArrayLength1(); testHexConstantLinks(); testLinkMS(); + testCADisconn(); return testDone(); } diff --git a/modules/database/test/tools/Recordtype.plt b/modules/database/test/tools/Recordtype.plt index ec189a92c..d9580b62b 100644 --- a/modules/database/test/tools/Recordtype.plt +++ b/modules/database/test/tools/Recordtype.plt @@ -2,7 +2,7 @@ use lib '@TOP@/lib/perl'; -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'; diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp index c8532d8fc..0de90c87a 100644 --- a/modules/libcom/src/iocsh/iocsh.cpp +++ b/modules/libcom/src/iocsh/iocsh.cpp @@ -601,12 +601,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 @@ -681,15 +681,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') @@ -838,16 +838,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; @@ -892,7 +890,7 @@ iocshBody (const char *pathname, const char *commandLine, const char *macros) errlogFlush(); if (readlineContext) epicsReadlineEnd(readlineContext); - epicsThreadSetOkToBlock( wasOkToBlock); + epicsThreadSetOkToBlock(wasOkToBlock); return 0; } diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c index 0d8c5678c..737d7e9c2 100644 --- a/modules/libcom/src/iocsh/libComRegister.c +++ b/modules/libcom/src/iocsh/libComRegister.c @@ -236,6 +236,7 @@ static const iocshFuncDef errlogFuncDef = {"errlog",1,errlogArgs}; static void errlogCallFunc(const iocshArgBuf *args) { errlog(args[0].sval); + errlogFlush(); } /* iocLogPrefix */ diff --git a/modules/libcom/src/osi/epicsGeneralTime.c b/modules/libcom/src/osi/epicsGeneralTime.c index 3054175ef..d08e9c6a9 100644 --- a/modules/libcom/src/osi/epicsGeneralTime.c +++ b/modules/libcom/src/osi/epicsGeneralTime.c @@ -225,14 +225,14 @@ static int generalTimeGetEventPriority(epicsTimeStamp *pDest, int eventNumber, gtProvider *ptp; int status = S_time_noProvider; epicsTimeStamp ts; + STATIC_ASSERT ( epicsTimeEventBestTime == -1 ); generalTime_Init(); IFDEBUG(2) printf("generalTimeGetEventPriority(eventNum=%d)\n", eventNumber); - if ((eventNumber < 0 || eventNumber >= NUM_TIME_EVENTS) && - (eventNumber != epicsTimeEventBestTime)) + 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; diff --git a/modules/libcom/src/osi/epicsGeneralTime.h b/modules/libcom/src/osi/epicsGeneralTime.h index 3adc23705..6bbb0b21b 100644 --- a/modules/libcom/src/osi/epicsGeneralTime.h +++ b/modules/libcom/src/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); diff --git a/modules/libcom/src/ring/epicsRingBytes.c b/modules/libcom/src/ring/epicsRingBytes.c index cb7e52e83..ab048e467 100644 --- a/modules/libcom/src/ring/epicsRingBytes.c +++ b/modules/libcom/src/ring/epicsRingBytes.c @@ -38,6 +38,7 @@ typedef struct ringPvt { volatile int nextPut; volatile int nextGet; int size; + int highWaterMark; volatile char buffer[1]; /* actually larger */ }ringPvt; @@ -47,6 +48,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; @@ -118,7 +120,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; @@ -131,8 +133,9 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( if (pring->lock) epicsSpinUnlock(pring->lock); return 0; } - if (nbytes) + if (nbytes) { memcpy ((void *)&pring->buffer[nextPut], value, nbytes); + } nextPut += nbytes; } else { @@ -143,8 +146,9 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( } topCount = size - nextPut; copyCount = (nbytes > topCount) ? topCount : nbytes; - if (copyCount) + if (copyCount) { memcpy ((void *)&pring->buffer[nextPut], value, copyCount); + } nextPut += copyCount; if (nextPut == size) { int nLeft = nbytes - copyCount; @@ -155,6 +159,10 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( } pring->nextPut = nextPut; + used = nextPut - nextGet; + if (used < 0) used += pring->size; + if (used > pring->highWaterMark) pring->highWaterMark = used; + if (pring->lock) epicsSpinUnlock(pring->lock); return nbytes; } @@ -224,3 +232,20 @@ epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id) { return (epicsRingBytesFreeBytes(id) <= 0); } + +epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id) +{ + ringPvt *pring = (ringPvt *)id; + return pring->highWaterMark; +} + +epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id) +{ + ringPvt *pring = (ringPvt *)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/modules/libcom/src/ring/epicsRingBytes.h b/modules/libcom/src/ring/epicsRingBytes.h index 011829bfe..3dc0081ad 100644 --- a/modules/libcom/src/ring/epicsRingBytes.h +++ b/modules/libcom/src/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/modules/libcom/src/ring/epicsRingPointer.cpp b/modules/libcom/src/ring/epicsRingPointer.cpp index 9c144cec1..709ab6509 100644 --- a/modules/libcom/src/ring/epicsRingPointer.cpp +++ b/modules/libcom/src/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/modules/libcom/src/ring/epicsRingPointer.h b/modules/libcom/src/ring/epicsRingPointer.h index 48d62036d..68bf8f5a6 100644 --- a/modules/libcom/src/ring/epicsRingPointer.h +++ b/modules/libcom/src/ring/epicsRingPointer.h @@ -40,18 +40,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 +63,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 +79,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 +102,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 +129,8 @@ inline bool epicsRingPointer::push(T *p) } buffer[next] = p; nextPush = newNext; + int used = getUsedNoLock(); + if (used > highWaterMark) highWaterMark = used; if (lock) epicsSpinUnlock(lock); return(true); } @@ -161,12 +171,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 +213,20 @@ inline bool epicsRingPointer::isFull() const return((count == 0) || (count == size)); } +template +inline int epicsRingPointer::getHighWaterMark() const +{ + return highWaterMark; +} + +template +inline void epicsRingPointer::resetHighWaterMark() +{ + if (lock) epicsSpinLock(lock); + highWaterMark = getUsedNoLock(); + if (lock) epicsSpinUnlock(lock); +} + #endif /* __cplusplus */ #endif /* INCepicsRingPointerh */ diff --git a/modules/libcom/test/ringBytesTest.c b/modules/libcom/test/ringBytesTest.c index 6cef93334..bb91d0201 100644 --- a/modules/libcom/test/ringBytesTest.c +++ b/modules/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/modules/libcom/test/ringPointerTest.c b/modules/libcom/test/ringPointerTest.c index 65a349489..d351708b5 100644 --- a/modules/libcom/test/ringPointerTest.c +++ b/modules/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); 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