diff --git a/.gitignore b/.gitignore index 412314940..529025933 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# Before adding patterns here, please read the gitignore +# documentation at https://git-scm.com/docs/gitignore /cfg/ /bin/ /lib/ @@ -11,14 +13,13 @@ /modules/RELEASE.*.local /modules/Makefile.local O.*/ -/QtC-* -/.qtc_* -/.vscode/ -*.orig *.log -.*.swp -.DS_Store .iocsh_history + +# Common files generated by other tools +.DS_Store + +# Files created by rpmbuild RPMS SRPMS BUILDROOT diff --git a/README b/README index 942d00cb3..da2492989 100644 --- a/README +++ b/README @@ -13,15 +13,7 @@ this distribution. --------------------------------------------------------- -Installation and release information can be found in the -various files in the documentation subdirectory. - -Additional information about EPICS including mailing list -archives and subscription instructions, documentation and -training materials, additional components, links to other -websites etc. is available on the EPICS home page at - https://epics.anl.gov/ +For more information, see the README.md file. $Format:%cD$ $Format:%H$ -https://code.launchpad.net/epics-base diff --git a/README.md b/README.md new file mode 100644 index 000000000..66c719c93 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ + +# EPICS Base + +EPICS (Experimental Physics and Industrial Control System) is a set of software +tools and applications which provide a software infrastructure for use in +building distributed control systems to operate devices such as Particle +Accelerators, Large Experiments and major Telescopes. EPICS Base is the central +core of the control system toolkit. More details can be found at the +[About page of the official website](https://epics-controls.org/about-epics/) + +## Links + +- [Official Website](https://epics-controls.org/) +- [Original Website](https://epics.anl.gov/) +- [Repository](https://github.com/epics-base/epics-base) + +### Documentation + +- [Documentation](https://docs.epics-controls.org/en/latest/) +- [Documentation Repository](https://github.com/epics-docs/epics-docs) + +### Community Communication + +- [Tech-Talk Mailing List](https://epics.anl.gov/tech-talk/) +- [Matrix Rooms](https://matrix.to/#/#epics:epics-controls.org) +- [News](https://epics-controls.org/news-and-events/) + +## Quick Install + +Download a release from the +[Downloads page](https://epics-controls.org/resources-and-support/base/downloads) +and unpack it. Inside the unpacked folder run: + +```bash +make +``` + +For more information on how to install on your system see the +[Installation page](https://docs.epics-controls.org/en/latest/getting-started/installation.html) +of the documentation. + +### Quick run a softIOC + +After building, you can run an example soft-IOC (Input/Output Controller) +which runs a Channel Access server. + +```bash +./bin/*/softIoc -x first +``` + +Run the `dbl` command to list the records it provides: + +```bash +epics> dbl +first:BaseVersion +first:exit +epics> +``` + +## License + +EPICS Base is distributed subject to a Software License +Agreement found in the file [LICENSE](./LICENSE) that is included with +this distribution. diff --git a/configure/CONFIG b/configure/CONFIG index e72537db1..edf8d4f73 100644 --- a/configure/CONFIG +++ b/configure/CONFIG @@ -50,6 +50,9 @@ endif include $(CONFIG)/CONFIG_COMMON include $(CONFIG)/CONFIG_FILE_TYPE +# Fetch the module-specific CFGS before overwriting INSTALL_LOCATION in order to load them later +TOP_CFG_CONFIGS := $(wildcard $(INSTALL_CFG)/CONFIG*) + # Base-specific build options # include $(CONFIG)/CONFIG_BASE @@ -117,7 +120,6 @@ endif # Include $(INSTALL_CFG)/CONFIG* definitions # -TOP_CFG_CONFIGS = $(wildcard $(INSTALL_CFG)/CONFIG*) ifneq ($(TOP_CFG_CONFIGS),) include $(TOP_CFG_CONFIGS) endif diff --git a/configure/os/CONFIG.Common.RTEMS-beagleboneblack b/configure/os/CONFIG.Common.RTEMS-beagleboneblack index 4b6107b05..edda91b9d 100644 --- a/configure/os/CONFIG.Common.RTEMS-beagleboneblack +++ b/configure/os/CONFIG.Common.RTEMS-beagleboneblack @@ -9,7 +9,7 @@ RTEMS_BSP = beagleboneblack RTEMS_TARGET_CPU = arm GNU_TARGET = arm-rtems -OP_SYS_LDLIBS += -Wl,--gc-sections +OP_SYS_LDFLAGS += -Wl,--gc-sections ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/beagleboneblack/lib/ include $(CONFIG)/os/CONFIG.Common.RTEMS diff --git a/configure/os/CONFIG.Common.RTEMS-pc686 b/configure/os/CONFIG.Common.RTEMS-pc686 index bfe4c26e4..5d30b17d8 100644 --- a/configure/os/CONFIG.Common.RTEMS-pc686 +++ b/configure/os/CONFIG.Common.RTEMS-pc686 @@ -20,7 +20,6 @@ define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< $@ endef -OP_SYS_LDLIBS += -Wl,--gc-sections ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/pc686/lib/ include $(CONFIG)/os/CONFIG.Common.RTEMS @@ -28,7 +27,7 @@ include $(CONFIG)/os/CONFIG.Common.RTEMS # # Put text segment where it will work with etherboot # -OP_SYS_LDFLAGS += -Wl,-Ttext,0x100000 +OP_SYS_LDFLAGS += -Wl,-Ttext,0x100000 -Wl,--gc-sections # This check must appear after the above include diff --git a/configure/os/CONFIG.Common.RTEMS-qoriq_e500 b/configure/os/CONFIG.Common.RTEMS-qoriq_e500 index 94c7e825e..234c2e4f4 100644 --- a/configure/os/CONFIG.Common.RTEMS-qoriq_e500 +++ b/configure/os/CONFIG.Common.RTEMS-qoriq_e500 @@ -20,7 +20,7 @@ ARCH_DEP_CFLAGS += -DRTEMS_HAS_ALTIVEC #ARCH_DEP_CFLAGS += -I$(RTEMS_BASE)/powerpc-rtems5/qoriq_e500/lib/include #OP_SYS_LDLIBS += -lbspExt #does not use posix stuff ... want to ignore -OP_SYS_LDLIBS += -Wl,--gc-sections +OP_SYS_LDFLAGS += -Wl,--gc-sections #ARCH_DEP_LDFLAGS = -mcpu=8540 -meabi -msdata=sysv -mstrict-align -mspe -mabi=spe -mfloat-gprs=double ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/$(RTEMS_BSP)/lib diff --git a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu index 83b518b69..51cd286ae 100644 --- a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu +++ b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_a9_qemu @@ -12,7 +12,7 @@ GNU_TARGET = arm-rtems #use dhcp/bootp ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL -OP_SYS_LDLIBS += -Wl,--gc-sections +OP_SYS_LDFLAGS += -Wl,--gc-sections ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/xilinx_zynq_a9_qemu/lib/ diff --git a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard index bac33eb91..f745b15d6 100644 --- a/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard +++ b/configure/os/CONFIG.Common.RTEMS-xilinx_zynq_zedboard @@ -9,7 +9,7 @@ RTEMS_BSP = xilinx_zynq_zedboard RTEMS_TARGET_CPU = arm GNU_TARGET = arm-rtems -OP_SYS_LDLIBS += -Wl,--gc-sections +OP_SYS_LDFLAGS += -Wl,--gc-sections ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/xilinx_zynq_zedboard/lib/ diff --git a/documentation/new-notes/PR-651.md b/documentation/new-notes/PR-651.md new file mode 100644 index 000000000..019dc07d7 --- /dev/null +++ b/documentation/new-notes/PR-651.md @@ -0,0 +1,12 @@ +### Documentation Updates + +The reference documentation for the [event](eventRecord.html) record type +has been updated to cover the use of named events which were added in Base +3.14.12.3 and 3.15.1. + +Documentation for CALC expression evaluation has been updated for format +enhancements and to add some missing operators. +The best documentation for these expressions can be found in the +[postfix.h](postfix_h.html) header in libCom, but both the +[calc](calcRecord.html) and [calcout](calcoutRecord.html) record reference +pages also cover the infix expressions supported. diff --git a/documentation/new-notes/PR-655.md b/documentation/new-notes/PR-655.md index d8bfe07f5..254ba1356 100644 --- a/documentation/new-notes/PR-655.md +++ b/documentation/new-notes/PR-655.md @@ -1,7 +1,7 @@ ### Records calc, calcout and sub extended -The record types calc, calcout and sub have been extended from -12 inputs A-L to 21 inputs A-U. +The record types calc, calcout and sub have been extended from 12 inputs +A - L to 21 inputs A - U. The macro `CALCPERFORM_NARGS` reflects this change. The new inputs can be used in calc links and access security as well. -The size of CALC and OCAL fields have been doubled to 160 chars. +The size of CALC and OCAL fields has been doubled to 160 chars. diff --git a/documentation/new-notes/PR-688.md b/documentation/new-notes/PR-688.md new file mode 100644 index 000000000..f36ef1de8 --- /dev/null +++ b/documentation/new-notes/PR-688.md @@ -0,0 +1,6 @@ +### `dfanout` improvements + +The dfanout record now has invalid output handling with the usual fields +`IVOA` and `IVOV` just like other output records. + +The number of output links has been increased from 8 to 16. diff --git a/modules/ca/src/client/cac.h b/modules/ca/src/client/cac.h index 79dcbaa9b..8fcb7c563 100644 --- a/modules/ca/src/client/cac.h +++ b/modules/ca/src/client/cac.h @@ -203,6 +203,7 @@ public: void destroyIIU ( tcpiiu & iiu ); const char * pLocalHostName (); + const resTable < tcpiiu, caServerID > & getServerTable(); private: epicsSingleton < localHostName > :: reference _refLocalHostName; @@ -424,4 +425,10 @@ inline double cac :: return this->connTMO; } +inline const resTable < tcpiiu, caServerID > & cac :: + getServerTable() +{ + return this->serverTable; +} + #endif // ifndef INC_cac_H diff --git a/modules/ca/src/client/cacChannel.cpp b/modules/ca/src/client/cacChannel.cpp index 33345967c..a76d6e957 100644 --- a/modules/ca/src/client/cacChannel.cpp +++ b/modules/ca/src/client/cacChannel.cpp @@ -129,6 +129,13 @@ unsigned cacChannel::getHostName ( return 0u; } +unsigned cacChannel::getHostMinorProtocol ( + epicsGuard < epicsMutex > &) const throw () +{ + epicsThreadOnce ( & cacChannelIdOnce, cacChannelSetup, 0); + return 0u; +} + // the default is to assume that it is a locally hosted channel const char * cacChannel::pHostName ( epicsGuard < epicsMutex > & ) const throw () diff --git a/modules/ca/src/client/cacIO.h b/modules/ca/src/client/cacIO.h index 760d8dd72..777155047 100644 --- a/modules/ca/src/client/cacIO.h +++ b/modules/ca/src/client/cacIO.h @@ -246,7 +246,8 @@ public: // !! deprecated, avoid use !! virtual const char * pHostName ( epicsGuard < epicsMutex > & guard ) const throw (); - + virtual unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > &) const throw () ; // exceptions class badString {}; class badType {}; diff --git a/modules/ca/src/client/cadef.h b/modules/ca/src/client/cadef.h index e7ab45cbe..5c60a06f7 100644 --- a/modules/ca/src/client/cadef.h +++ b/modules/ca/src/client/cadef.h @@ -1460,6 +1460,17 @@ LIBCA_API const char * epicsStdCall ca_host_name (chid channel); LIBCA_API unsigned epicsStdCall ca_get_host_name ( chid pChan, char *pBuf, unsigned bufLength ); +/** \brief Return the minor protocol version number used by the host to + * which a channel is cuurently connected. + * + * \param[in] pChan channel identifier + * \returns The minor protocol version number. + * If the channel is disconnected CA_UKN_MINOR_VERSION is returned. + */ +LIBCA_API unsigned epicsStdCall ca_host_minor_protocol (chid pChan); + +#define HAS_CA_HOST_MINOR_PROTOCOL + /** \brief Call their function with their argument whenever * a new fd is added or removed. * diff --git a/modules/ca/src/client/nciu.cpp b/modules/ca/src/client/nciu.cpp index a862cb228..fc5d3cb4c 100644 --- a/modules/ca/src/client/nciu.cpp +++ b/modules/ca/src/client/nciu.cpp @@ -410,6 +410,13 @@ const char * nciu::pHostName ( return this->piiu->pHostName ( guard ); } +unsigned nciu::getHostMinorProtocol ( + epicsGuard < epicsMutex > & guard) const throw () +{ + return this->piiu->getHostMinorProtocol ( + guard ); +} + bool nciu::ca_v42_ok ( epicsGuard < epicsMutex > & guard ) const { diff --git a/modules/ca/src/client/nciu.h b/modules/ca/src/client/nciu.h index d909aa917..7e94fe1b8 100644 --- a/modules/ca/src/client/nciu.h +++ b/modules/ca/src/client/nciu.h @@ -183,6 +183,8 @@ public: unsigned getHostName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw (); + unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > &) const throw (); void writeException ( epicsGuard < epicsMutex > &, epicsGuard < epicsMutex > &, int status, const char *pContext, unsigned type, arrayElementCount count ); diff --git a/modules/ca/src/client/netiiu.cpp b/modules/ca/src/client/netiiu.cpp index c73732af6..04a5fb524 100644 --- a/modules/ca/src/client/netiiu.cpp +++ b/modules/ca/src/client/netiiu.cpp @@ -116,6 +116,12 @@ const char * netiiu::pHostName ( return pHostNameNetIIU; } +unsigned netiiu::getHostMinorProtocol ( + epicsGuard < epicsMutex > & ) const throw () +{ + return CA_UKN_MINOR_VERSION; +} + osiSockAddr netiiu::getNetworkAddress ( epicsGuard < epicsMutex > & ) const { diff --git a/modules/ca/src/client/netiiu.h b/modules/ca/src/client/netiiu.h index 298337bd3..d21ed8428 100644 --- a/modules/ca/src/client/netiiu.h +++ b/modules/ca/src/client/netiiu.h @@ -43,6 +43,8 @@ public: unsigned bufLength ) const throw () = 0; virtual const char * pHostName ( epicsGuard < epicsMutex > & ) const throw () = 0; + virtual unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > & ) const throw (); virtual bool ca_v41_ok ( epicsGuard < epicsMutex > & ) const = 0; virtual bool ca_v42_ok ( diff --git a/modules/ca/src/client/oldAccess.h b/modules/ca/src/client/oldAccess.h index 2c39d2768..3e8edb914 100644 --- a/modules/ca/src/client/oldAccess.h +++ b/modules/ca/src/client/oldAccess.h @@ -64,6 +64,8 @@ public: chid pChan, char * pBuf, unsigned bufLength ); friend const char * epicsStdCall ca_host_name ( chid pChan ); + friend unsigned epicsStdCall ca_host_minor_protocol ( + chid pChan ); friend const char * epicsStdCall ca_name ( chid pChan ); friend void epicsStdCall ca_set_puser ( diff --git a/modules/ca/src/client/oldChannelNotify.cpp b/modules/ca/src/client/oldChannelNotify.cpp index 71688c94c..0a3512827 100644 --- a/modules/ca/src/client/oldChannelNotify.cpp +++ b/modules/ca/src/client/oldChannelNotify.cpp @@ -193,6 +193,16 @@ const char * epicsStdCall ca_host_name ( return pChan->io.pHostName ( guard ); } +/* + * ca_host_minorProtocol () + */ +unsigned epicsStdCall ca_host_minor_protocol ( + chid pChan ) +{ + epicsGuard < epicsMutex > guard ( pChan->cacCtx.mutexRef () ); + return pChan->io.getHostMinorProtocol( guard ); +} + /* * ca_set_puser () */ diff --git a/modules/ca/src/client/tcpiiu.cpp b/modules/ca/src/client/tcpiiu.cpp index 30f6fad17..b8ebaac32 100644 --- a/modules/ca/src/client/tcpiiu.cpp +++ b/modules/ca/src/client/tcpiiu.cpp @@ -1804,6 +1804,13 @@ const char * tcpiiu::pHostName ( return this->hostNameCacheInstance.pointer (); } +unsigned tcpiiu::getHostMinorProtocol ( + epicsGuard < epicsMutex > & guard) const throw () +{ + guard.assertIdenticalMutex ( this->mutex ); + return this->minorProtocolVersion; +} + void tcpiiu::disconnectAllChannels ( epicsGuard < epicsMutex > & cbGuard, epicsGuard < epicsMutex > & guard, diff --git a/modules/ca/src/client/udpiiu.cpp b/modules/ca/src/client/udpiiu.cpp index d36e2c5b4..bfe3d628e 100644 --- a/modules/ca/src/client/udpiiu.cpp +++ b/modules/ca/src/client/udpiiu.cpp @@ -1342,6 +1342,12 @@ const char * udpiiu::pHostName ( return netiiu::pHostName ( cacGuard ); } +unsigned udpiiu::getHostMinorProtocol ( + epicsGuard < epicsMutex > & cacGuard ) const throw () +{ + return netiiu::getHostMinorProtocol ( cacGuard ); +} + bool udpiiu::ca_v42_ok ( epicsGuard < epicsMutex > & cacGuard ) const { diff --git a/modules/ca/src/client/udpiiu.h b/modules/ca/src/client/udpiiu.h index aba79be5d..abc3fa757 100644 --- a/modules/ca/src/client/udpiiu.h +++ b/modules/ca/src/client/udpiiu.h @@ -239,7 +239,9 @@ private: unsigned bufLength ) const throw (); const char * pHostName ( epicsGuard < epicsMutex > & ) const throw (); - bool ca_v41_ok ( + unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > & ) const throw (); + bool ca_v41_ok ( epicsGuard < epicsMutex > & ) const; bool ca_v42_ok ( epicsGuard < epicsMutex > & ) const; diff --git a/modules/ca/src/client/virtualCircuit.h b/modules/ca/src/client/virtualCircuit.h index 42af12f5f..0f43ea95e 100644 --- a/modules/ca/src/client/virtualCircuit.h +++ b/modules/ca/src/client/virtualCircuit.h @@ -168,6 +168,8 @@ public: unsigned getHostName ( epicsGuard < epicsMutex > &, char *pBuf, unsigned bufLength ) const throw (); + unsigned getHostMinorProtocol ( + epicsGuard < epicsMutex > &) const throw (); bool alive ( epicsGuard < epicsMutex > & ) const; bool connecting ( diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index bb0724e29..53498d0f5 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -805,7 +805,7 @@ int dbLoadRecords(const char* file, const char* subs) if(dbLoadRecordsHook) dbLoadRecordsHook(file, subs); } else { - fprintf(stderr, ERL_ERROR " failed to load '%s'\n", file); + fprintf(stderr, ERL_ERROR ": Failed to load '%s'\n", file); if(status==-2) fprintf(stderr, " Records cannot be loaded after iocInit!\n"); } @@ -1095,7 +1095,7 @@ static long dbPutFieldLink(DBADDR *paddr, return S_db_badDbrtype; } - status = dbParseLink(pstring, pfldDes->field_type, &link_info); + status = dbParseLink(pstring, pfldDes->field_type, &link_info, precord->name, pfldDes->name); if (status) return status; diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c index c910d4692..a60f2572b 100644 --- a/modules/database/src/ioc/db/dbEvent.c +++ b/modules/database/src/ioc/db/dbEvent.c @@ -557,6 +557,40 @@ static void event_remove ( struct event_que *ev_que, pevent->npend--; } +/* synchronize with worker thread. + * + * On return, any previously pending events or extra labor have been handled. + * + * caller must lock evUser->lock + */ +static +void db_sync_event (struct event_user * const evUser) +{ + /* grab current cycle counter, then wait for it to change */ + epicsUInt32 curSeq = evUser->pflush_seq; + event_waiter wait; + wait.wake = epicsEventCreate(epicsEventEmpty); /* failure allowed */ + + ellAdd(&evUser->waiters, &wait.node); + do { + epicsMutexUnlock( evUser->lock ); + /* ensure worker will cycle at least once */ + epicsEventMustTrigger(evUser->ppendsem); + + if(wait.wake) { + epicsEventMustWait(wait.wake); + } else { + epicsThreadSleep(0.01); /* ick. but better than cantProceed() */ + } + + epicsMutexMustLock ( evUser->lock ); + } while(curSeq == evUser->pflush_seq); + ellDelete(&evUser->waiters, &wait.node); + /* destroy under lock to ensure epicsEventMustTrigger() has returned */ + if(wait.wake) + epicsEventDestroy(wait.wake); +} + /* * DB_CANCEL_EVENT() * @@ -594,34 +628,9 @@ void db_cancel_event (dbEventSubscription event) UNLOCKEVQUE (que); if(sync) { - /* cycle through worker */ - struct event_user *evUser = que->evUser; - epicsUInt32 curSeq; - event_waiter wait; - wait.wake = epicsEventCreate(epicsEventEmpty); /* may fail */ - - epicsMutexMustLock ( evUser->lock ); - ellAdd(&evUser->waiters, &wait.node); - /* grab current cycle counter, then wait for it to change */ - curSeq = evUser->pflush_seq; - do { - epicsMutexUnlock( evUser->lock ); - /* ensure worker will cycle at least once */ - epicsEventMustTrigger(evUser->ppendsem); - - if(wait.wake) { - epicsEventMustWait(wait.wake); - } else { - epicsThreadSleep(0.01); /* ick. but better than cantProceed() */ - } - - epicsMutexMustLock ( evUser->lock ); - } while(curSeq == evUser->pflush_seq); - ellDelete(&evUser->waiters, &wait.node); - /* destroy under lock to ensure epicsEventMustTrigger() has returned */ - if(wait.wake) - epicsEventDestroy(wait.wake); - epicsMutexUnlock( evUser->lock ); + epicsMutexMustLock ( que->evUser->lock ); + db_sync_event(que->evUser); + epicsMutexUnlock( que->evUser->lock ); } } @@ -635,10 +644,10 @@ void db_flush_extra_labor_event (dbEventCtx ctx) struct event_user * const evUser = (struct event_user *) ctx; epicsMutexMustLock ( evUser->lock ); - while ( evUser->extraLaborBusy ) { - epicsMutexUnlock ( evUser->lock ); - epicsThreadSleep(0.1); - epicsMutexMustLock ( evUser->lock ); + if ( evUser->extraLaborBusy || (evUser->extra_labor && evUser->extralabor_sub) ) { + db_sync_event(evUser); + // At this point, original labor completed. + // Do not wait for any additional labor queued afterwards. } epicsMutexUnlock ( evUser->lock ); } @@ -1027,9 +1036,7 @@ static void event_task (void *pParm) * labor to this task */ epicsMutexMustLock ( evUser->lock ); - evUser->extraLaborBusy = TRUE; if ( evUser->extra_labor && evUser->extralabor_sub ) { - evUser->extra_labor = FALSE; pExtraLaborSub = evUser->extralabor_sub; pExtraLaborArg = evUser->extralabor_arg; } @@ -1037,12 +1044,14 @@ static void event_task (void *pParm) pExtraLaborSub = NULL; pExtraLaborArg = NULL; } + evUser->extra_labor = FALSE; if ( pExtraLaborSub ) { + evUser->extraLaborBusy = TRUE; epicsMutexUnlock ( evUser->lock ); (*pExtraLaborSub)(pExtraLaborArg); epicsMutexMustLock ( evUser->lock ); + evUser->extraLaborBusy = FALSE; } - evUser->extraLaborBusy = FALSE; for ( ev_que = &evUser->firstque; ev_que; ev_que = ev_que->nextque ) { /* unlock during iteration is safe as event_que will not be free'd */ diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c index 671763636..a11afee3a 100644 --- a/modules/database/src/ioc/db/dbUnitTest.c +++ b/modules/database/src/ioc/db/dbUnitTest.c @@ -35,6 +35,8 @@ #include "errSymTbl.h" #include "iocshRegisterCommon.h" +#define DBR_NAME(dbrType) (VALID_DB_REQ(dbrType) ? pamapdbfType[dbrType].strvalue+3 : "???") + static dbEventCtx testEvtCtx; static epicsMutexId testEvtLock; static ELLLIST testEvtList; /* holds testMonitor::node */ @@ -153,8 +155,8 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap) OP(DBR_ENUM, int, enum16); #undef OP default: - testFail("invalid DBR: dbPutField(\"%s\", %d, ...)", - dbChannelName(chan), dbrType); + testFail("invalid DBR: dbPutField(\"%s\", DBR%s, ...)", + dbChannelName(chan), DBR_NAME(dbrType)); ret = S_db_badDbrtype; break; } @@ -174,7 +176,8 @@ void testdbPutFieldOk(const char* pv, int dbrType, ...) ret = testdbVPutField(pv, dbrType, ap); va_end(ap); - testOk(ret==0, "dbPutField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, ret, errSymMsg(ret)); + testOk(ret==0, "dbPutField(\"%s\", DBR%s, ...) -> %#lx (%s)", + pv, DBR_NAME(dbrType), ret, errSymMsg(ret)); } void testdbPutFieldFail(long status, const char* pv, int dbrType, ...) @@ -186,8 +189,8 @@ void testdbPutFieldFail(long status, const char* pv, int dbrType, ...) ret = testdbVPutField(pv, dbrType, ap); va_end(ap); - testOk(ret==status, "dbPutField(\"%s\", %d, ...) -> %#lx (%s) == %#lx (%s)", - pv, dbrType, status, errSymMsg(status), ret, errSymMsg(ret)); + testOk(ret==status, "dbPutField(\"%s\", DBR%s, ...) -> %#lx (%s) == %#lx (%s)", + pv, DBR_NAME(dbrType), status, errSymMsg(status), ret, errSymMsg(ret)); } void testdbGetFieldEqual(const char* pv, int dbrType, ...) @@ -213,10 +216,12 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) status = dbChannelGetField(chan, dbrType, pod.bytes, NULL, &nReq, NULL); if (status) { - testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status)); + testFail("dbGetField(\"%s\", DBR%s, ...) -> %#lx (%s)", + pv, DBR_NAME(dbrType), status, errSymMsg(status)); goto done; } else if(nReq==0) { - testFail("dbGetField(\"%s\", %d, ...) -> zero length", pv, dbrType); + testFail("dbGetField(\"%s\", DBR%s, ...) -> zero length", + pv, DBR_NAME(dbrType)); goto done; } @@ -224,13 +229,14 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) case DBR_STRING: { const char *expect = va_arg(ap, char*); testOk(strcmp(expect, pod.valStr)==0, - "dbGetField(\"%s\", %d) -> \"%s\" == \"%s\"", - pv, dbrType, expect, pod.valStr); + "dbGetField(\"%s\", DBR%s) -> \"%s\" == \"%s\"", + pv, DBR_NAME(dbrType), expect, pod.valStr); break; } #define OP(DBR,Type,mem,pat) case DBR: {Type expect = va_arg(ap,Type); \ - testOk(expect==pod.val.mem, "dbGetField(\"%s\", %d) -> " pat " == " pat, \ - pv, dbrType, expect, (Type)pod.val.mem); break;} + testOk(expect==pod.val.mem||((expect!=expect)&&(pod.val.mem!=pod.val.mem)), \ + "dbGetField(\"%s\", DBR%s) -> " pat " == " pat, \ + pv, DBR_NAME(dbrType), expect, (Type)pod.val.mem); break;} OP(DBR_CHAR, int, int8, "%d"); OP(DBR_UCHAR, int, uInt8, "%d"); @@ -240,12 +246,12 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) OP(DBR_ULONG, unsigned int, uInt32, "%u"); OP(DBR_INT64, long long, int64, "%lld"); OP(DBR_UINT64, unsigned long long, uInt64, "%llu"); - OP(DBR_FLOAT, double, float32, "%e"); - OP(DBR_DOUBLE, double, float64, "%e"); OP(DBR_ENUM, int, enum16, "%d"); + OP(DBR_FLOAT, double, float32, "%g"); + OP(DBR_DOUBLE, double, float64, "%g"); #undef OP default: - testFail("dbGetField(\"%s\", %d) -> unsupported dbf", pv, dbrType); + testFail("dbGetField(\"%s\", DBR%s) -> unsupported dbf", pv, DBR_NAME(dbrType)); } done: @@ -265,7 +271,7 @@ void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, con status = dbChannelPutField(chan, dbrType, pbuf, count); - testOk(status==0, "dbPutField(\"%s\", dbr=%d, count=%lu, ...) -> %ld", pv, dbrType, count, status); + testOk(status==0, "dbPutField(\"%s\", DBR%s, count=%lu, ...) -> %ld", pv, DBR_NAME(dbrType), count, status); done: if(chan) @@ -294,7 +300,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign status = dbChannelGetField(chan, dbfType, gbuf, NULL, &nRequest, NULL); if (status) { - testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status); + testFail("dbGetField(\"%s\", DBR%s, ...) -> %#lx", pv, DBR_NAME(dbfType), status); } else { unsigned match = nRequest==cnt; @@ -327,14 +333,14 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign OP(DBR_ULONG, unsigned int, "%u"); OP(DBR_INT64, long long, "%lld"); OP(DBR_UINT64, unsigned long long, "%llu"); - OP(DBR_FLOAT, float, "%e"); - OP(DBR_DOUBLE, double, "%e"); + OP(DBR_FLOAT, float, "%g"); + OP(DBR_DOUBLE, double, "%g"); OP(DBR_ENUM, int, "%d"); #undef OP } } - testOk(match, "dbGetField(\"%s\", dbrType=%d, nRequest=%ld ...) match", pv, dbfType, nRequest); + testOk(match, "dbGetField(\"%s\", DBR%s, nRequest=%ld ...) match", pv, DBR_NAME(dbfType), nRequest); } done: @@ -428,6 +434,17 @@ void testMonitorWait(testMonitor *mon) } } +static void dummylabor(void* unused) {(void)unused;} + +void testMonitorSync(testMonitor *mon) +{ + // db_flush_extra_labor_event() only blocks if there is actual labor pending + (void)db_add_extra_labor_event(testEvtCtx, dummylabor, NULL); + (void)db_post_extra_labor(testEvtCtx); + db_flush_extra_labor_event(testEvtCtx); + (void)db_add_extra_labor_event(testEvtCtx, NULL, NULL); +} + unsigned testMonitorCount(testMonitor *mon, unsigned reset) { unsigned count; diff --git a/modules/database/src/ioc/db/dbUnitTest.h b/modules/database/src/ioc/db/dbUnitTest.h index f697b07fa..fbacb65aa 100644 --- a/modules/database/src/ioc/db/dbUnitTest.h +++ b/modules/database/src/ioc/db/dbUnitTest.h @@ -139,21 +139,45 @@ DBCORE_API dbCommon* testdbRecordPtr(const char* pv); typedef struct testMonitor testMonitor; -/** Setup monitoring the named PV for changes */ +/** Setup monitoring the named PV for changes + * + * @param[in] pvname Requested PV name. Must be valid for dbChannelCreate(). + * @param[in] dbe_mask A bitwise or of DBE_VALUE and friends. + * @param[in] opt Currently unused. Set to zero. + * @returns Newly allocated testMonitor object, which caller must testMonitorDestroy() + * + * Calls testAbort() on failure. Will never return NULL. + * + * @since 3.16.0.1 + */ DBCORE_API testMonitor* testMonitorCreate(const char* pvname, unsigned dbe_mask, unsigned opt); -/** Stop monitoring */ +/** Stop monitoring + * + * @since 3.16.0.1 + */ DBCORE_API void testMonitorDestroy(testMonitor*); /** Return immediately if it has been updated since create, last wait, * or reset (count w/ reset=1). * Otherwise, block until the value of the target PV is updated. + * + * @since 3.16.0.1 */ DBCORE_API void testMonitorWait(testMonitor*); +/** Synchronize with dbEvent working for subscription. + * + * On return, any updates previously posted for this subscriptions have been delivered. + * + * @since UNRELEASED + */ +DBCORE_API void testMonitorSync(testMonitor*); /** Return the number of monitor events which have occured since create, * or a previous reset (called reset=1). * Calling w/ reset=0 only returns the count. * Calling w/ reset=1 resets the count to zero and ensures that the next * wait will block unless subsequent events occur. Returns the previous * count. + * + * @since 3.16.0.1 */ DBCORE_API unsigned testMonitorCount(testMonitor*, unsigned reset); diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index bbd5bac93..7ea975986 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -34,6 +34,7 @@ #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "epicsExport.h" +#include "epicsAssert.h" #include "link.h" #include "special.h" #include "iocInit.h" @@ -237,6 +238,8 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, goto cleanup; } + errlogInit(0); /* Initialize the errSymTable */ + if(*ppdbbase == 0) *ppdbbase = dbAllocBase(); savedPdbbase = *ppdbbase; if(path && strlen(path)>0) { @@ -351,14 +354,18 @@ cleanup: return(status); } -long dbReadDatabase(DBBASE **ppdbbase,const char *filename, - const char *path,const char *substitutions) -{return (dbReadCOM(ppdbbase,filename,0,path,substitutions));} +long dbReadDatabase(DBBASE **ppdbbase, const char *filename, + const char *path, const char *substitutions) +{ + return dbReadCOM(ppdbbase, filename, 0, path, substitutions); +} + +long dbReadDatabaseFP(DBBASE **ppdbbase, FILE *fp, + const char *path, const char *substitutions) +{ + return dbReadCOM(ppdbbase, 0, fp, path, substitutions); +} -long dbReadDatabaseFP(DBBASE **ppdbbase,FILE *fp, - const char *path,const char *substitutions) -{return (dbReadCOM(ppdbbase,0,fp,path,substitutions));} - static int db_yyinput(char *buf, int max_size) { size_t l,n; @@ -1190,6 +1197,37 @@ static void dbRecordHead(char *recordType, char *name, int visible) dbVisibleRecord(pdbentry); } +/* For better suggestions for wrong field names + the following array contains pairs of often + confused fields. Thus, the number of elements + must be even. + For the last character, ranges like A-F are + allowed as a shortcut. Pairs must have matching + range size. + If extending this map, please add only field names + found in record types from base. + Each array element (i.e. both sides of a pair) + is tested against the faulty field name. + The first match (considering ranges) where the + other side of the pair is an existing field name + (after adjusting for ranges) will be suggested + as a replacement. + If no such match is found, the suggestion falls + back to weighted lexical similarity with existing + field names. +*/ + +static const char* const dbFieldConfusionMap [] = { + "INP","OUT", + "DOL","INP", + "ZNAM","ZRST", + "ONAM","ONST", + "INPA-J","DOL0-9", + "INPK-P","DOLA-F", + "INP0-9","INPA-J" +}; +STATIC_ASSERT(NELEMENTS(dbFieldConfusionMap)%2==0); + static void dbRecordField(char *name,char *value) { DBENTRY *pdbentry; @@ -1206,18 +1244,140 @@ static void dbRecordField(char *name,char *value) dbGetRecordTypeName(pdbentry), dbGetRecordName(pdbentry), name); if(dbGetRecordName(pdbentry)) { DBENTRY temp; - double bestSim = -1.0; const dbFldDes *bestFld = NULL; + int i; dbCopyEntryContents(pdbentry, &temp); - for(status = dbFirstField(&temp, 0); !status; status = dbNextField(&temp, 0)) { - double sim = epicsStrSimilarity(name, temp.pflddes->name); - if(!bestFld || sim > bestSim) { - bestSim = sim; + for(i = 0; i < NELEMENTS(dbFieldConfusionMap); i++) { + const char* fieldname = dbFieldConfusionMap[i]; + const char* replacement = dbFieldConfusionMap[i^1]; /* swap even with odd indices */ + const char* guess = NULL; + char buf[8]; /* no field name is so long */ + size_t l = strlen(fieldname); + if (l >= 3 && fieldname[l-2] == '-' && + strncmp(name, fieldname, l-3) == 0 && + name[l-3] >= fieldname[l-3] && + name[l-3] <= fieldname[l-1]) + { + /* range map (like XXXA-Z) */ + size_t l2 = strlen(replacement); + strncpy(buf, replacement, sizeof(buf)-1); + buf[l2-3] += name[l-3] - fieldname[l-3]; + buf[l2-2] = 0; + guess = buf; + } else if (strcmp(name, fieldname) == 0) { + /* simple map */ + guess = replacement; + } + if (guess && dbFindFieldPart(&temp, &guess) == 0) { + /* guessed field exists */ bestFld = temp.pflddes; + break; + } + } + if (!bestFld) { + /* no map found, use weighted lexical similarity + the weights are a bit arbitrary */ + double bestSim = -1.0; + char quote = 0; + if (*value == '"' || *value == '\'') + quote = *value++; + for (status = dbFirstField(&temp, 0); !status; status = dbNextField(&temp, 0)) { + if (temp.pflddes->special == SPC_NOMOD || + temp.pflddes->special == SPC_DBADDR) /* cannot be configured */ + continue; + double sim = epicsStrSimilarity(name, temp.pflddes->name); + if (!temp.pflddes->promptgroup) + sim *= 0.5; /* no prompt: unlikely */ + if (temp.pflddes->interest) + sim *= 1.0 - 0.1 * temp.pflddes->interest; /* 10% less likely per interest level */ + if (sim == 0) + continue; + if (*value != quote) { + /* value given, check match to field type */ + long status = 0; + char* end = "e; + + switch (temp.pflddes->field_type) { + epicsAny dummy; + case DBF_CHAR: + status = epicsParseInt8(value, &dummy.int8, 0, &end); + break; + case DBF_UCHAR: + status = epicsParseUInt8(value, &dummy.uInt8, 0, &end); + break; + case DBF_SHORT: + status = epicsParseInt16(value, &dummy.int16, 0, &end); + break; + case DBF_USHORT: + case DBF_ENUM: + status = epicsParseUInt16(value, &dummy.uInt16, 0, &end); + break; + case DBF_LONG: + status = epicsParseInt32(value, &dummy.int32, 0, &end); + break; + case DBF_ULONG: + status = epicsParseUInt32(value, &dummy.uInt32, 0, &end); + break; + case DBF_INT64: + status = epicsParseInt64(value, &dummy.int64, 0, &end); + break; + case DBF_UINT64: + status = epicsParseUInt64(value, &dummy.uInt64, 0, &end); + break; + case DBF_FLOAT: + status = epicsParseFloat(value, &dummy.float32, &end); + break; + case DBF_DOUBLE: + status = epicsParseDouble(value, &dummy.float64, &end); + break; + case DBF_MENU: + case DBF_DEVICE: { + char** choices; + int nChoice; + int choice; + + if (temp.pflddes->field_type == DBF_MENU) { + dbMenu* menu = (dbMenu*)temp.pflddes->ftPvt; + choices = menu->papChoiceValue; + nChoice = menu->nChoice; + } else { + dbDeviceMenu* menu = (dbDeviceMenu*)temp.pflddes->ftPvt; + choices = menu->papChoice; + nChoice = menu->nChoice; + } + status = epicsParseUInt16(value, &dummy.uInt16, 0, &end); + if (!status && *end == quote && dummy.uInt16 < nChoice) { + if (temp.pflddes->field_type == DBF_DEVICE) + sim *= 0.5; /* numeric device type index is uncommon */ + break; + } + for (choice = 0; choice < nChoice; choice++) { + size_t len = strlen(choices[choice]); + end = value + len; + if (strncmp(value, choices[choice], len) == 0 && *end == quote) { + sim *= 1.5; /* boost for matching choice string */ + status = 0; + break; + } + } + if (choice == nChoice) + status = S_stdlib_noConversion; + break; + } + default: + break; + } + if (status || *end != quote) + sim *= 0.1; /* value type does not match field type: unlikely */ + } + if (sim > bestSim) { + bestSim = sim; + bestFld = temp.pflddes; + } } } dbFinishEntry(&temp); - if(bestSim>0.0) { + if (bestFld) { fprintf(stderr, " Did you mean \"%s\"?", bestFld->name); if(bestFld->prompt) fprintf(stderr, " (%s)", bestFld->prompt); diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.c b/modules/database/src/ioc/dbStatic/dbStaticLib.c index 5b81fe213..39c1ce5f2 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticLib.c +++ b/modules/database/src/ioc/dbStatic/dbStaticLib.c @@ -2212,7 +2212,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) if(!plink->text) continue; - if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) { + if(dbParseLink(plink->text, pflddes->field_type, &link_info, prec->name, pflddes->name)!=0) { /* This was already parsed once when ->text was set. * Any syntax error messages were printed at that time. */ @@ -2241,7 +2241,7 @@ void dbFreeLinkInfo(dbLinkInfo *pinfo) pinfo->target = NULL; } -long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) +long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, const char *recname, const char *fieldname) { char *pstr; size_t len; @@ -2378,7 +2378,13 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) /* filter modifiers based on link type */ switch(ftype) { case DBF_INLINK: /* accept all */ break; - case DBF_OUTLINK: pinfo->modifiers &= ~pvlOptCPP; break; + case DBF_OUTLINK: + if(pinfo->modifiers & (pvlOptCPP|pvlOptCP)){ + errlogPrintf(ERL_WARNING ": Discarding CP/CPP modifier in CA output link from %s.%s to %s.\n", + recname, fieldname, pinfo->target); + } + pinfo->modifiers &= ~(pvlOptCPP|pvlOptCP); + break; case DBF_FWDLINK: pinfo->modifiers &= pvlOptCA; break; } } @@ -2616,7 +2622,7 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) dbLinkInfo link_info; DBLINK *plink = (DBLINK *)pfield; - status = dbParseLink(pstring, pflddes->field_type, &link_info); + status = dbParseLink(pstring, pflddes->field_type, &link_info, dbGetRecordName(pdbentry), dbGetFieldName(pdbentry)); if (status) break; if (plink->type==CONSTANT && plink->value.constantStr==NULL) { @@ -3600,7 +3606,7 @@ void dbReportDeviceConfig(dbBase *pdbbase, FILE *report) if (plink->text) { /* Not yet parsed */ dbLinkInfo linfo; - if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo)) + if (dbParseLink(plink->text, pdbentry->pflddes->field_type, &linfo, dbGetRecordName(pdbentry), dbGetFieldName(pdbentry))) continue; linkType = linfo.ltype; diff --git a/modules/database/src/ioc/dbStatic/dbStaticPvt.h b/modules/database/src/ioc/dbStatic/dbStaticPvt.h index 033fde40c..32287a149 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticPvt.h +++ b/modules/database/src/ioc/dbStatic/dbStaticPvt.h @@ -74,7 +74,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec); /* Parse link string. no record locks needed. * on success caller must free pinfo->target */ -DBCORE_API long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo); +DBCORE_API long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, const char *recordname, const char *fieldname); /* Check if link type allow the parsed link value pinfo * to be assigned to the given link. * Record containing plink must be locked. diff --git a/modules/database/src/ioc/misc/registerAllRecordDeviceDrivers.cpp b/modules/database/src/ioc/misc/registerAllRecordDeviceDrivers.cpp index 1a422d427..4b96a465b 100644 --- a/modules/database/src/ioc/misc/registerAllRecordDeviceDrivers.cpp +++ b/modules/database/src/ioc/misc/registerAllRecordDeviceDrivers.cpp @@ -9,7 +9,14 @@ #include #include +#if defined(vxWorks) && \ + (_WRS_VXWORKS_MAJOR+0 <= 6) && (_WRS_VXWORKS_MINOR+0 < 9) +typedef int intptr_t; +typedef unsigned int uintptr_t; +#else #include +#endif + #include #include diff --git a/modules/database/src/std/ComponentReference.pod b/modules/database/src/std/ComponentReference.pod index c43394d50..029b19980 100644 --- a/modules/database/src/std/ComponentReference.pod +++ b/modules/database/src/std/ComponentReference.pod @@ -14,9 +14,9 @@ documentation is now being published. =over -=item * L +=item * L -=item * L +=item * L =back diff --git a/modules/database/src/std/link/links.dbd.pod b/modules/database/src/std/link/links.dbd.pod index 9a3f7b9c7..27689e321 100644 --- a/modules/database/src/std/link/links.dbd.pod +++ b/modules/database/src/std/link/links.dbd.pod @@ -86,6 +86,8 @@ input links, and returns a double-precision floating-point result. The expression is evaluated by the EPICS Calc engine, and the result is returned as the value of the link. +Since UNRELEASED the number of inputs has been increased from 12 to 21. + Two additional expressions may also be provided and are evaluated to determine whether the record owning the link should be placed in alarm state. In both cases the result of the main calculation is available to these expressions as @@ -126,7 +128,7 @@ An optional expression that returns non-zero to raise a minor alarm. =item args -A JSON list of up to 12 input arguments for the expression, which are assigned +A JSON list of up to 24 input arguments for the expression, which are assigned to the inputs C, C, C, ... C. Each input argument may be either a numeric literal or an embedded JSON link inside C<{}> braces. The same input values are provided to the two alarm expressions as to the primary expression. diff --git a/modules/database/src/std/rec/aSubRecord.dbd.pod b/modules/database/src/std/rec/aSubRecord.dbd.pod index 5131af881..86fd9e70d 100644 --- a/modules/database/src/std/rec/aSubRecord.dbd.pod +++ b/modules/database/src/std/rec/aSubRecord.dbd.pod @@ -198,7 +198,7 @@ Except when it doesn't. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } =head3 Output Event Flag diff --git a/modules/database/src/std/rec/aaiRecord.dbd.pod b/modules/database/src/std/rec/aaiRecord.dbd.pod index 969f5800f..e77832585 100644 --- a/modules/database/src/std/rec/aaiRecord.dbd.pod +++ b/modules/database/src/std/rec/aaiRecord.dbd.pod @@ -319,7 +319,7 @@ Scan forward link if necessary, set PACT FALSE, and return. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(INP,DBF_INLINK) { prompt("Input Specification") @@ -331,19 +331,19 @@ Scan forward link if necessary, set PACT FALSE, and return. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(NELM,DBF_ULONG) { prompt("Number of Elements") @@ -351,6 +351,7 @@ Scan forward link if necessary, set PACT FALSE, and return. special(SPC_NOMOD) interest(1) initial("1") + prop(YES) # get_graphic_double, get_control_double } field(FTVL,DBF_MENU) { prompt("Field Type of Value") diff --git a/modules/database/src/std/rec/aaoRecord.dbd.pod b/modules/database/src/std/rec/aaoRecord.dbd.pod index cfb70af2b..c35b7454e 100644 --- a/modules/database/src/std/rec/aaoRecord.dbd.pod +++ b/modules/database/src/std/rec/aaoRecord.dbd.pod @@ -45,7 +45,7 @@ its output. It can be a hardware address, a channel access or database link, or a constant. Only in records that use soft device support can the OUT field be a channel access link, a database link, or a constant. Otherwise, the OUT field must be a hardware address. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of hardware addresses and database links. =head4 Fields related to array writing @@ -341,7 +341,7 @@ Scan forward link if necessary, set PACT FALSE, and return. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(OUT,DBF_OUTLINK) { prompt("Output Specification") @@ -364,19 +364,19 @@ Scan forward link if necessary, set PACT FALSE, and return. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(NELM,DBF_ULONG) { prompt("Number of Elements") @@ -384,6 +384,7 @@ Scan forward link if necessary, set PACT FALSE, and return. special(SPC_NOMOD) interest(1) initial("1") + prop(YES) # get_graphic_double, get_control_double } field(FTVL,DBF_MENU) { prompt("Field Type of Value") diff --git a/modules/database/src/std/rec/aiRecord.dbd.pod b/modules/database/src/std/rec/aiRecord.dbd.pod index 7b051725b..6cdad47eb 100644 --- a/modules/database/src/std/rec/aiRecord.dbd.pod +++ b/modules/database/src/std/rec/aiRecord.dbd.pod @@ -46,7 +46,7 @@ input data should come from. The format for the INP field value depends on the device support layer that is selected by the DTYP field. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for a description of the various hardware address formats supported. =head3 Units Conversion @@ -212,7 +212,7 @@ positive number of seconds will delay the record going into or out of a minor alarm severity or from minor to major severity until the input signal has been in the alarm range for that number of seconds. -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. @@ -265,7 +265,7 @@ monitoring functionality. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(LINR,DBF_MENU) { prompt("Linearization") @@ -294,19 +294,19 @@ monitoring functionality. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(AOFF,DBF_DOUBLE) { prompt("Adjustment Offset") @@ -331,35 +331,35 @@ monitoring functionality. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -367,7 +367,7 @@ monitoring functionality. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -375,7 +375,7 @@ monitoring functionality. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -383,7 +383,7 @@ monitoring functionality. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { diff --git a/modules/database/src/std/rec/aoRecord.dbd.pod b/modules/database/src/std/rec/aoRecord.dbd.pod index 6d596644f..34519ab52 100644 --- a/modules/database/src/std/rec/aoRecord.dbd.pod +++ b/modules/database/src/std/rec/aoRecord.dbd.pod @@ -162,7 +162,7 @@ OUT field. For analog outputs that write their values to devices, the OUT field must specify the address of the I/O card. In addition, the DTYP field must contain the name of the device support module. Be aware that the address format differs according to the I/O bus used. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of hardware addresses. For soft records the output link can be a database link, a channel @@ -328,7 +328,7 @@ for more information on simulation mode and its fields. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(LINR,DBF_MENU) { prompt("Linearization") @@ -357,7 +357,7 @@ for more information on simulation mode and its fields. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(ROFF,DBF_ULONG) { prompt("Raw Offset") @@ -382,26 +382,26 @@ for more information on simulation mode and its fields. promptgroup("30 - Action") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_control_double } field(DRVL,DBF_DOUBLE) { prompt("Drive Low Limit") promptgroup("30 - Action") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_control_double } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double } field(AOFF,DBF_DOUBLE) { prompt("Adjustment Offset") @@ -420,35 +420,35 @@ for more information on simulation mode and its fields. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -456,7 +456,7 @@ for more information on simulation mode and its fields. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -464,7 +464,7 @@ for more information on simulation mode and its fields. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -472,7 +472,7 @@ for more information on simulation mode and its fields. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) #get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { diff --git a/modules/database/src/std/rec/biRecord.dbd.pod b/modules/database/src/std/rec/biRecord.dbd.pod index 4ef5be036..b46c21f65 100644 --- a/modules/database/src/std/rec/biRecord.dbd.pod +++ b/modules/database/src/std/rec/biRecord.dbd.pod @@ -47,13 +47,13 @@ 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
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of the hardware address. 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
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of database and channel access addresses. Also, see L in this chapter for information on soft device support. @@ -107,7 +107,7 @@ 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 +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. @@ -186,7 +186,7 @@ is not equal to VAL. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ONAM,DBF_STRING) { prompt("One Name") @@ -194,7 +194,7 @@ is not equal to VAL. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_Str } field(RVAL,DBF_ULONG) { prompt("Raw Value") diff --git a/modules/database/src/std/rec/boRecord.dbd.pod b/modules/database/src/std/rec/boRecord.dbd.pod index 6bc4e9892..84f22024f 100644 --- a/modules/database/src/std/rec/boRecord.dbd.pod +++ b/modules/database/src/std/rec/boRecord.dbd.pod @@ -45,7 +45,7 @@ continuous control, a database link to a control algorithm record should be entered in the DOL field. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on hardware addresses and links. =fields DOL, OMSL @@ -105,13 +105,13 @@ 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
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of hardware addresses. 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
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> 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. @@ -239,7 +239,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ONAM,DBF_STRING) { prompt("One Name") @@ -247,7 +247,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(RVAL,DBF_ULONG) { prompt("Raw Value") diff --git a/modules/database/src/std/rec/calcRecord.dbd.pod b/modules/database/src/std/rec/calcRecord.dbd.pod index 9d61b08c0..d51f1408b 100644 --- a/modules/database/src/std/rec/calcRecord.dbd.pod +++ b/modules/database/src/std/rec/calcRecord.dbd.pod @@ -14,6 +14,8 @@ 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. +Since UNRELEASED the number of inputs has been increased from 12 to 21. + =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. @@ -35,45 +37,49 @@ These fields are described in L. =head3 Read Parameters The read parameters for the Calc record consist of 21 input links INPA - -INPU. The fields can be database links, channel access links, or -constants. If they are links, they must specify another record's field or a +INPU. 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
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on how to specify database links. =fields INPA - INPU =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. +At the core of the Calc record lies the CALC and RPCL fields. +The CALC field holds an infix expression to be evaluated whenever the record is +processed. +The calculated value is placed in the VAL field and can be accessed from there. + +The CALC expression gets compiled into a stream of Reverse Polish Notation (RPN) +opcodes for a stack-based machine, and stored in the RPCL field. +The RPN opcodes are used to calculate VAL at run-time, and are more efficient +than evaluating the infix expression. +The CALC expression can be replaced at run-time, triggering a special record +routine to compile the new expression into 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. +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 C<;>, any one of which may provide the calculation result. +All other expressions included in the string 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. +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 colon or C operator. =fields CALC, RPCL @@ -85,10 +91,10 @@ parentheses and commas, and the question mark or '?:' operator. Standard double precision floating point numbers =item * -Inf: Infinity +C: Infinity =item * -Nan: Not a Number +C: Not a Number =back @@ -97,14 +103,14 @@ Nan: Not a Number =over 1 =item * -PI: returns the mathematical constant E +C: returns the mathematical constant E =item * -D2R: evaluates to E/180 which, when used as a multiplier, converts an +C: 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 +C: evaluates to 180/E which as a multiplier converts an angle from radians to degrees =back @@ -127,112 +133,205 @@ 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 +=head3 Arithmetic Operators + +Except for unary minus these are infix binary operators. =over 1 =item * -ABS: Absolute value (unary) +C<+> : Addition =item * -SQR: Square root (unary) +C<-> : Subtraction =item * -MIN: Minimum (any number of args) +C<-> : Minus (unary) =item * -MAX: Maximum (any number of args) +C<*> : Multiplication =item * -FINITE: returns non-zero if none of the arguments are NaN or Inf (any -number of args) +C : Division =item * -ISNAN: returns non-zero if any of the arguments is NaN or Inf (any number -of args) +C<%> : Modulo =item * -CEIL: Ceiling (unary) +C<^> : Exponential =item * -FLOOR: Floor (unary) - -=item * -FMOD: Floating point modulo (binary) Added in 7.0.8 - -=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) +C<**> : Exponential =back -=head3 Trigonometric Operators +=head3 Algebraic Functions + +When functions take more than one argument, a comma separator must appear +between them. =over 1 =item * -SIN: Sine +C : Absolute value =item * -SINH: Hyperbolic sine +C : Exponential function =item * -ASIN: Arc sine +C : Floating point modulo. Added in 7.0.8 =item * -COS: Cosine +C : Natural log =item * -COSH: Hyperbolic cosine +C : Log base 10 =item * -ACOS: Arc cosine +C : Natural log =item * -TAN: Tangent +C : Minimum =item * -TANH: Hyperbolic tangent +C : Maximum =item * -ATAN: Arc tangent +C : Square root + +=item * +C : Square root + +=back + +=head3 Trigonometric Functions + +=over 1 + +=item * +C : Sine + +=item * +C : Arc sine + +=item * +C : Cosine + +=item * +C : Arc cosine + +=item * +C : Tangent + +=item * +C : Arc tangent + +=item * +C : 2-parameter Arc tangent. Arg's are reversed to ANSI C + +=back + +=head3 Hyperbolic Trigonometry Functions + +=over 1 + +=item * +C : Hyperbolic sine + +=item * +C : Hyperbolic cosine + +=item * +C : Hyperbolic tangent + +=back + +=head3 Numeric Functions + +=over 1 + +=item * +C : Ceiling + +=item * +C : Floor + +=item * +C : Round to nearest integer + +=item * +C : returns non-zero if any argument is Inf + +=item * +C : returns non-zero (true) if any argument is NaN +or Inf + +=item * +C : returns non-zero (true) if none of the +arguments are NaN or Inf + +=back + +=head3 Boolean/Logical Operators + +These operators use their arguments as a true (non-zero) or false (zero) value. + +=over 1 + +=item * +C<&&> : And, infix binary + +=item * +C<||> : Or, infix binary + +=item * +C : Not, unary prefix + +=back + +=head3 Bitwise Operators + +Mostly infix binary, the arguments are converted to a 32-bit integer, the +operator is applied, and the result converted back into a double. + +=over 1 + +=item * +C<&> : Bitwise and + +=item * +C<|> : Bitwise or + +=item * +C<~> : Bitwise not or one's complement, unary prefix + +=item * +C<<< << >>> : Arithmetic shift left + +=item * +C<<< >> >>> : Arithmetic shift right + +=item * +C<<<< >>> >>>> : Logical shift right + +=item * +C : Bitwise and + +=item * +C : Bitwise or + +=item * +C : Bitwise exclusive or + +=item * +C : Bitwise not or one's complement, unary prefix =back =head3 Relational Operators +These are all infix binary operators. + =over 1 =item * @@ -247,59 +346,17 @@ C<<< <= >>> : Less than or equal to =item * C<<< < >>> : Less than +=item * +C<<< != >>> : Not equal to + =item * C<<< # >>> : Not equal to =item * -C<<< = >>> : Equal to - -=back - -=head3 Logical Operators - -=over 1 +C<<< == >>> : Equal to =item * -C<&&> : And - -=item * -C<||> : Or - -=item * -C : Not - -=back - -=head3 Bitwise Operators - -=over 1 - -=item * -C<|> : Bitwise Or - -=item * -C<&> : Bitwise And - -=item * -OR : Bitwise Or - -=item * -AND : Bitwise And - -=item * -XOR : Bitwise Exclusive Or - -=item * -C<~> : One's Complement - -=item * -C<<< << >>> : Arithmetic Left Shift - -=item * -C<<< >> >>> : Arithmetic Right Shift - -=item * -C<<<< >>> >>>> : Logical Right Shift +C<<< = >>> : Equal to (not assignment) =back @@ -312,24 +369,26 @@ C<:=> : assigns a value (right hand side) to a variable (i.e. field) =back -=head3 Parantheses, Comma, and Semicolon +=head3 Parentheses, Comma, and Semicolon -The open and close parentheses are supported. Nested parentheses are -supported. +The open C<(> and close parentheses C<)> are supported to override precedence +rules in a sub-expression. +Nested parentheses are supported to significant depth. -The comma is supported when used to separate the arguments of a binary -function. +The comma C<,> is required to separate the arguments of a function. -The semicolon is used to separate expressions. Although only one -traditional calculation expression is allowed, multiple assignment -expressions are allowed. +The semicolon C<;> is used to value separate expressions. +Exactly one value expression must be present, but multiple assignment +expressions may be included before and/or after the value expression. -=head3 Conditional Expression +=head3 Conditional Operator -The C language's question mark operator is supported. The format is: -C +The C language's question mark colon C ternary operator is supported. +The format is: -=head3 Expression Examples +I C I C<:> I + +=head2 Expression Examples =head3 Algebraic @@ -370,21 +429,21 @@ Result is C if C<<< (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: +Prior to Base 3.14.9 it was legal to omit the colon C<:> and the second (else) +part of the conditional, like this: -C<(A + B)<(C + D) ? E> +C<<< (A + B)<(C + D) ? E >>> =over 1 -=item -Result is E if (A + B)<(C + D) +=item * +Result is C if C<<< (A + B)<(C + D) >>> -=item -Result is unchanged if (A + B)>=(C + D) +=item * +Result is unchanged if C<<< (A + B)>=(C + D) >>> From 3.14.9 onwards, this expresion must be written as -C<(A + B) < (C + D) ? E : VAL> +C<<< (A + B) < (C + D) ? E : VAL >>> =back @@ -406,7 +465,7 @@ Convert A to integer Convert B to integer =item * -Bitwise And A and B +Bitwise A C B =item * Convert result to floating point @@ -465,7 +524,7 @@ reporting of limit alarms until the signal has been within the alarm range for that number of seconds (the default AFTC value of zero retains the previous behavior). -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. @@ -634,60 +693,60 @@ manner for the VAL field. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(HOPR,DBF_DOUBLE) { prompt("High Operating Rng") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_dpuble, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_dpuble, get_control_double } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -695,7 +754,7 @@ manner for the VAL field. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -703,7 +762,7 @@ manner for the VAL field. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -711,7 +770,7 @@ manner for the VAL field. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(AFTC, DBF_DOUBLE) { @@ -1018,23 +1077,25 @@ 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 +Check to see if monitors should be invoked. =over 1 @@ -1053,9 +1114,8 @@ NSEV and NSTA are reset to 0. =back -=over - =item 5. + Scan forward link if necessary, set PACT FALSE, and return. =back diff --git a/modules/database/src/std/rec/calcoutRecord.dbd.pod b/modules/database/src/std/rec/calcoutRecord.dbd.pod index a8946e69b..368ecc854 100644 --- a/modules/database/src/std/rec/calcoutRecord.dbd.pod +++ b/modules/database/src/std/rec/calcoutRecord.dbd.pod @@ -20,6 +20,8 @@ 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. +Since UNRELEASED the number of inputs has been increased from 12 to 21. + =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. @@ -58,8 +60,8 @@ These fields are listed in L. =head3 Read Parameters The read parameters for the Calcout record consists of 21 input links INPA - -INPU. The fields can be database links, channel access links, or -constants. If they are links, they must specify another record's field. If +INPU. 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 - @@ -70,38 +72,45 @@ fields. =fields INPA - INPU -=head3 Expression +=head3 Expressions -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. +Like the Calc record, the Calcout record's CALC field holds an infix expression +to be evaluated whenever the record is processed. +The resulting value is placed in the VAL field. + +The OOPT field condition is applied to VAL (see L) and +controls whether to write to the output link (or post a named event), and the +DOPT field selects whether VAL should be written, or another expression from +the OCAL field should be evaluated and used instead. + +The CALC and OCAL expressions get compiled into streams of Reverse Polish +Notation (RPN) opcodes for a stack-based machine, and stored in the RPCL and +ORPC fields respectively. + +The RPN opcodes are used to calculate VAL at run-time, and are more efficient +than evaluating the infix expression. +The CALC and OCAL expressions can be replaced at run-time, triggering a special +record routine to compile the new expression into 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. +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 C<;>, any one of which may provide the calculation result. +All other expressions included in the string 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 operator, the assignment operator, -parentheses and commas, and the question mark or '?:' operator. +relational operators, logical operators, the assignment operator, parentheses +and commas, and the question mark colon or C operator. -=fields CALC, VAL, RPCL +=fields CALC, VAL, OVAL, RPCL, ORPC =head3 Literals @@ -111,10 +120,10 @@ parentheses and commas, and the question mark or '?:' operator. Standard double precision floating point numbers =item * -Inf: Infinity +C: Infinity =item * -Nan: Not a Number +C: Not a Number =back @@ -123,14 +132,14 @@ Nan: Not a Number =over =item * -PI: returns the mathematical constant E +C: returns the mathematical constant E =item * -D2R: evaluates to E/180 which, when used as a multiplier, converts an +C: 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 +C: evaluates to 180/E which, when used as a multiplier, converts an angle from radians to degrees =back @@ -153,112 +162,205 @@ 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 +=head3 Arithmetic Operators + +Except for unary minus these are infix binary operators. =over 1 =item * -ABS: Absolute value (unary) +C<+> : Addition =item * -SQR: Square root (unary) +C<-> : Subtraction =item * -MIN: Minimum (any number of args) +C<-> : Minus (unary) =item * -MAX: Maximum (any number of args) +C<*> : Multiplication =item * -FINITE: returns non-zero if none of the arguments are NaN or Inf (any -number of args) +C : Division =item * -ISNAN: returns non-zero if any of the arguments is NaN or Inf (any number -of args) +C<%> : Modulo =item * -CEIL: Ceiling (unary) +C<^> : Exponential =item * -FLOOR: Floor (unary) - -=item * -FMOD: Floating point modulo (binary) Added in 7.0.8 - -=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) +C<**> : Exponential =back -=head3 Trigonometric Operators +=head3 Algebraic Functions + +When functions take more than one argument, a comma separator must appear +between them. =over 1 =item * -SIN: Sine +C : Absolute value =item * -SINH: Hyperbolic sine +C : Exponential function =item * -ASIN: Arc sine +C : Floating point modulo. Added in 7.0.8 =item * -COS: Cosine +C : Natural log =item * -COSH: Hyperbolic cosine +C : Log base 10 =item * -ACOS: Arc cosine +C : Natural log =item * -TAN: Tangent +C : Minimum =item * -TANH: Hyperbolic tangent +C : Maximum =item * -ATAN: Arc tangent +C : Square root + +=item * +C : Square root + +=back + +=head3 Trigonometric Functions + +=over 1 + +=item * +C : Sine + +=item * +C : Arc sine + +=item * +C : Cosine + +=item * +C : Arc cosine + +=item * +C : Tangent + +=item * +C : Arc tangent + +=item * +C : 2-parameter Arc tangent. Arg's are reversed to ANSI C + +=back + +=head3 Hyperbolic Trigonometry Functions + +=over 1 + +=item * +C : Hyperbolic sine + +=item * +C : Hyperbolic cosine + +=item * +C : Hyperbolic tangent + +=back + +=head3 Numeric Functions + +=over 1 + +=item * +C : Ceiling + +=item * +C : Floor + +=item * +C : Round to nearest integer + +=item * +C : returns non-zero if any argument is Inf + +=item * +C : returns non-zero (true) if any argument is NaN +or Inf + +=item * +C : returns non-zero (true) if none of the +arguments are NaN or Inf + +=back + +=head3 Boolean/Logical Operators + +These operators use their arguments as a true (non-zero) or false (zero) value. + +=over 1 + +=item * +C<&&> : And, infix binary + +=item * +C<||> : Or, infix binary + +=item * +C : Not, unary prefix + +=back + +=head3 Bitwise Operators + +Mostly infix binary, the arguments are converted to a 32-bit integer, the +operator is applied, and the result converted back into a double. + +=over 1 + +=item * +C<&> : Bitwise and + +=item * +C<|> : Bitwise or + +=item * +C<~> : Bitwise not or one's complement, unary prefix + +=item * +C<<< << >>> : Arithmetic shift left + +=item * +C<<< >> >>> : Arithmetic shift right + +=item * +C<<<< >>> >>>> : Logical shift right + +=item * +C : Bitwise and + +=item * +C : Bitwise or + +=item * +C : Bitwise exclusive or + +=item * +C : Bitwise not or one's complement, unary prefix =back =head3 Relational Operators +These are all infix binary operators. + =over 1 =item * @@ -273,59 +375,17 @@ C<<< <= >>> : Less than or equal to =item * C<<< < >>> : Less than +=item * +C<<< != >>> : Not equal to + =item * C<<< # >>> : Not equal to =item * -C<<< = >>> : Equal to - -=back - -=head3 Logical Operators - -=over 1 +C<<< == >>> : Equal to =item * -&& : And - -=item * -|| : Or - -=item * -! : Not - -=back - -=head3 Bitwise Operators - -=over 1 - -=item * -C<|> : Bitwise Or - -=item * -C<&> : Bitwise And - -=item * -OR : Bitwise Or - -=item * -AND : Bitwise And - -=item * -XOR : Bitwise Exclusive Or - -=item * -C<~> : One's Complement - -=item * -C<<< << >>> : Arithmetic Left Shift - -=item * -C<<< >> >>> : Arithmetic Right Shift - -=item * -C<<<< >>> >>>> : Logical Right Shift +C<<< = >>> : Equal to (not assignment) =back @@ -340,22 +400,24 @@ C<:=> : assigns a value (right hand side) to a variable (i.e. field) =head3 Parentheses, Comma, and Semicolon -The open and close parentheses are supported. Nested parentheses are -supported. +The open C<(> and close parentheses C<)> are supported to override precedence +rules in a sub-expression. +Nested parentheses are supported to significant depth. -The comma is supported when used to separate the arguments of a binary -function. +The comma C<,> is required to separate the arguments of a function. -The semicolon is used to separate expressions. Although only one -traditional calculation expression is allowed, multiple assignment -expressions are allowed. +The semicolon C<;> is used to value separate expressions. +Exactly one value expression must be present, but multiple assignment +expressions may be included before and/or after the value expression. -=head3 Conditional Expression +=head3 Conditional Operator -The C language's question mark operator is supported. The format is: -C +The C language's question mark colon C ternary operator is supported. +The format is: -=head3 Expression Examples +I C I C<:> I + +=head2 Expression Examples =head3 Algebraic @@ -396,21 +458,21 @@ Result is C if C<<< (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: +Prior to Base 3.14.9 it was legal to omit the colon C<:> and the second (else) +part of the conditional, like this: -C<(A + B)<(C + D) ? E> +C<<< (A + B)<(C + D) ? E >>> =over 1 -=item -Result is E if (A + B)<(C + D) +=item * +Result is E if C<<< (A + B)<(C + D) >>> -=item -Result is unchanged if (A + B)>=(C + D) +=item * +Result is unchanged if C<<< (A + B)>=(C + D) >>> From 3.14.9 onwards, this expression must be written as -C<(A + B) < (C + D) ? E : VAL> +C<<< (A + B) < (C + D) ? E : VAL >>> =back @@ -432,7 +494,7 @@ Convert A to integer Convert B to integer =item * -Bitwise And A and B +Bitwise A C B =item * Convert result to floating point @@ -511,13 +573,14 @@ 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. +If the OEVT field isn't empty and the condition in the OOPT field is met, the +record will post the corresponding named 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 enters an INVALID alarm status. The options are @@ -595,7 +658,7 @@ conditions. The HYST field defines an alarm deadband for each limit. -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. @@ -1017,60 +1080,60 @@ manner for the VAL field. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(HOPR,DBF_DOUBLE) { prompt("High Operating Rng") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -1078,7 +1141,7 @@ manner for the VAL field. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -1086,7 +1149,7 @@ manner for the VAL field. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -1094,7 +1157,7 @@ manner for the VAL field. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { @@ -1407,26 +1470,31 @@ 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. + Determine if the Output Execution Option (OOPT) is met. If 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 @@ -1447,6 +1515,7 @@ NSEV and NSTA are reset to 0 =back =item 6. + If no output delay was specified, scan forward link if necessary, set PACT FALSE, and return. @@ -1457,19 +1526,23 @@ FALSE, and return. =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). +put the value of OVAL to the OUT link and post the event named in OEVT (if +not empty). =item 4. + If an output delay was implemented, process the forward link. =back diff --git a/modules/database/src/std/rec/compressRecord.dbd.pod b/modules/database/src/std/rec/compressRecord.dbd.pod index 20c52f496..f2ea5100c 100644 --- a/modules/database/src/std/rec/compressRecord.dbd.pod +++ b/modules/database/src/std/rec/compressRecord.dbd.pod @@ -88,7 +88,7 @@ As stated above, the ALG field specifies which algorithm to be performed on the The INP should be a database or channel access link. Though INP can be a constant, the data compression algorithms are supported only when INP is a database link. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on specifying links. @@ -449,26 +449,26 @@ Scan forward link if necessary, set PACT FALSE, and return. prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(OFF,DBF_ULONG) { prompt("Offset") diff --git a/modules/database/src/std/rec/dfanoutRecord.dbd.pod b/modules/database/src/std/rec/dfanoutRecord.dbd.pod index 355ea38e8..7d92d4c75 100644 --- a/modules/database/src/std/rec/dfanoutRecord.dbd.pod +++ b/modules/database/src/std/rec/dfanoutRecord.dbd.pod @@ -11,9 +11,12 @@ The Data Fanout or "dfanout" record is used to forward data to up to 16 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 +capability to forward data has been added to it. It has no associated device support. +Since UNRELEASED the number of output links has been increased +from 8 to 16 and IVOA and IVOV fields have been added. + =head2 Parameter Fields The record-specific fields are described below, grouped by functionality. @@ -60,7 +63,7 @@ undergoes no conversions before it is sent out to the output links. The OUTA-OUTP fields specify where VAL is to be sent. Each field that is to forward data must specify an address to another record. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on specifying links. The SELL, SELM, and SELN fields specify which output links are to be @@ -140,7 +143,7 @@ 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 +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. @@ -194,82 +197,82 @@ hysteresis factors for monitor callbacks. } field(OUTA,DBF_OUTLINK) { prompt("Output Spec A") - promptgroup("50 - Outputs A-H") + promptgroup("50 - Output") interest(1) } field(OUTB,DBF_OUTLINK) { prompt("Output Spec B") - promptgroup("50 - Outputs A-H") + promptgroup("50 - Output") interest(1) } field(OUTC,DBF_OUTLINK) { prompt("Output Spec C") - promptgroup("50 - Outputs A-H") + promptgroup("50 - Output") interest(1) } field(OUTD,DBF_OUTLINK) { prompt("Output Spec D") - promptgroup("50 - Outputs A-H") + promptgroup("50 - Output") interest(1) } field(OUTE,DBF_OUTLINK) { prompt("Output Spec E") - promptgroup("50 - Outputs A-H") + promptgroup("50 - Output") interest(1) } field(OUTF,DBF_OUTLINK) { prompt("Output Spec F") - promptgroup("50 - Outputs A-H") + promptgroup("50 - Output") interest(1) } field(OUTG,DBF_OUTLINK) { prompt("Output Spec G") - promptgroup("50 - Outputs A-H") + promptgroup("50 - Output") interest(1) } field(OUTH,DBF_OUTLINK) { prompt("Output Spec H") - promptgroup("50 - Outputs A-H") + promptgroup("50 - Output") interest(1) } field(OUTI,DBF_OUTLINK) { prompt("Output Spec I") - promptgroup("51 - Outputs I-P") + promptgroup("50 - Output") interest(1) } field(OUTJ,DBF_OUTLINK) { prompt("Output Spec J") - promptgroup("51 - Outputs I-P") + promptgroup("50 - Output") interest(1) } field(OUTK,DBF_OUTLINK) { prompt("Output Spec K") - promptgroup("51 - Outputs I-P") + promptgroup("50 - Output") interest(1) } field(OUTL,DBF_OUTLINK) { prompt("Output Spec L") - promptgroup("51 - Outputs I-P") + promptgroup("50 - Output") interest(1) } field(OUTM,DBF_OUTLINK) { prompt("Output Spec M") - promptgroup("51 - Outputs I-P") + promptgroup("50 - Output") interest(1) } field(OUTN,DBF_OUTLINK) { prompt("Output Spec N") - promptgroup("51 - Outputs I-P") + promptgroup("50 - Output") interest(1) } field(OUTO,DBF_OUTLINK) { prompt("Output Spec O") - promptgroup("51 - Outputs I-P") + promptgroup("50 - Output") interest(1) } field(OUTP,DBF_OUTLINK) { prompt("Output Spec P") - promptgroup("51 - Outputs I-P") + promptgroup("50 - Output") interest(1) } field(DOL,DBF_INLINK) { @@ -288,60 +291,60 @@ hysteresis factors for monitor callbacks. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -349,7 +352,7 @@ hysteresis factors for monitor callbacks. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -357,7 +360,7 @@ hysteresis factors for monitor callbacks. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -365,7 +368,7 @@ hysteresis factors for monitor callbacks. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { @@ -404,7 +407,7 @@ hysteresis factors for monitor callbacks. interest(2) menu(menuIvoa) } - field(IVOV,DBF_LONG) { + field(IVOV,DBF_DOUBLE) { prompt("INVALID output value") promptgroup("50 - Output") interest(2) @@ -487,6 +490,7 @@ Alarms ranges are checked against the contents of the VAL field. Check severity and then send the value through the OUTA-OUTP links, depending on the setting of SELM and the value in SELN. + See L for information on how INVALID alarms affect output records. diff --git a/modules/database/src/std/rec/eventRecord.dbd.pod b/modules/database/src/std/rec/eventRecord.dbd.pod index 964c3973c..e7a17c499 100644 --- a/modules/database/src/std/rec/eventRecord.dbd.pod +++ b/modules/database/src/std/rec/eventRecord.dbd.pod @@ -9,9 +9,12 @@ =title Event Record (event) -The normal use for this record type is to post an event and/or process a -forward link. Device support for this record can provide a hardware interrupt -handler routine for I/O Event-scanned records. +The normal use for this record type is to post a soft-event and/or process a +forward link. +C device support is provided to allow the soft-event name to be +read from an input link. +Hardware device support for this record can provide an interrupt handler routine +to trigger processing of the record when an I/O Intr event occurs. =head2 Parameter Fields @@ -37,18 +40,31 @@ recordtype(event) { =head3 Scan Parameters The event record has the standard fields for specifying under what circumstances -it will be processed. +it should be processed. These fields are described in L. =fields SCAN, PHAS, EVNT, PRIO, PINI -=head3 Event Number Parameters +=head3 Event Name Parameters -The VAL field contains the event number read by the device support routines. It -is this number which is posted. For records that use C device -support, it can be configured before run-time or set via dbPuts. +The VAL field is a string (prior to the Base-3.15.1 release it was a short +integer) providing the name of an IOC soft-event. +This named soft-event gets posted whenever the record is processed. -=fields VAL +When the soft-event name is known at design time, the VAL field should be set to +the name in a loaded database file. +Soft-event names do not have to be registered before use, a handle for the name +is automatically created and stored when a name is first seen and the same +handle returned later if the same name is re-used. + +The EPVT field holds the handle for the soft-event named in the VAL field. +Looking up the handle for a soft-event name is fast and uses a hash table. + +For records that use the default C device support, the soft-event +name can be fetched through the INP field link, written to the VAL field and the +handle looked up during record processing. + +=fields VAL, EPVT =cut @@ -69,16 +85,18 @@ support, it can be configured before run-time or set via dbPuts. =head3 Input Specification -The device support routines use the address in this record to obtain input. For -records that provide an interrupt handler, the INP field should specify the +The device support routines use the address in this record to obtain input. +For records that provide an interrupt handler, the INP field should specify the address of the I/O card, and the DTYP field should specify a valid device -support module. Be aware that the address format differs according to the card -type used. See L
+support module. + +The address format differs according to the card type used. See +L
for information on the format of hardware addresses and specifying links. For soft records, the INP field can be a constant, a database link, or a channel -access link. For soft records, the DTYP field should specify C. +access link, and the DTYP field should be empty or set to C. =fields INP, DTYP @@ -93,24 +111,27 @@ access link. For soft records, the DTYP field should specify C. =head3 Operator Display Parameters See L for more on the record name (NAME) and description (DESC) fields. - +Parameters> +for more on the record name (NAME) and description (DESC) fields. =fields NAME, DESC =head3 Alarm Parameters -The Event record has the alarm parameters common to all record types. L lists other fields related to alarms that are common to all record -types. +The Event record has the alarm parameters common to all record types. +L lists other fields related to alarms +that are common to all record types. + +=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF =head3 Simulation Mode Parameters The following fields are used to operate the event record in the simulation -mode. See L for more information on these -fields. +mode. +See L +for more information on these fields. -=fields SIOL, SVAL, SIML, SIMM, SIMS +=fields SIOL, SVAL, SIML, SIMM, SIMS, SSCN, SDLY =cut @@ -179,38 +200,50 @@ initialized if SIOL is CONSTANT or PV_LINK. If device support includes C, it is called. +The string in VAL is converted to a soft-event handle in EPVT. + =head4 process See next section. +=head4 special + +When the VAL field is set, the new string is converted to a soft-event handle in +EPVT. + =head3 Record Processing -Routine process implements the following algorithm: +Routine C implements the following algorithm: =over =item 1. -readValue is called. See L for more information. +C is called. +See L for more information. =item 2. -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. +If PACT has changed to TRUE, the device support read routine has started +but has not completed reading a new soft-event name. +In this case, the processing routine returns immediately, leaving PACT TRUE. =item 3. -If VAL E 0, post event number VAL. +Set PACT to TRUE. =item 4. -Check to see if monitors should be invoked. Alarm monitors are invoked if the -alarm status or severity has chanet to 0. +Post the soft-event whose handle is in EPVT. =item 5. -Scan forward link if necessary, set PACT FALSE, and return. +Check to see if monitors should be invoked. +Alarm monitors are invoked if the new alarm status or severity are non-zero. + +=item 6. + +Scan forward link if set, set PACT to FALSE, and return. =back @@ -221,13 +254,15 @@ Scan forward link if necessary, set PACT FALSE, and return. Each record must have an associated set of device support routines. The device support routines are primarily interested in the following fields: -=fields PACT, DPVT, UDF, NSEV, NSTA, INP, PRIO +=fields PACT, DPVT, UDF, NSEV, NSTA, INP, PRIO, VAL, EPVT =head3 Device Support Routines Device support consists of the following routines: -=head4 long report(int level) +=head4 report + + long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. @@ -235,9 +270,11 @@ It should print a report on the state of the device support to stdout. The C parameter may be used to output increasingly more detailed information at higher levels, or to select different types of information with different levels. -Level zero should print no more than a small summary. +Level zero should print only a 1-line summary. -=head4 long init(int after) +=head4 init + + long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with @@ -247,23 +284,25 @@ with C set to 1. =head4 init_record - init_record(precord) + long init_record(precord) This routine is optional. If provided, it is called by the record support C routine. =head4 get_ioint_info - get_ioint_info(int cmd, struct dbCommon *precord, IOSCANPVT *ppvt) + long get_ioint_info(int cmd, struct dbCommon *precord, IOSCANPVT *ppvt) -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. +This routine is called by the dbScan 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. +The C routine is optional, but must be provided by any device +support that implements C scanning for the event record type. =head4 read_event - read_event(precord) + long read_event(precord) This routine returns the following values: @@ -281,15 +320,13 @@ Other: Error. =head3 Device Support For Soft Records -The C device support module is available. The INP link type must -be either CONSTANT, DB_LINK, or CA_LINK. +A C device support module is available. +The INP link field is used to fetch the soft-event name. -If the INP link type is CONSTANT, then the constant value is stored into VAL by -C, and UDF is set to FALSE. If the INP link type is PV_LINK, then -dbCaAddInlink is called by C. +The C routine reads a string through INP and stores it in VAL, +then looks up the named soft-event handle and sets EPVT. -C calls recGblGetLinkValue to read the current value of VAL. See -L for details on soft input. +See L for details on soft input. =cut diff --git a/modules/database/src/std/rec/int64inRecord.dbd.pod b/modules/database/src/std/rec/int64inRecord.dbd.pod index a6e793d8d..51d0f3c40 100644 --- a/modules/database/src/std/rec/int64inRecord.dbd.pod +++ b/modules/database/src/std/rec/int64inRecord.dbd.pod @@ -135,47 +135,47 @@ monitoring deadband functionality. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_INT64) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_INT64) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_INT64) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_INT64) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_INT64) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_INT64) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") diff --git a/modules/database/src/std/rec/int64outRecord.dbd.pod b/modules/database/src/std/rec/int64outRecord.dbd.pod index 07e929a12..b9e8aacd5 100644 --- a/modules/database/src/std/rec/int64outRecord.dbd.pod +++ b/modules/database/src/std/rec/int64outRecord.dbd.pod @@ -171,61 +171,61 @@ monitoring deadband functionality. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(DRVH,DBF_INT64) { prompt("Drive High Limit") promptgroup("30 - Action") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_control_double } field(DRVL,DBF_INT64) { prompt("Drive Low Limit") promptgroup("30 - Action") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_control_double } field(HOPR,DBF_INT64) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_INT64) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_INT64) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_INT64) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_INT64) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_INT64) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") diff --git a/modules/database/src/std/rec/longinRecord.dbd.pod b/modules/database/src/std/rec/longinRecord.dbd.pod index 94b8cf7f5..1cf425ab0 100644 --- a/modules/database/src/std/rec/longinRecord.dbd.pod +++ b/modules/database/src/std/rec/longinRecord.dbd.pod @@ -345,53 +345,54 @@ sets UDF to FALSE. read_longin returns the status of C. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_LONG) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_LONG) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_LONG) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_LONG) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_LONG) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_LONG) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -399,6 +400,7 @@ sets UDF to FALSE. read_longin returns the status of C. promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -406,6 +408,7 @@ sets UDF to FALSE. read_longin returns the status of C. promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -413,6 +416,7 @@ sets UDF to FALSE. read_longin returns the status of C. promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_LONG) { diff --git a/modules/database/src/std/rec/longoutRecord.dbd.pod b/modules/database/src/std/rec/longoutRecord.dbd.pod index 9fead68d4..d9a2c6e3c 100644 --- a/modules/database/src/std/rec/longoutRecord.dbd.pod +++ b/modules/database/src/std/rec/longoutRecord.dbd.pod @@ -78,7 +78,7 @@ channel access link. If the link is a constant, the result is no output. The DTYP field must then specify the C<<< Soft Channel >>> device support routine. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of hardware addresses and database links. =fields OUT, DTYP, OOPT, OOCH @@ -187,33 +187,33 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(DRVH,DBF_LONG) { prompt("Drive High Limit") promptgroup("30 - Action") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_control_double } field(DRVL,DBF_LONG) { prompt("Drive Low Limit") promptgroup("30 - Action") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_control_double } field(HOPR,DBF_LONG) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_LONG) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } =head3 Alarm Parameters @@ -233,7 +233,7 @@ The HYST field sets an alarm deadband around each limit alarm. For an explanation of the IVOA and IVOV fields, see L. -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. @@ -247,34 +247,35 @@ to alarms that are common to all record types. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_LONG) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_LONG) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_LONG) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -282,6 +283,7 @@ to alarms that are common to all record types. promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -289,6 +291,7 @@ to alarms that are common to all record types. promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -296,6 +299,7 @@ to alarms that are common to all record types. promptgroup("70 - Alarm") pp(TRUE) interest(1) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_LONG) { diff --git a/modules/database/src/std/rec/mbbiDirectRecord.dbd.pod b/modules/database/src/std/rec/mbbiDirectRecord.dbd.pod index 40e1fd103..43d45352c 100644 --- a/modules/database/src/std/rec/mbbiDirectRecord.dbd.pod +++ b/modules/database/src/std/rec/mbbiDirectRecord.dbd.pod @@ -102,6 +102,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. promptgroup("40 - Input") special(SPC_NOMOD) interest(1) + prop(YES) # get_precision } field(INP,DBF_INLINK) { prompt("Input Specification") diff --git a/modules/database/src/std/rec/mbbiRecord.dbd.pod b/modules/database/src/std/rec/mbbiRecord.dbd.pod index 56b0d42f2..8e208b064 100644 --- a/modules/database/src/std/rec/mbbiRecord.dbd.pod +++ b/modules/database/src/std/rec/mbbiRecord.dbd.pod @@ -276,7 +276,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ONST,DBF_STRING) { prompt("One String") @@ -285,7 +285,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TWST,DBF_STRING) { prompt("Two String") @@ -294,7 +294,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(THST,DBF_STRING) { prompt("Three String") @@ -303,7 +303,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FRST,DBF_STRING) { prompt("Four String") @@ -312,7 +312,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FVST,DBF_STRING) { prompt("Five String") @@ -321,7 +321,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(SXST,DBF_STRING) { prompt("Six String") @@ -330,7 +330,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(SVST,DBF_STRING) { prompt("Seven String") @@ -339,7 +339,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(EIST,DBF_STRING) { prompt("Eight String") @@ -348,7 +348,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(NIST,DBF_STRING) { prompt("Nine String") @@ -357,7 +357,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TEST,DBF_STRING) { prompt("Ten String") @@ -366,7 +366,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ELST,DBF_STRING) { prompt("Eleven String") @@ -375,7 +375,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TVST,DBF_STRING) { prompt("Twelve String") @@ -384,7 +384,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TTST,DBF_STRING) { prompt("Thirteen String") @@ -393,7 +393,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FTST,DBF_STRING) { prompt("Fourteen String") @@ -402,7 +402,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FFST,DBF_STRING) { prompt("Fifteen String") @@ -411,7 +411,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } =head3 Alarm Parameters @@ -436,7 +436,7 @@ state occurs, if set to MAJOR or MINOR. The other fields, when set to MAJOR or MINOR, trigger an alarm when VAL equals the corresponding state. -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. diff --git a/modules/database/src/std/rec/mbboDirectRecord.dbd.pod b/modules/database/src/std/rec/mbboDirectRecord.dbd.pod index 76d319280..d4266fa52 100644 --- a/modules/database/src/std/rec/mbboDirectRecord.dbd.pod +++ b/modules/database/src/std/rec/mbboDirectRecord.dbd.pod @@ -89,7 +89,7 @@ For records that are to write values to hardware devices, the OUT output link must contain the address of the I/O card, and the DTYP field must specify the proper device support module. Be aware that the address format differs according to the I/O bus used. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on the format of hardware addresses. During record processing VAL is converted into RVAL, which is the actual 32-bit @@ -153,6 +153,7 @@ Parameters> for more on the record name (NAME) and description (DESC) fields. promptgroup("50 - Output") special(SPC_NOMOD) interest(1) + prop(YES) # get_precision } field(DOL,DBF_INLINK) { prompt("Desired Output Link") @@ -299,7 +300,7 @@ the IVOV field to the output. See L for more information about IVOA and IVOV. -See L +See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. diff --git a/modules/database/src/std/rec/mbboRecord.dbd.pod b/modules/database/src/std/rec/mbboRecord.dbd.pod index 3c3652a39..61189e2d6 100644 --- a/modules/database/src/std/rec/mbboRecord.dbd.pod +++ b/modules/database/src/std/rec/mbboRecord.dbd.pod @@ -354,7 +354,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ONST,DBF_STRING) { prompt("One String") @@ -363,7 +363,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TWST,DBF_STRING) { prompt("Two String") @@ -372,7 +372,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(THST,DBF_STRING) { prompt("Three String") @@ -381,7 +381,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FRST,DBF_STRING) { prompt("Four String") @@ -390,7 +390,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FVST,DBF_STRING) { prompt("Five String") @@ -399,7 +399,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(SXST,DBF_STRING) { prompt("Six String") @@ -408,7 +408,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(SVST,DBF_STRING) { prompt("Seven String") @@ -417,7 +417,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(EIST,DBF_STRING) { prompt("Eight String") @@ -426,7 +426,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(NIST,DBF_STRING) { prompt("Nine String") @@ -435,7 +435,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TEST,DBF_STRING) { prompt("Ten String") @@ -444,7 +444,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ELST,DBF_STRING) { prompt("Eleven String") @@ -453,7 +453,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TVST,DBF_STRING) { prompt("Twelve String") @@ -462,7 +462,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(TTST,DBF_STRING) { prompt("Thirteen String") @@ -471,7 +471,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FTST,DBF_STRING) { prompt("Fourteen String") @@ -480,7 +480,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(FFST,DBF_STRING) { prompt("Fifteen String") @@ -489,7 +489,7 @@ for more information on simulation mode and its fields. pp(TRUE) interest(1) size(26) - prop(YES) + prop(YES) # get_enum_str, get_enum_strs, put_enum_str } field(ZRSV,DBF_MENU) { prompt("State Zero Severity") diff --git a/modules/database/src/std/rec/printfRecord.dbd.pod b/modules/database/src/std/rec/printfRecord.dbd.pod index a3bef9610..9bb98b0cc 100644 --- a/modules/database/src/std/rec/printfRecord.dbd.pod +++ b/modules/database/src/std/rec/printfRecord.dbd.pod @@ -148,7 +148,7 @@ which type of the data is requested through the appropriate input link. As with C a C<*> character may be used in the format to specify width and/or precision instead of numeric literals, in which case additional input links are used to provide the necessary integer parameter or parameters. See L
+Specification|https://docs.epics-controls.org/en/latest/process-database/EPICS_Process_Database_Concepts.html#address-specification> for information on specifying links. The formatted string is written to the VAL field. The maximum number of diff --git a/modules/database/src/std/rec/selRecord.dbd.pod b/modules/database/src/std/rec/selRecord.dbd.pod index 56d650366..0988d721b 100644 --- a/modules/database/src/std/rec/selRecord.dbd.pod +++ b/modules/database/src/std/rec/selRecord.dbd.pod @@ -299,6 +299,7 @@ Scan forward link if necessary, set PACT FALSE, and return. prompt("Display Precision") promptgroup("80 - Display") interest(1) + prop(YES) # get_precision } field(NVL,DBF_INLINK) { prompt("Index Value Location") @@ -370,54 +371,54 @@ Scan forward link if necessary, set PACT FALSE, and return. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Rng") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HHSV,DBF_MENU) { prompt("Hihi Severity") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -425,7 +426,7 @@ Scan forward link if necessary, set PACT FALSE, and return. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -433,7 +434,7 @@ Scan forward link if necessary, set PACT FALSE, and return. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -441,7 +442,7 @@ Scan forward link if necessary, set PACT FALSE, and return. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { diff --git a/modules/database/src/std/rec/subArrayRecord.dbd.pod b/modules/database/src/std/rec/subArrayRecord.dbd.pod index 8a95b142e..e3054a938 100644 --- a/modules/database/src/std/rec/subArrayRecord.dbd.pod +++ b/modules/database/src/std/rec/subArrayRecord.dbd.pod @@ -334,7 +334,7 @@ INP is expected to point to an array field of a waveform record or similar. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(FTVL,DBF_MENU) { prompt("Field Type of Value") @@ -353,26 +353,27 @@ INP is expected to point to an array field of a waveform record or similar. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(MALM,DBF_ULONG) { prompt("Maximum Elements") promptgroup("30 - Action") special(SPC_NOMOD) interest(1) - initial("1") + initial("1") + prop(YES) # get_graphic_double, get_control_double } field(NELM,DBF_ULONG) { prompt("Number of Elements") diff --git a/modules/database/src/std/rec/subRecord.c b/modules/database/src/std/rec/subRecord.c index be274d18f..401c6a8eb 100644 --- a/modules/database/src/std/rec/subRecord.c +++ b/modules/database/src/std/rec/subRecord.c @@ -86,7 +86,7 @@ static long do_sub(subRecord *); static long fetch_values(subRecord *); static void monitor(subRecord *); -#define INP_ARG_MAX 12 +#define INP_ARG_MAX 21 static long init_record(struct dbCommon *pcommon, int pass) { @@ -196,9 +196,9 @@ static long special(DBADDR *paddr, int after) #define indexof(field) subRecord##field static long get_linkNumber(int fieldIndex) { - if (fieldIndex >= indexof(A) && fieldIndex <= indexof(L)) + if (fieldIndex >= indexof(A) && fieldIndex < indexof(A) + INP_ARG_MAX) return fieldIndex - indexof(A); - if (fieldIndex >= indexof(LA) && fieldIndex <= indexof(LL)) + if (fieldIndex >= indexof(LA) && fieldIndex < indexof(LA) + INP_ARG_MAX) return fieldIndex - indexof(LA); return -1; } diff --git a/modules/database/src/std/rec/subRecord.dbd.pod b/modules/database/src/std/rec/subRecord.dbd.pod index 6e6edd4bb..774664684 100644 --- a/modules/database/src/std/rec/subRecord.dbd.pod +++ b/modules/database/src/std/rec/subRecord.dbd.pod @@ -12,6 +12,8 @@ The subroutine record is used to call a C initialization routine and a recurring scan routine. There is no device support for this record. +Since UNRELEASED the number of inputs has been increased from 12 to 21. + =recordtype sub =cut @@ -30,17 +32,17 @@ These fields are described in L. =head3 Read Parameters -The subroutine record has twelve input links (INPA-INPL), each of which has a -corresponding value field (A-L). These fields are used to retrieve and store +The subroutine record has 21 input links INPA - INPU, each of which has a +corresponding value field A - U. These fields are used to retrieve and store values that can be passed to the subroutine that the record calls. The input links can be either channel access or database links, or constants. When constants, the corresponding value field for the link is initialized with the constant value and the field's value can be changed at run-time via dbPuts. -Otherwise, the values for (A-F) are fetched from the input links when the record -is processed. +Otherwise, the values for A - U are fetched from the input links when the +record is processed. -=fields INPA - INPL, A - L +=fields INPA - INPU, A - U =head3 Subroutine Connection @@ -115,11 +117,11 @@ processing routines or the monitors. VAL should be set by the subroutine. SADR holds the subroutine address and is set by the record processing routine. -The rest of these fields--LALM, ALST, MLST, and the LA-LL fields--are used to +The rest of these fields--LALM, ALST, MLST, and the LA-LU fields--are used to implement the monitors. For example, when LA is not equal to A, the value-change monitors are called for that field. -=fields VAL, SADR, LALM, ALST, MLST, LA - LL +=fields VAL, SADR, LALM, ALST, MLST, LA - LU =head2 Record Support @@ -161,7 +163,7 @@ recGblGetPrec() >>>. long (*get_graphic_double)(struct dbAddr *paddr, struct dbr_grDouble *p) Sets the upper display and lower display limits for a field. If the field is -VAL, A-L, LA-LL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, +VAL, A-U, LA-LU, 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. @@ -170,7 +172,7 @@ upper and lower maximum values for the field type will be used. long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p) Sets the upper control and the lower control limits for a field. If the field is -VAL, A-L, LA-LL, HIHI, HIGH, LOW, or LOLO, the limits are set to HOPR and LOPR, +VAL, A-U, LA-LU, 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. @@ -241,7 +243,7 @@ met. =item * -Monitors for A-L are invoked if value has changed. +Monitors for A-U are invoked if value has changed. =item * @@ -500,58 +502,103 @@ processing. promptgroup("42 - Input G-L") interest(1) } + field(INPM,DBF_INLINK) { + prompt("Input M") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPN,DBF_INLINK) { + prompt("Input N") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPO,DBF_INLINK) { + prompt("Input O") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPP,DBF_INLINK) { + prompt("Input P") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPQ,DBF_INLINK) { + prompt("Input Q") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPR,DBF_INLINK) { + prompt("Input R") + promptgroup("43 - Input M-R") + interest(1) + } + field(INPS,DBF_INLINK) { + prompt("Input S") + promptgroup("44 - Input S-U") + interest(1) + } + field(INPT,DBF_INLINK) { + prompt("Input T") + promptgroup("44 - Input S-U") + interest(1) + } + field(INPU,DBF_INLINK) { + prompt("Input U") + promptgroup("44 - Input S-U") + interest(1) + } field(EGU,DBF_STRING) { prompt("Engineering Units") promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(HIHI,DBF_DOUBLE) { prompt("Hihi Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOLO,DBF_DOUBLE) { prompt("Lolo Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(HIGH,DBF_DOUBLE) { prompt("High Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(LOW,DBF_DOUBLE) { prompt("Low Alarm Limit") promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double } field(PREC,DBF_SHORT) { prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(BRSV,DBF_MENU) { prompt("Bad Return Severity") @@ -565,7 +612,7 @@ processing. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LLSV,DBF_MENU) { @@ -573,7 +620,7 @@ processing. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HSV,DBF_MENU) { @@ -581,7 +628,7 @@ processing. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(LSV,DBF_MENU) { @@ -589,7 +636,7 @@ processing. promptgroup("70 - Alarm") pp(TRUE) interest(1) - prop(YES) + prop(YES) # get_alarm_double menu(menuAlarmSevr) } field(HYST,DBF_DOUBLE) { @@ -655,6 +702,42 @@ processing. prompt("Value of Input L") pp(TRUE) } + field(M,DBF_DOUBLE) { + prompt("Value of Input M") + pp(TRUE) + } + field(N,DBF_DOUBLE) { + prompt("Value of Input N") + pp(TRUE) + } + field(O,DBF_DOUBLE) { + prompt("Value of Input O") + pp(TRUE) + } + field(P,DBF_DOUBLE) { + prompt("Value of Input P") + pp(TRUE) + } + field(Q,DBF_DOUBLE) { + prompt("Value of Input Q") + pp(TRUE) + } + field(R,DBF_DOUBLE) { + prompt("Value of Input R") + pp(TRUE) + } + field(S,DBF_DOUBLE) { + prompt("Value of Input S") + pp(TRUE) + } + field(T,DBF_DOUBLE) { + prompt("Value of Input T") + pp(TRUE) + } + field(U,DBF_DOUBLE) { + prompt("Value of Input U") + pp(TRUE) + } field(LA,DBF_DOUBLE) { prompt("Prev Value of A") special(SPC_NOMOD) @@ -715,6 +798,51 @@ processing. special(SPC_NOMOD) interest(3) } + field(LM,DBF_DOUBLE) { + prompt("Prev Value of M") + special(SPC_NOMOD) + interest(3) + } + field(LN,DBF_DOUBLE) { + prompt("Prev Value of N") + special(SPC_NOMOD) + interest(3) + } + field(LO,DBF_DOUBLE) { + prompt("Prev Value of O") + special(SPC_NOMOD) + interest(3) + } + field(LP,DBF_DOUBLE) { + prompt("Prev Value of P") + special(SPC_NOMOD) + interest(3) + } + field(LQ,DBF_DOUBLE) { + prompt("Prev Value of Q") + special(SPC_NOMOD) + interest(3) + } + field(LR,DBF_DOUBLE) { + prompt("Prev Value of R") + special(SPC_NOMOD) + interest(3) + } + field(LS,DBF_DOUBLE) { + prompt("Prev Value of S") + special(SPC_NOMOD) + interest(3) + } + field(LT,DBF_DOUBLE) { + prompt("Prev Value of T") + special(SPC_NOMOD) + interest(3) + } + field(LU,DBF_DOUBLE) { + prompt("Prev Value of U") + special(SPC_NOMOD) + interest(3) + } field(LALM,DBF_DOUBLE) { prompt("Last Value Alarmed") special(SPC_NOMOD) diff --git a/modules/database/src/std/rec/waveformRecord.dbd.pod b/modules/database/src/std/rec/waveformRecord.dbd.pod index db4ca362f..c47721d4b 100644 --- a/modules/database/src/std/rec/waveformRecord.dbd.pod +++ b/modules/database/src/std/rec/waveformRecord.dbd.pod @@ -417,7 +417,7 @@ routine and NORD is also set at that time. prompt("Display Precision") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_precision } field(INP,DBF_INLINK) { prompt("Input Specification") @@ -429,19 +429,19 @@ routine and NORD is also set at that time. promptgroup("80 - Display") interest(1) size(16) - prop(YES) + prop(YES) # get_units } field(HOPR,DBF_DOUBLE) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(LOPR,DBF_DOUBLE) { prompt("Low Operating Range") promptgroup("80 - Display") interest(1) - prop(YES) + prop(YES) # get_graphic_double, get_control_double } field(NELM,DBF_ULONG) { prompt("Number of Elements") diff --git a/modules/database/src/std/softIoc/softMain.cpp b/modules/database/src/std/softIoc/softMain.cpp index 384a002fa..0c45975b0 100644 --- a/modules/database/src/std/softIoc/softMain.cpp +++ b/modules/database/src/std/softIoc/softMain.cpp @@ -185,7 +185,7 @@ int main(int argc, char *argv[]) break; case 'D': if(lazy_dbd_loaded) { - throw std::runtime_error("-D specified too late. softIoc.dbd already loaded.\n"); + throw std::runtime_error("-D specified too late, softIoc.dbd already loaded.\n"); } dbd_file = optarg; break; @@ -195,8 +195,7 @@ int main(int argc, char *argv[]) + optarg + "\"" + ( !macros.empty() ? (std::string(", \"") + macros + "\"") : std::string() ) + ")"); - errIf(dbLoadRecords(optarg, macros.c_str()), - std::string("Failed to load: ")+optarg); + errIf(dbLoadRecords(optarg, macros.c_str()), ""); loadedDb = true; break; case 'm': @@ -216,8 +215,7 @@ int main(int argc, char *argv[]) xmacro += optarg; verbose_out(CMD, std::string("dbLoadRecords(\"") + exit_file + "\", \"" + xmacro + "\")"); - errIf(dbLoadRecords(exit_file.c_str(), xmacro.c_str()), - std::string("Failed to load: ")+exit_file); + errIf(dbLoadRecords(exit_file.c_str(), xmacro.c_str()), ""); loadedDb = true; break; } @@ -274,7 +272,8 @@ int main(int argc, char *argv[]) }catch(std::exception& e){ errlogFlush(); - std::cerr<inp, DBF_DOUBLE, &pxxx->val)) + pxxx->udf = FALSE; + + return 0; +} + +static long read_xxx(struct xxxRecord *pxxx) +{ + long status = dbGetLink(&(pxxx->inp), DBF_DOUBLE, &(pxxx->val), 0, 0); + + /* If get was successful VAL is now defined */ + if (!status) + pxxx->udf = FALSE; + + return 0; +} + /*Create the dset for devXxxSoft */ -static long init_record(); -static long read_xxx(); -struct { - long number; - DEVSUPFUN report; - DEVSUPFUN init; - DEVSUPFUN init_record; - DEVSUPFUN get_ioint_info; - DEVSUPFUN read_xxx; -}devXxxSoft={ - 5, - NULL, - NULL, - init_record, - NULL, +xxxdset devXxxSoft = { + { 5, NULL, NULL, init_record, NULL }, read_xxx, }; -epicsExportAddress(dset,devXxxSoft); - - -static long init_record(pxxx) - struct xxxRecord *pxxx; -{ - if(recGblInitConstantLink(&pxxx->inp,DBF_DOUBLE,&pxxx->val)) - pxxx->udf = FALSE; - return(0); -} - -static long read_xxx(pxxx) - struct xxxRecord *pxxx; -{ - long status; - - status = dbGetLink(&(pxxx->inp),DBF_DOUBLE, &(pxxx->val),0,0); - /*If return was succesful then set undefined false*/ - if(!status) pxxx->udf = FALSE; - return(0); -} +epicsExportAddress(dset, devXxxSoft); diff --git a/modules/database/src/template/top/exampleApp/src/xxxRecord.c b/modules/database/src/template/top/exampleApp/src/xxxRecord.c index bf8c79982..282c9744c 100644 --- a/modules/database/src/template/top/exampleApp/src/xxxRecord.c +++ b/modules/database/src/template/top/exampleApp/src/xxxRecord.c @@ -67,15 +67,6 @@ rset xxxRSET={ }; epicsExportAddress(rset,xxxRSET); -typedef struct xxxset { /* xxx input dset */ - long number; - DEVSUPFUN dev_report; - DEVSUPFUN init; - DEVSUPFUN init_record; /*returns: (-1,0)=>(failure,success)*/ - DEVSUPFUN get_ioint_info; - DEVSUPFUN read_xxx; -}xxxdset; - static void checkAlarms(xxxRecord *prec); static void monitor(xxxRecord *prec); @@ -92,13 +83,13 @@ static long init_record(struct dbCommon *pcommon, int pass) return(S_dev_noDSET); } /* must have read_xxx function defined */ - if( (pdset->number < 5) || (pdset->read_xxx == NULL) ) { + if( (pdset->common.number < 5) || (pdset->read_xxx == NULL) ) { recGblRecordError(S_dev_missingSup,(void *)prec,"xxx: init_record"); return(S_dev_missingSup); } - if( pdset->init_record ) { - if((status=(*pdset->init_record)(prec))) return(status); + if( pdset->common.init_record ) { + if((status=(*pdset->common.init_record)(pcommon))) return(status); } return(0); } diff --git a/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd b/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd index af9e01050..088f33f6d 100644 --- a/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd +++ b/modules/database/src/template/top/exampleApp/src/xxxRecord.dbd @@ -3,120 +3,129 @@ # SPDX-License-Identifier: EPICS recordtype(xxx) { - include "dbCommon.dbd" - field(VAL,DBF_DOUBLE) { - prompt("Current EGU Value") - promptgroup("40 - Input") - asl(ASL0) - pp(TRUE) - } - field(INP,DBF_INLINK) { - prompt("Input Specification") - promptgroup("40 - Input") - special(SPC_NOMOD) - interest(1) - } - field(PREC,DBF_SHORT) { - prompt("Display Precision") - promptgroup("80 - Display") - interest(1) - } - field(EGU,DBF_STRING) { - prompt("Engineering Units") - promptgroup("80 - Display") - interest(1) - size(16) - } - field(HOPR,DBF_FLOAT) { - prompt("High Operating Range") - promptgroup("80 - Display") - interest(1) - } - field(LOPR,DBF_FLOAT) { - prompt("Low Operating Range") - promptgroup("80 - Display") - interest(1) - } - field(HIHI,DBF_FLOAT) { - prompt("Hihi Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - } - field(LOLO,DBF_FLOAT) { - prompt("Lolo Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - } - field(HIGH,DBF_FLOAT) { - prompt("High Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - } - field(LOW,DBF_FLOAT) { - prompt("Low Alarm Limit") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - } - field(HHSV,DBF_MENU) { - prompt("Hihi Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(LLSV,DBF_MENU) { - prompt("Lolo Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(HSV,DBF_MENU) { - prompt("High Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - menu(menuAlarmSevr) - } - field(LSV,DBF_MENU) { - prompt("Low Severity") - promptgroup("70 - Alarm") - pp(TRUE) - interest(1) - 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) - } + include "dbCommon.dbd" + % + %/* Declare Device Support Entry Table */ + %struct xxxRecord; + %typedef struct xxxdset { + % dset common; + % long (*read_xxx)(struct xxxRecord *prec); + %} xxxdset; + %#define HAS_xxxdset + % + field(VAL,DBF_DOUBLE) { + prompt("Current EGU Value") + promptgroup("40 - Input") + asl(ASL0) + pp(TRUE) + } + field(INP,DBF_INLINK) { + prompt("Input Specification") + promptgroup("40 - Input") + special(SPC_NOMOD) + interest(1) + } + field(PREC,DBF_SHORT) { + prompt("Display Precision") + promptgroup("80 - Display") + interest(1) + } + field(EGU,DBF_STRING) { + prompt("Engineering Units") + promptgroup("80 - Display") + interest(1) + size(16) + } + field(HOPR,DBF_FLOAT) { + prompt("High Operating Range") + promptgroup("80 - Display") + interest(1) + } + field(LOPR,DBF_FLOAT) { + prompt("Low Operating Range") + promptgroup("80 - Display") + interest(1) + } + field(HIHI,DBF_FLOAT) { + prompt("Hihi Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + } + field(LOLO,DBF_FLOAT) { + prompt("Lolo Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + } + field(HIGH,DBF_FLOAT) { + prompt("High Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + } + field(LOW,DBF_FLOAT) { + prompt("Low Alarm Limit") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + } + field(HHSV,DBF_MENU) { + prompt("Hihi Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(LLSV,DBF_MENU) { + prompt("Lolo Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(HSV,DBF_MENU) { + prompt("High Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + menu(menuAlarmSevr) + } + field(LSV,DBF_MENU) { + prompt("Low Severity") + promptgroup("70 - Alarm") + pp(TRUE) + interest(1) + 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/test/ioc/db/dbPutLinkTest.c b/modules/database/test/ioc/db/dbPutLinkTest.c index 2b7d3a518..a53214f6d 100644 --- a/modules/database/test/ioc/db/dbPutLinkTest.c +++ b/modules/database/test/ioc/db/dbPutLinkTest.c @@ -92,7 +92,7 @@ static void testLinkParse(void) for (;td->str; td++) { int i, N; testDiag("Parsing \"%s\"", td->str); - testOk(dbParseLink(td->str, DBF_INLINK, &info) == 0, "Parser returned OK"); + testOk(dbParseLink(td->str, DBF_INLINK, &info, "dummy","INP") == 0, "Parser returned OK"); if (!testOk(info.ltype == td->info.ltype, "Link type value")) testDiag("Expected %d, got %d", td->info.ltype, info.ltype); if (td->info.target && info.target) @@ -121,6 +121,16 @@ static void testLinkParse(void) dbFreeLinkInfo(&info); } + info.modifiers |= pvlOptCPP; + dbParseLink("something CPP", DBF_OUTLINK, &info, "dummy","OUT"); + testOk(info.modifiers == 0, "CPP modifier was discarded"); + dbFreeLinkInfo(&info); + + info.modifiers |= pvlOptCP; + dbParseLink("something CP", DBF_OUTLINK, &info, "dummy","OUT"); + testOk(info.modifiers == 0, "CP modifier was discarded"); + dbFreeLinkInfo(&info); + testIocShutdownOk(); testdbCleanup(); @@ -155,7 +165,7 @@ static void testLinkFailParse(void) eltc(1); for(;*td; td++) { - testOk(dbParseLink(*td, DBF_INLINK, &info) == S_dbLib_badField, + testOk(dbParseLink(*td, DBF_INLINK, &info, "dummy","INP") == S_dbLib_badField, "dbParseLink correctly rejected \"%s\"", *td); } @@ -705,7 +715,7 @@ void testTSEL(void) MAIN(dbPutLinkTest) { - testPlan(352); + testPlan(354); testLinkParse(); testLinkFailParse(); testCADBSet(); diff --git a/modules/database/test/ioc/dbtemplate/msi.plt b/modules/database/test/ioc/dbtemplate/msi.plt index 09835d3ac..e8b9bb900 100644 --- a/modules/database/test/ioc/dbtemplate/msi.plt +++ b/modules/database/test/ioc/dbtemplate/msi.plt @@ -57,10 +57,10 @@ ok(msi('-I. -I.. -S ../t12-substitute.txt'), slurp('../t12-result.txt')); delete @ENV{ keys %envs }; # Not really needed # Substitution file, relative path includes -ok(msi('-I @TOP@/modules -S ../t13-substitute.txt'), slurp('../t13-result.txt')); +ok(msi('-I ../.. -S ../t13-substitute.txt'), slurp('../t13-result.txt')); # Template file, relative path includes -ok(msi('-I @TOP@/modules ../t14-template.txt'), slurp('../t14-result.txt')); +ok(msi('-I ../.. ../t14-template.txt'), slurp('../t14-result.txt')); # Test support routines diff --git a/modules/database/test/ioc/dbtemplate/t13-substitute.txt b/modules/database/test/ioc/dbtemplate/t13-substitute.txt index 63d3fca1f..5890df7f3 100644 --- a/modules/database/test/ioc/dbtemplate/t13-substitute.txt +++ b/modules/database/test/ioc/dbtemplate/t13-substitute.txt @@ -1,3 +1,3 @@ -file database/test/ioc/dbtemplate/t13-template.txt { +file dbtemplate/t13-template.txt { { a=foo } } diff --git a/modules/database/test/ioc/dbtemplate/t14-template.txt b/modules/database/test/ioc/dbtemplate/t14-template.txt index 276663f7a..b1de6175a 100644 --- a/modules/database/test/ioc/dbtemplate/t14-template.txt +++ b/modules/database/test/ioc/dbtemplate/t14-template.txt @@ -1,5 +1,5 @@ This is t14-template.txt -include "database/test/ioc/dbtemplate/t14-include.txt" +include "dbtemplate/t14-include.txt" End of t14-template.txt diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile index d6aaf44d3..c5eca0dbe 100644 --- a/modules/database/test/std/rec/Makefile +++ b/modules/database/test/std/rec/Makefile @@ -107,6 +107,13 @@ testHarness_SRCS += boTest.c TESTFILES += ../boTest.db TESTS += boTest +TESTPROD_HOST += dfanoutTest +dfanoutTest_SRCS += dfanoutTest.c +dfanoutTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += dfanoutTest.c +TESTFILES += ../dfanoutTest.db +TESTS += dfanoutTest + TESTPROD_HOST += biTest biTest_SRCS += biTest.c biTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp diff --git a/modules/database/test/std/rec/dfanoutTest.c b/modules/database/test/std/rec/dfanoutTest.c new file mode 100644 index 000000000..14597868c --- /dev/null +++ b/modules/database/test/std/rec/dfanoutTest.c @@ -0,0 +1,182 @@ +/*************************************************************************\ +* Copyright (c) 2023 Marco Montevechi Filho +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "dbUnitTest.h" +#include "testMain.h" +#include "errlog.h" +#include "dbAccess.h" +#include "menuIvoa.h" +#include "epicsThread.h" +#include "epicsMath.h" +#include "dfanoutRecord.h" + +static const char *dfanout_OUT_pvs[] = { + "test_dfanout_record.OUTA", "test_dfanout_record.OUTB", + "test_dfanout_record.OUTC", "test_dfanout_record.OUTD", + "test_dfanout_record.OUTE", "test_dfanout_record.OUTF", + "test_dfanout_record.OUTG", "test_dfanout_record.OUTH", + "test_dfanout_record.OUTI", "test_dfanout_record.OUTJ", + "test_dfanout_record.OUTK", "test_dfanout_record.OUTL", + "test_dfanout_record.OUTM", "test_dfanout_record.OUTN", + "test_dfanout_record.OUTO", "test_dfanout_record.OUTP"}; + +static const char *dfanout_receivers[] = { + "test_dfanout_outa", "test_dfanout_outb", + "test_dfanout_outc", "test_dfanout_outd", + "test_dfanout_oute", "test_dfanout_outf", + "test_dfanout_outg", "test_dfanout_outh", + "test_dfanout_outi", "test_dfanout_outj", + "test_dfanout_outk", "test_dfanout_outl", + "test_dfanout_outm", "test_dfanout_outn", + "test_dfanout_outo", "test_dfanout_outp"}; + +void recTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static void test_all(double val, int exception){ + + // if mask == -1 then it tests all. + int i; + for (i = 0; i < NELEMENTS(dfanout_receivers); ++i) { + if ( i == exception) continue; + testdbGetFieldEqual(dfanout_receivers[i], DBF_DOUBLE, val); + } +} + +static void test_all_output(void){ + + /* set output fields */ + int i; + for (i = 0; i < NELEMENTS(dfanout_OUT_pvs); ++i) { + testdbPutFieldOk(dfanout_OUT_pvs[i], DBF_STRING, dfanout_receivers[i]); + } + + /* set VAL from src to any random number */ + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 5); + + /* verify that OUT records are updated */ + test_all(5, -1); + +} + +static void test_selm_specified() { + + /* Resetting values */ + testDiag("Testing Specified"); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 0); + test_all(0, -1); + + testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "Specified"); + testdbPutFieldOk("test_dfanout_record.SELN", DBF_LONG, 0); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 10); + test_all(0, -1); + + int val; + for (val = 0; val < NELEMENTS(dfanout_receivers); ++val) { + testdbPutFieldOk("test_dfanout_record.SELN", DBF_LONG, val + 1); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, val + 1); + + testdbGetFieldEqual(dfanout_receivers[val], DBF_LONG, val + 1); + + int not_seln, not_seln_val; + for (not_seln = 0; not_seln < NELEMENTS(dfanout_receivers); ++not_seln) { + if (not_seln == val) continue; + if (not_seln < val) { // If record has already been tested, expected value is index + 1 + not_seln_val = not_seln + 1; + } else { // If record hasn't been tested yet, expected value is 0 + not_seln_val = 0; + } + testdbGetFieldEqual(dfanout_receivers[not_seln], DBF_LONG, not_seln_val); + } + + } + +} + +static void test_selm_mask() { + + /* Resetting values */ + testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "All"); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 0); + test_all(0, -1); + + /* Testing all 65535 possible masks seems a bit excessive -- just test some patterns */ + epicsUInt32 mask = 0x100055aa; + while (1) { + testDiag("Testing mask 0x%04x", mask); + /* Resets values. Tests if fields in bitmask have been set */ + testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "All"); //Setting all values to 1 so we know what to compare with. + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 1); + + testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "Mask"); + testdbPutFieldOk("test_dfanout_record.SELN", DBF_LONG, mask); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 9); + + int item; + for (item = 0; item < NELEMENTS(dfanout_receivers); ++item) { + + if ( mask & (1 << item) ) { // If i represents a set bit in the bitmask + testdbGetFieldEqual(dfanout_receivers[item], DBF_LONG, 9); + } else { + testdbGetFieldEqual(dfanout_receivers[item], DBF_LONG, 1); + } + + } + if (!mask) break; + mask >>= 1; + } +} + +static void test_ivoa() { + + + testDiag("Testing IVOA = Continue normally"); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_LONG, 1); + testdbPutFieldOk("test_dfanout_record.IVOA", DBF_STRING, "Continue normally"); + testdbPutFieldOk("test_dfanout_record.SELM", DBF_STRING, "All"); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_DOUBLE, epicsNAN); + test_all(epicsNAN, -1); + testdbGetFieldEqual("test_dfanout_record.VAL", DBF_DOUBLE, epicsNAN); + + testDiag("Testing IVOA = Don't drive outputs"); + testdbPutFieldOk("test_dfanout_record.IVOA", DBF_STRING, "Don't drive outputs"); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_DOUBLE, 1.2345); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_DOUBLE, epicsNAN); + test_all(1.2345, -1); + testdbGetFieldEqual("test_dfanout_record.VAL", DBF_DOUBLE, epicsNAN); + + testDiag("Testing IVOA = Set output to IVOV"); + testdbPutFieldOk("test_dfanout_record.IVOA", DBF_STRING, "Set output to IVOV"); + testdbPutFieldOk("test_dfanout_record.IVOV", DBF_DOUBLE, 3.1415); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_DOUBLE, 42); + testdbPutFieldOk("test_dfanout_src.VAL", DBF_DOUBLE, epicsNAN); + test_all(3.1415, -1); + testdbGetFieldEqual("test_dfanout_record.VAL", DBF_DOUBLE, 3.1415); +} + +MAIN(dfanoutTest) { + + testPlan(1067); + + testdbPrepare(); + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + recTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("dfanoutTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + test_all_output(); + test_selm_specified(); + test_selm_mask(); + test_ivoa(); + + testIocShutdownOk(); + testdbCleanup(); + + return testDone(); +} diff --git a/modules/database/test/std/rec/dfanoutTest.db b/modules/database/test/std/rec/dfanoutTest.db new file mode 100644 index 000000000..ebff7fb25 --- /dev/null +++ b/modules/database/test/std/rec/dfanoutTest.db @@ -0,0 +1,56 @@ +record(ao, "test_dfanout_src") { + field(FLNK, "test_dfanout_record") +} + +record(dfanout, "test_dfanout_record") { + field(OMSL, "closed_loop") + field(DOL, "test_dfanout_src") +} + +record(ai, "test_dfanout_outa") { +} + +record(ai, "test_dfanout_outb") { +} + +record(ai, "test_dfanout_outc") { +} + +record(ai, "test_dfanout_outd") { +} + +record(ai, "test_dfanout_oute") { +} + +record(ai, "test_dfanout_outf") { +} + +record(ai, "test_dfanout_outg") { +} + +record(ai, "test_dfanout_outh") { +} + +record(ai, "test_dfanout_outi") { +} + +record(ai, "test_dfanout_outj") { +} + +record(ai, "test_dfanout_outk") { +} + +record(ai, "test_dfanout_outl") { +} + +record(ai, "test_dfanout_outm") { +} + +record(ai, "test_dfanout_outn") { +} + +record(ai, "test_dfanout_outo") { +} + +record(ai, "test_dfanout_outp") { +} diff --git a/modules/libcom/src/calc/postfix.h b/modules/libcom/src/calc/postfix.h index 90d34e1cf..1e945252c 100644 --- a/modules/libcom/src/calc/postfix.h +++ b/modules/libcom/src/calc/postfix.h @@ -22,7 +22,10 @@ #include "libComAPI.h" -/** \brief Number of input arguments to a calc expression (A-U) */ +/** \brief Number of input arguments to a calc expression (A-U) + * + * Since UNRELEASED the number of inputs has been increased from 12 to 21. + */ #define CALCPERFORM_NARGS 21 /** \brief Size of the internal partial result stack */ #define CALCPERFORM_STACK 80 @@ -41,20 +44,18 @@ * few bytes smaller for some sizes. * * The maximum expansion from infix to postfix is for the sub-expression - \code - .1?.1: -\endcode - * which is 6 characters long and results in 21 bytes of postfix: -\code + * .1?.1: which is 6 characters long and results in 21 bytes of + * postfix: +\verbatim .1 => LITERAL_DOUBLE + 8 byte value ? => COND_IF .1 => LITERAL_DOUBLE + 8 byte value : => COND_ELSE ... => COND_END -\endcode +\endverbatim * For other short expressions the factor 21/6 always gives a big enough - * postfix buffer (proven by hand, look at '1+' and '.1+' as well). + * postfix buffer (proven by hand, look at \c 1+ and .1+ as well). */ #define INFIX_TO_POSTFIX_SIZE(n) ((n)*21/6) @@ -115,193 +116,207 @@ extern "C" { /** \brief Compile an infix expression into postfix byte-code * - * Converts an expression from an infix string to postfix byte-code + * Converts an expression from an infix string to postfix byte-code. * * \param pinfix Pointer to the infix string * \param ppostfix Pointer to the postfix buffer * \param perror Place to return an error code * \return Non-zero value in event of error * - * It is the caller's responsibility to ensure that \c ppostfix points - * to sufficient storage to hold the postfix expression. The macro - * INFIX_TO_POSTFIX_SIZE(n) can be used to calculate an appropriate - * postfix buffer size from the length of the infix buffer. + * It is the caller's responsibility to ensure that \p ppostfix points to + * sufficient storage to hold the postfix expression. + * The macro INFIX_TO_POSTFIX_SIZE(n) can be used to calculate an + * appropriate postfix buffer size from the length of the infix buffer. + * The macro's parameter \p n must count the terminating nil byte too. * - * \note "n" must count the terminating nil byte too. + * -# 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 expression 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 simplest expression element is a **numeric literal,** any + * (positive) number expressed using the standard floating point syntax + * that can be stored as a double precision value. + * This now includes the values Infinity and NaN (not a number). + * Note that negative numbers will be encoded as a positive literal, to + * which the unary negate operator is applied. * - * -# 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. + * Examples: + * - \c 1 + * - \c 2.718281828459 + * - \c Inf * - * -# ***Numeric Literals*** - * The simplest expression element is a numeric literal, any (positive) - * number expressed using the standard floating point syntax that can be stored - * as a double precision value. This now includes the values Infinity and - * NaN (not a number). Note that negative numbers will be encoded as a - * positive literal to which the unary negate operator is applied. - * - * - Examples: - * - 1 - * - 2.718281828459 - * - Inf - * - * -# ***Constants*** - * There are three trigonometric constants available to any expression + * -# There are three **trigonometric constants** available to any expression * which return a value: - * - pi returns the value of the mathematical constant pi. - * - D2R evaluates to pi/180 which, when used as a multiplier, - * converts an angle from degrees to radians. - * - R2D evaluates to 180/pi which as a multiplier converts an angle - * from radians to degrees. + * - \c pi returns the value of the mathematical constant pi. + * - \c D2R evaluates to pi/180 which, when used as a multiplier, + * converts an angle from degrees to radians. + * - \c R2D evaluates to 180/pi which as a multiplier converts an + * angle from radians to degrees. * - * -# ***Variables*** - * Variables are used to provide inputs to an expression, and are named - * using the single letters A through U inclusive or the keyword VAL which - * refers to the previous result of this calculation. The software that - * makes use of the expression evaluation code should document how the - * individual variables are given values; for the calc record type the input - * links INPA through INPU can be used to obtain these from other record fields, - * and VAL refers to the the VAL field (which can be overwritten from outside - * the record via Channel Access or a database link). + * -# **Variables** are used to provide inputs to an expression, and are + * named using the single letters \c A through \c U inclusive or the + * keyword \c VAL which refers to the previous result of this + * calculation. + * The software that makes use of the expression evaluation code should + * document how the individual variables are given values. + * For the calc and calcout record types the input links \c INPA through + * \c INPU can be used to obtain values from other record fields, and \c + * VAL refers to the the VAL field (which can be overwritten from + * outside the record via Channel Access or a database link). * - * -# ***Variable Assignment Operator*** - * Recently added is the ability to assign the result of a sub-expression to - * any of the single letter variables, which can then be used in another - * sub-expression. The variable assignment operator is the character pair - * := and must immediately follow the name of the variable to receive the - * expression value. Since the infix string must return exactly one value, every + * -# The **Variable Assignment Operator** was added in 3.14.9 and + * provides the ability to assign the result of a sub-expression to any + * of the single letter variables, which can then be used in later + * sub-expressions. + * The variable assignment operator is the character pair := and + * must immediately follow the name of the variable to receive the + * expression value. + * Since the infix string must return exactly one value, every * expression string must have exactly one sub-expression that is not an - * assignment, which can appear anywhere in the string. Sub-expressions within - * the string are separated by a semi-colon character. + * assignment, which can appear anywhere in the string. + * Sub-expressions within the string are separated by a semi-colon + * character ; . * - * - Examples: - * - B; B:=A - * - i:=i+1; a*sin(i*D2R) + * Examples: + * - B; B:=A + * - i:=i+1; a*sin(i*D2R) * - * -# ***Arithmetic Operators*** - * The usual binary arithmetic operators are provided: + - * and / with their - * usual relative precedence and left-to-right associativity, and - may also - * be used as a unary negate operator where it has a higher precedence and - * associates from right to left. There is no unary plus operator, so numeric - * literals cannot begin with a + sign. + * -# The standard binary **Arithmetic Operators** are provided: + - + * * and \c / with their usual relative precedence and + * left-to-right associativity. + * A minus sign \c - may also be used as a unary negate operator where + * it has a higher precedence and associates from right to left. + * There is no unary plus operator, so numeric literals cannot begin + * with a plus sign \c + . * - * - Examples: - * - a*b + c - * - a/-4 - b + * Examples: + * - a*b + c + * - a/-4 - b * - * Three other binary operators are also provided: % is the integer modulo operator, - * while the synonymous operators ** and ^ raise their left operand to the power of - * the right operand. % has the same precedence and associativity as * and /, while - * the power operators associate left-to-right and have a precedence in between * and - * unary minus. + * Three other binary operators are also provided: + * \c % is the integer modulo operator, while the synonymous operators + * \c ** and \c ^ raise their left operand to the power of the right + * operand. + * \c % has the same precedence and associativity as \c * and \c /, + * while the power operators associate left-to-right and have a + * precedence in between \c * and unary minus \c - . * - * - Examples: - * - e:=a%10 - * - d:=a/10%10 - * - c:=a/100%10 - * - b:=a/1000%10 - * - b*4096+c*256+d*16+e - * - sqrt(a**2 + b**2) + * Examples: + * - e:=a%10 + * - d:=a/10%10 + * - c:=a/100%10 + * - b:=a/1000%10 + * - b*4096+c*256+d*16+e + * - sqrt(a**2 + b**2) * - * -# ***Algebraic Functions*** - * Various algebraic functions are available which take parameters inside - * parentheses. The parameter separator is a comma. + * -# Various **Algebraic Functions** are available which take parameters + * inside parentheses. + * The parameter separator is a comma , . * - * - Absolute value: abs(a) - * - Exponential ea: exp(a) - * - Logarithm, base 10: log(a) - * - Natural logarithm (base e): ln(a) or loge(a) - * - n parameter maximum value: max(a, b, ...) - * - n parameter minimum value: min(a, b, ...) - * - Square root: sqr(a) or sqrt(a) - * - Floating point modulo: fmod(num, den) - * \since The fmod() function was added in 7.0.8 + * - Absolute value: \c abs(a) + * - Exponential ea: \c exp(a) + * - Logarithm, base 10: \c log(a) + * - Natural logarithm (base e): \c ln(a) or \c loge(a) + * - n parameter maximum value: max(a, b, ...) + * - n parameter minimum value: min(a, b, ...) + * - Square root: \c sqr(a) or \c sqrt(a) + * - Floating point modulo: fmod(num, den) + *
The \c fmod() function was added in 7.0.8 * - * -# ***Trigonometric Functions*** - * Standard circular trigonometric functions, with angles expressed in radians: - * - Sine: sin(a) - * - Cosine: cos(a) - * - Tangent: tan(a) - * - Arcsine: asin(a) - * - Arccosine: acos(a) - * - Arctangent: atan(a) - * - 2 parameter arctangent: atan2(a, b) - * \note Note that these arguments are the reverse of the ANSI C function, - * so while C would return arctan(a/b) the calc expression engine returns arctan(b/a) + * -# Standard circular **Trigonometric Functions** exist with angles + * expressed in radians: * - * -# ***Hyperbolic Trigonometry*** - * The basic hyperbolic functions are provided, but no inverse functions - * (which are not provided by the ANSI C math library either). - * - Hyperbolic sine: sinh(a) - * - Hyperbolic cosine: cosh(a) - * - Hyperbolic tangent: tanh(a) + * - Sine: \c sin(a) + * - Cosine: \c cos(a) + * - Tangent: \c tan(a) + * - Arcsine: \c asin(a) + * - Arccosine: \c acos(a) + * - Arctangent: \c atan(a) + * - 2 parameter arctangent: atan2(a, b) + *
Note that the \c atan2 arguments are the reverse of the ANSI C + * function, so while C would return \c arctan(a/b) the calc + * expression engine returns \c arctan(b/a) * - * -# ***Numeric Functions*** - * The numeric functions perform operations related to the floating point - * numeric representation and truncation or rounding. - * - Round up to next integer: ceil(a) - * - Round down to next integer: floor(a) - * - Round to nearest integer: nint(a) - * - Test for infinite result: isinf(a) - * - Test for any non-numeric values: isnan(a, ...) - * - Test for all finite, numeric values: finite(a, ...) - * - Random number between 0 and 1: rndm + * -# The basic **Hyperbolic Trigonometry** functions are provided, but + * no inverse functions (which aren't provided by the ANSI C math + * library either). * - * -# ***Boolean Operators*** - * These operators regard their arguments as true or false, where 0.0 is - * false and any other value is true. + * - Hyperbolic sine: \c sinh(a) + * - Hyperbolic cosine: \c cosh(a) + * - Hyperbolic tangent: \c tanh(a) * - * - Boolean and: a && b - * - Boolean or: a || b - * - Boolean not: !a + * -# These **Numeric Functions** perform operations related to the + * floating point numeric representation and truncation or rounding. * - * -# ***Bitwise Operators*** - * Most bitwise operators convert their arguments to 32-bit signed integer (by - * truncation), perform the appropriate bitwise operation, then convert back - * to a floating point value. The arithmetic right shift operator >> thus - * retains the sign bit of the left-hand argument. The logical right shift - * operator >>> is performed on an unsigned integer though, so injects zeros - * while shifting. The right-hand shift argument is masked so only the lower - * 5 bits are used. Unlike in C, ^ is not a bitwise exclusive-or operator. + * - Round up to next integer: \c ceil(a) + * - Round down to next integer: \c floor(a) + * - Round to nearest integer: \c nint(a) + * - Test for infinite result: \c isinf(a) + * - Test for any non-numeric values: isnan(a, ...) + * - Test for all finite, numeric values: finite(a, ...) + * - Random number between 0 and 1: \c rndm * - * - Bitwise and: a & b or a and b - * - Bitwise or: a | b or a or b - * - Bitwise exclusive or: a xor b - * - Bitwise not (ones complement): ~a or not a - * - Arithmetic left shift: a << b - * - Arithmetic right shift: a >> b - * - Logical right shift: a >>> b + * -# The **Boolean Operators** evaluate their arguments as true or + * false, where \c 0.0 is false and any other value is true. * - * -# ***Relational Operators*** - * Standard numeric comparisons between two values: + * - Boolean and: a && b + * - Boolean or: a || b + * - Boolean not: \c !a * - * - Less than: a < b - * - Less than or equal to: a <= b - * - Equal to: a = b or a == b - * - Greater than or equal to: a >= b - * - Greater than: a > b - * - Not equal to: a != b or a # b + * -# Most **Bitwise Operators** convert their arguments to 32-bit signed + * integer (by truncation), perform the appropriate bitwise operation, + * then convert back to a floating point value. + * The arithmetic right shift operator \c >> thus retains the sign bit of + * the left-hand argument. + * The logical right shift operator \c >>> is performed on an unsigned + * integer though, so it injects zeros while shifting. + * The right-hand shift argument is masked so only the lower 5 bits are + * used. + * Unlike in C, \c ^ is not a bitwise exclusive-or operator. * - * -# ***Conditional Operator*** - * Expressions can use the C conditional operator, which has a lower - * precedence than all of the other operators except for the assignment operator. + * - Bitwise and: a & b or a and b + * - Bitwise or: a | b or a or b + * - Bitwise exclusive or: a xor b + * - Bitwise not (ones complement): ~a or not a + * - Arithmetic left shift: a << b + * - Arithmetic right shift: a >> b + * - Logical right shift: a >>> b * - * - condition ? true result : false result - * - Example: - * - a < 360 ? a+1 : 0 + * -# The **Relational Operators** perform numeric comparisons between + * two double-precision values: * - * -# ***Parentheses*** - * Sub-expressions can be placed within parentheses to override operator presence rules. - * Parentheses can be nested to any depth, but the intermediate value stack used by - * the expression evaluation engine is limited to 80 results (which require an - * expression at least 321 characters long to reach). + * - Less than: a < b + * - Less than or equal to: a <= b + * - Equal to: a = b or a == b + * - Greater than or equal to: a >= b + * - Greater than: a > b + * - Not equal to: a != b or a # b + * + * -# Expressions can use the C **Conditional Operator**, which has a + * lower precedence than all of the other operators except for the + * assignment operator. + * + * - \a condition ? \a true-expression : + * \a false-expression + * - Example: + * a < 360 ? a+1 : 0 + * + * -# Sub-expressions can be placed within **Parentheses** () + * to override operator presence rules. + * Parentheses can be nested to any depth, but the intermediate value + * stack used by the expression evaluation engine is limited to 80 + * results (which takes an expression at least 321 characters long to + * reach). */ LIBCOM_API long postfix(const char *pinfix, char *ppostfix, short *perror); @@ -310,10 +325,12 @@ LIBCOM_API long * * Evaluates the postfix expression against a set ot input values. * - * \param parg Pointer to an array of double values for the arguments A-U - * that can appear in the expression. Note that the argument values may be - * modified if the expression uses the assignment operator. - * \param presult Where to put the calculated result, which may be a NaN or Infinity. + * \param parg Pointer to an array of double values for the arguments + * \c A-U that can appear in the expression. + * Note that the argument values may be modified if the expression uses + * the assignment operator. + * \param presult Where to put the calculated result, which may be a NaN + * or Infinity. * \param ppostfix The postfix expression created by postfix(). * \return Status value 0 for OK, or non-zero if an error is discovered * during the evaluation process. diff --git a/modules/libcom/src/env/envDefs.h b/modules/libcom/src/env/envDefs.h index d59840285..aaed59ee5 100644 --- a/modules/libcom/src/env/envDefs.h +++ b/modules/libcom/src/env/envDefs.h @@ -78,6 +78,12 @@ LIBCOM_API extern const ENV_PARAM IOCSH_HISTSIZE; LIBCOM_API extern const ENV_PARAM IOCSH_HISTEDIT_DISABLE; LIBCOM_API extern const ENV_PARAM EPICS_MUTEX_USE_PRIORITY_INHERITANCE; LIBCOM_API extern const ENV_PARAM EPICS_ABORT_ON_ASSERT; +/** @brief List of all parameters. + * + * A NULL terminated array of all ENV_PARAM known to EPICS Base. + * This array is assembled during the EPICS Base build, and + * contains at least the preceeding parameters. + */ LIBCOM_API extern const ENV_PARAM *env_param_list[]; struct in_addr; diff --git a/modules/libcom/src/fdmgr/fdManager.cpp b/modules/libcom/src/fdmgr/fdManager.cpp index 9e133d2ad..e25a0f787 100644 --- a/modules/libcom/src/fdmgr/fdManager.cpp +++ b/modules/libcom/src/fdmgr/fdManager.cpp @@ -187,19 +187,12 @@ LIBCOM_API void fdManager::process(double delay) ++ioPending; #ifdef FDMGR_USE_POLL -#if __cplusplus >= 201100L - priv->pollfds.emplace_back(pollfd{ - .fd = iter->getFD(), - .events = WIN_POLLEVENT_FILTER(PollEvents[iter->getType()]) - }); -#else struct pollfd pollfd; pollfd.fd = iter->getFD(); pollfd.events = WIN_POLLEVENT_FILTER(PollEvents[iter->getType()]); pollfd.revents = 0; priv->pollfds.push_back(pollfd); #endif -#endif #ifdef FDMGR_USE_SELECT FD_SET(iter->getFD(), &priv->fdSets[iter->getType()]); diff --git a/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h b/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h index 42723b97c..80b72012f 100644 --- a/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h +++ b/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h @@ -45,7 +45,11 @@ /* * Enable format-string checking if possible */ +#if __GNUC__ * 100 + __GNUC_MINOR__ >= 404 +#define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__gnu_printf__,f,a))) +#else #define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__printf__,f,a))) +#endif /* * Deprecation marker diff --git a/modules/libcom/src/osi/epicsTime.h b/modules/libcom/src/osi/epicsTime.h index 6a4da6dc7..bb75dd56b 100644 --- a/modules/libcom/src/osi/epicsTime.h +++ b/modules/libcom/src/osi/epicsTime.h @@ -329,7 +329,7 @@ public: /** \brief The default constructor sets the time to the EPICS epoch. */ #if __cplusplus>=201103L - constexpr epicsTime() :ts{} {} + constexpr epicsTime() :ts{0, 0} {} #else epicsTime () { ts.secPastEpoch = ts.nsec = 0u; diff --git a/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c b/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c index 7ccdc878d..fd55fa005 100644 --- a/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c +++ b/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c @@ -74,6 +74,22 @@ LIBCOM_API void epicsStdCall epicsMessageQueueDestroy( free(id); } +LIBCOM_API int epicsStdCall epicsMessageQueueSend( + epicsMessageQueueId id, + void *message, + unsigned int messageSize) +{ + return mq_send(id->id, (const char*)message, messageSize, 0); +} + +LIBCOM_API int epicsStdCall epicsMessageQueueReceive( + epicsMessageQueueId id, + void *message, + unsigned int messageSize) +{ + return mq_receive(id->id, (char*)message, messageSize, NULL); +} + LIBCOM_API int epicsStdCall epicsMessageQueueTrySend( epicsMessageQueueId id, diff --git a/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.h b/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.h index 95e66e73e..a3fc30bd4 100644 --- a/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.h +++ b/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.h @@ -23,6 +23,4 @@ struct epicsMessageQueueOSD { mqd_t id; char name[24]; }; -#define epicsMessageQueueSend(q,m,l) (mq_send((q)->id, (const char*)(m), (l), 0)) -#define epicsMessageQueueReceive(q,m,s) (mq_receive((q)->id, (char*)(m), (s), NULL)) diff --git a/modules/libcom/src/osi/os/default/gnuReadline.c b/modules/libcom/src/osi/os/default/gnuReadline.c index a4a322569..78b16f6cc 100644 --- a/modules/libcom/src/osi/os/default/gnuReadline.c +++ b/modules/libcom/src/osi/os/default/gnuReadline.c @@ -16,6 +16,7 @@ #include #include +#include "errlog.h" #include "epicsExit.h" #include "envDefs.h" #include "epicsReadlinePvt.h" @@ -78,7 +79,7 @@ osdReadline (const char *prompt, struct readlineContext *context) line = malloc(linesize); if (line == NULL) { - printf("Out of memory!\n"); + fprintf(stderr, ERL_ERROR " osdReadline() Out of memory!\n"); return NULL; } if (prompt) { @@ -98,7 +99,7 @@ osdReadline (const char *prompt, struct readlineContext *context) linesize = linelen + 50; cp = (char *)realloc(line, linesize); if (cp == NULL) { - printf ("Out of memory!\n"); + fprintf(stderr, ERL_ERROR " osdReadline() Out of memory!\n"); free(line); line = NULL; break; diff --git a/modules/libcom/src/osi/os/posix/osdSock.c b/modules/libcom/src/osi/os/posix/osdSock.c index 0d873e628..41f6580f0 100644 --- a/modules/libcom/src/osi/os/posix/osdSock.c +++ b/modules/libcom/src/osi/os/posix/osdSock.c @@ -41,6 +41,13 @@ # define SOCK_CLOEXEC (0) #endif +#if defined(AI_PASSIVE) && !defined(__rtems__) +# define USE_INFO +#else +# define USE_BY +#endif + +#ifdef USE_BY /* * Protect some routines which are not thread-safe */ @@ -60,7 +67,7 @@ static void unlockInfo (void) { epicsMutexUnlock (infoMutex); } - +#endif static size_t nAttached; @@ -164,6 +171,7 @@ LIBCOM_API void epicsStdCall epicsSocketDestroy ( SOCKET s ) } } +#ifdef USE_BY /* * ipAddrToHostName * On many systems, gethostbyaddr must be protected by a @@ -214,6 +222,53 @@ LIBCOM_API int epicsStdCall hostToIPAddr unlockInfo (); return ret; } +#endif +#ifdef USE_INFO +unsigned epicsStdCall ipAddrToHostName(const struct in_addr *pAddr, char *pBuf, unsigned bufSize) +{ + osiSockAddr query; + if(!bufSize) + return 0; // non-sense + + memset(&query, 0, sizeof(query)); + query.ia.sin_family = AF_INET; + query.ia.sin_addr = *pAddr; + + int ret = getnameinfo(&query.sa, sizeof(query), pBuf, bufSize, NULL, 0, NI_NAMEREQD); + if(ret==0) { + ret = strlen (pBuf); + + } else { // lookup fails + ret = 0; // indicate failure to caller + } + return ret; +} + +int epicsStdCall hostToIPAddr(const char *pHostName, struct in_addr *pIPA) +{ + struct addrinfo hint, *result = NULL; + + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_INET; + + int ret = getaddrinfo(pHostName, NULL, &hint, &result); + if(ret==0) { + const struct addrinfo *ai; + ret = -1; + for(ai = result; ai; ai = ai->ai_next) { + assert(ai->ai_family==AF_INET); // ensured by hint + const struct sockaddr_in *answer = (const struct sockaddr_in*)ai->ai_addr; + *pIPA = answer->sin_addr; + ret = 0; + break; + } + } + if(result) { + freeaddrinfo(result); + } + return ret; +} +#endif diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index 1c3910591..88ffbd0ab 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -473,16 +473,16 @@ void epicsThreadRealtimeLock(void) #ifdef __linux__ case ENOMEM: fprintf(stderr, "epicsThreadRealtimeLock " - "Warning: unable to lock memory. RLIMIT_MEMLOCK is too small or missing CAP_IPC_LOCK\n"); + ERL_WARNING ": unable to lock memory. RLIMIT_MEMLOCK is too small or missing CAP_IPC_LOCK\n"); break; case EPERM: fprintf(stderr, "epicsThreadRealtimeLock " - "Warning: unable to lock memory. missing CAP_IPC_LOCK\n"); + ERL_WARNING ": unable to lock memory. missing CAP_IPC_LOCK\n"); break; #endif default: fprintf(stderr, "epicsThreadRealtimeLock " - "Warning: Unable to lock the virtual address space.\n" + ERL_WARNING ": Unable to lock the virtual address space.\n" "VM page faults may harm real-time performance. errno=%d\n", err); } @@ -666,6 +666,8 @@ static epicsThreadOSD *createImplicit(void) pthreadInfo->tid = tid; pthreadInfo->osiPriority = 0; pthreadInfo->isOkToBlock = 1; + status = pthread_attr_init(&pthreadInfo->attr); + checkStatusOnce(status,"pthread_attr_init"); #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) && _POSIX_THREAD_PRIORITY_SCHEDULING > 0 if(pthread_getschedparam(tid,&pthreadInfo->schedPolicy,&pthreadInfo->schedParam) == 0) { diff --git a/modules/libcom/test/fdManagerTest.cpp b/modules/libcom/test/fdManagerTest.cpp index 0dc1b330c..ae9f5f79b 100644 --- a/modules/libcom/test/fdManagerTest.cpp +++ b/modules/libcom/test/fdManagerTest.cpp @@ -216,7 +216,8 @@ void testOnlyTimer() epicsTime now(epicsTime::getCurrent()); trig_timer.timer.start(trig, now+0.1); never_timer.timer.start(never, now+9999999.0); - mgr.process(0.2); + for(unsigned i=0; i<10 && !trig.expired; i++) + mgr.process(0.2); testOk1(trig.expired); testOk1(!never.expired); } diff --git a/src/template/base/top/.gitignore b/src/template/base/top/.gitignore index a3960f2d2..8b9a1c9c0 100644 --- a/src/template/base/top/.gitignore +++ b/src/template/base/top/.gitignore @@ -2,6 +2,9 @@ # # SPDX-License-Identifier: EPICS +# Before adding patterns here, please read the gitignore +# documentation at https://git-scm.com/docs/gitignore + # Install directories /bin/ /cfg/ @@ -28,9 +31,4 @@ O.*/ # Common files created by other tools -/QtC-* -/.vscode/ -*.orig -*.log -.*.swp .DS_Store diff --git a/src/template/ext/top/.gitignore b/src/template/ext/top/.gitignore index 51edcf53e..febbdf4d6 100644 --- a/src/template/ext/top/.gitignore +++ b/src/template/ext/top/.gitignore @@ -2,6 +2,9 @@ # # SPDX-License-Identifier: EPICS +# Before adding patterns here, please read the gitignore +# documentation at https://git-scm.com/docs/gitignore + # Install directories /bin/ /cfg/ @@ -19,9 +22,4 @@ O.*/ # Common files created by other tools -/QtC-* -/.vscode/ -*.orig -*.log -.*.swp .DS_Store diff --git a/src/tools/EpicsHostArch.pl b/src/tools/EpicsHostArch.pl index dee1ffe03..bb3a0250c 100644 --- a/src/tools/EpicsHostArch.pl +++ b/src/tools/EpicsHostArch.pl @@ -7,30 +7,103 @@ # in file LICENSE that is included with this distribution. #************************************************************************* -# Returns an architecture name for EPICS_HOST_ARCH that should be -# appropriate for the CPU that this version of Perl was built for. -# Any arguments to the program will be appended with separator '-' -# to allow flags like -gnu -debug and/or -static to be added. - -# Before Base has been built, use a command like this: -# bash$ export EPICS_HOST_ARCH=`perl src/tools/EpicsHostArch.pl` -# -# If Base is already built, use -# tcsh% setenv EPICS_HOST_ARCH `perl base/lib/perl/EpicsHostArch.pl` - -# If your architecture is not recognized by this script, please send -# the output from running 'perl --version' to the EPICS tech-talk -# mailing list to have it added. - use strict; +use warnings; + +use Getopt::Std; +$Getopt::Std::STANDARD_HELP_VERSION = 1; use Config; use POSIX; -print join('-', HostArch(), @ARGV), "\n"; +use FindBin qw($Bin); +use lib ("$Bin/../../lib/perl", $Bin); -sub HostArch { - my $arch = $Config{archname}; +use Pod::Usage; + +=head1 NAME + +EpicsHostArch.pl - Prints the current host architecture + +=head1 SYNOPSIS + +B [extension] + +B -g [extension] + +B -h + +=head1 DESCRIPTION + +Returns an architecture name for EPICS_HOST_ARCH that should be +appropriate for the CPU that this version of Perl was built for. +Any arguments to the program will be appended with separator '-' +to allow flags like -gnu -debug and/or -static to be added. + +Before Base has been built, use a command like this: + +C + +If Base is already built, use: + +C + +If your architecture is not recognized by this script, please send +the output from running C to the EPICS tech-talk +mailing list to have it added. + +If the C<-g> option is provided with an argument, print the EPICS +architecture corresponding to the given GNU architecture tuplet. + +=head1 OPTIONS + +B understands the following options: + +=over 4 + +=item B<-h> + +Help, display this document as text. + +=item B<-g> + +If specified, convert the given GNU architecture tuplet instead. + +=back + +=head1 EXAMPLES + +C + +C + +C + +C + +=cut + +our ($opt_h); +our ($opt_g); +$opt_h = 0; + +sub HELP_MESSAGE { + pod2usage(-exitval => 2, -verbose => $opt_h * 3); +} + +HELP_MESSAGE() if !getopts('hg:') || $opt_h; + +# Convert GNU-like architecture tuples (-) +# to EPICS terminology (-) +# +# Documentation for GNU-like terminology: +# - https://www.gnu.org/software/autoconf/manual/autoconf-2.65/html_node/System-Type.html#System-Type +# - https://git.savannah.gnu.org/cgit/config.git/tree/ +# +# Some examples from the Debian project: +# - https://wiki.debian.org/Multiarch/Tuples +sub toEpicsArch { + my $arch = shift; for ($arch) { return 'linux-x86_64' if m/^x86_64-linux/; return 'linux-x86' if m/^i[3-6]86-linux/; @@ -45,15 +118,9 @@ sub HostArch { return 'solaris-x86' if m/^i86pc-solaris/; return 'freebsd-x86_64' if m/^x86_64-freebsd/; return 'freebsd-x86_64' if m/^amd64-freebsd/; + return 'darwin-x86' if m/^x86(_64)?-darwin/; + return 'darwin-aarch64' if m/^(arm64|aarch64)-darwin/; - my ($kernel, $hostname, $release, $version, $cpu) = uname; - if (m/^darwin/) { - for ($cpu) { - return 'darwin-x86' if m/^x86_64/; - return 'darwin-aarch64' if m/^arm64/; - } - die "$0: macOS CPU type '$cpu' not recognized\n"; - } # mingw64 has 32bit and 64bit build shells which give same arch result if (m/^(x86_64|i686)-msys/) { die "$0: Architecture '$arch' is unclear,\n". @@ -64,3 +131,21 @@ sub HostArch { die "$0: Architecture '$arch' not recognized\n"; } } + +my $arch; + +if ($opt_g) { + $arch = $opt_g; +} else { + $arch = $Config{archname}; + + # On darwin, $Config{archname} returns darwin-2level, which is unusable + # so we use `uname` instead + $_ = $arch; + if (m/^darwin/) { + my ($kernel, $hostname, $release, $version, $cpu) = uname; + $arch = $cpu . "-darwin"; + } +} + +print join('-', toEpicsArch($arch), @ARGV), "\n"; diff --git a/src/tools/Makefile b/src/tools/Makefile index bb47f37a0..c40119ad3 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -22,9 +22,10 @@ PERL_MODULES += EPICS/PodXHtml.pm PERL_MODULES += Pod/Markdown.pm PERL_MODULES += URI/Escape.pm -# This goes into lib/perl, not bin/ +# This also goes into lib/perl PERL_MODULES += EpicsHostArch.pl +PERL_SCRIPTS += EpicsHostArch.pl PERL_SCRIPTS += assembleSnippets.pl PERL_SCRIPTS += convertRelease.pl PERL_SCRIPTS += cvsclean.pl diff --git a/src/tools/makeTestfile.pl b/src/tools/makeTestfile.pl index 31c64bd66..22d4fad10 100644 --- a/src/tools/makeTestfile.pl +++ b/src/tools/makeTestfile.pl @@ -140,6 +140,8 @@ else { ######################################## Code for Unix run-hosts print $OUT <<__UNIX__; +use POSIX qw(WIFEXITED WIFSIGNALED WEXITSTATUS); + my \$pid = fork(); die "\$tool: Can't fork for '$error': \$!\\n" unless defined \$pid; @@ -154,9 +156,19 @@ if (\$pid) { }; alarm \$timeout; - waitpid \$pid, 0; - alarm 0; - exit \$? >> 8; + while (1) { + waitpid \$pid, 0; + if (WIFEXITED(\$?)) { + # normal exit + alarm 0; + exit WEXITSTATUS(\$?); + } elsif (WIFSIGNALED(\$?)) { + # terminated by signal + alarm 0; + die "\$tool: Test was terminated by signal '\$?'\\n"; + } + # non-terminal change of status, continue waiting + } } else { # Child process