Set up the headings in the Release Notes for the next release
+ version number and restore the warning about this being an unreleased
+ version of EPICS.
Commit these changes (don't push).
@@ -387,12 +305,12 @@ starting at
- Website Manager
+ Release Manager
Go to the Launchpad milestone for this release. Click the Create
release button and add the release date. Put a URL for the release page
in the Release notes box, and click the Create release button. Upload
diff --git a/documentation/mainpage.dox b/documentation/mainpage.dox
index 472f1d999..ed3d688ef 100644
--- a/documentation/mainpage.dox
+++ b/documentation/mainpage.dox
@@ -5,11 +5,11 @@ Documentation index
@li @ref releasenotes
@li @ref install
-@li @ref recordrefmanual
+@li EPICS Component Reference Manual
+@li Field Modifiers and Channel Filters
+@li Extensible IOC Database Links
@li Channel Access Reference Manual
-@li Server Side Filters Reference
@li msi: Macro Substitution and Include Tool
-@li JSON Link Types
@li Perl 5 Interface to Channel Access
*/
diff --git a/modules/CONFIG_SITE.local b/modules/CONFIG_SITE.local
index db97a5b78..f0becc2bb 100644
--- a/modules/CONFIG_SITE.local
+++ b/modules/CONFIG_SITE.local
@@ -2,6 +2,16 @@
# EPICS BASE is distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
+#
+# modules/CONFIG_SITE.local
+#
+# Despite the .local in its name, this file *is* included in EPICS Base
+# sources and should *not* be modified by sites. This trick is necessary
+# to allow external submodules to be placed inside the modules directory
+# without having to modify them, as long as their configure/CONFIG_SITE
+# file includes the standard line
+# -include $(TOP)/../CONFIG_SITE.local
+# that causes this file to be read in during submodule builds.
# The name our submodules know us by:
PARENT_MODULE = EPICS_BASE
diff --git a/modules/ca/src/client/access.cpp b/modules/ca/src/client/access.cpp
index 9c94d61c5..58cc4bca5 100644
--- a/modules/ca/src/client/access.cpp
+++ b/modules/ca/src/client/access.cpp
@@ -88,7 +88,7 @@ const char * ca_message_text []
"Bad event subscription (monitor) identifier",
"Remote channel has new network address",
"New or resumed network connection",
-"Specified task isnt a member of a CA context",
+"Specified task isn't a member of a CA context",
"Attempt to use defunct CA feature failed",
"The supplied string is empty",
@@ -324,7 +324,7 @@ int epicsStdCall ca_create_channel (
*chanptr = pChanNotify;
pChanNotify->initiateConnect ( guard );
// no need to worry about a connect preempting here because
- // the connect sequence will not start untill initiateConnect()
+ // the connect sequence will not start until initiateConnect()
// is called
}
catch ( cacChannel::badString & ) {
@@ -384,9 +384,9 @@ int epicsStdCall ca_clear_channel ( chid pChan )
// we will definately stall out here if all of the
// following are true
//
- // o user creates non-preemtive mode client library context
+ // o user creates non-preemptive mode client library context
// o user doesnt periodically call a ca function
- // o user calls this function from an auxiillary thread
+ // o user calls this function from an auxiliary thread
//
CallbackGuard cbGuard ( cac.cbMutex );
epicsGuard < epicsMutex > guard ( cac.mutex );
@@ -720,7 +720,7 @@ int epicsStdCall ca_context_status ( ca_client_context * pcac, unsigned level )
/*
* ca_current_context ()
*
- * used when an auxillary thread needs to join a CA client context started
+ * used when an auxiliary thread needs to join a CA client context started
* by another thread
*/
// extern "C"
@@ -740,7 +740,7 @@ struct ca_client_context * epicsStdCall ca_current_context ()
/*
* ca_attach_context ()
*
- * used when an auxillary thread needs to join a CA client context started
+ * used when an auxiliary thread needs to join a CA client context started
* by another thread
*/
// extern "C"
diff --git a/modules/ca/src/client/acctst.c b/modules/ca/src/client/acctst.c
index 83d848f12..34298680c 100644
--- a/modules/ca/src/client/acctst.c
+++ b/modules/ca/src/client/acctst.c
@@ -494,7 +494,7 @@ void verifyConnectionHandlerConnect ( appChan *pChans, unsigned chanCount,
/*
* verifyBlockingConnect ()
*
- * 1) verify that we dont print a disconnect message when
+ * 1) verify that we don't print a disconnect message when
* we delete the last channel
*
* 2) verify that we delete the connection to the IOC
@@ -645,7 +645,7 @@ void verifyBlockingConnect ( appChan *pChans, unsigned chanCount,
status = ca_pend_io ( 1e-16 );
if ( status == ECA_TIMEOUT ) {
/*
- * we end up here if the channel isnt on the same host
+ * we end up here if the channel isn't on the same host
*/
epicsThreadSleep ( 0.1 );
ca_poll ();
@@ -1112,7 +1112,7 @@ void verifyHighThroughputRead ( chid chan, unsigned interestLevel )
unsigned i;
/*
- * verify we dont jam up on many uninterrupted
+ * verify we don't jam up on many uninterrupted
* solicitations
*/
if ( ca_read_access (chan) ) {
@@ -1152,7 +1152,7 @@ void verifyHighThroughputWrite ( chid chan, unsigned interestLevel )
}
/*
- * verify we dont jam up on many uninterrupted
+ * verify we don't jam up on many uninterrupted
* get callback requests
*/
void verifyHighThroughputReadCallback ( chid chan, unsigned interestLevel )
@@ -1181,7 +1181,7 @@ void verifyHighThroughputReadCallback ( chid chan, unsigned interestLevel )
}
/*
- * verify we dont jam up on many uninterrupted
+ * verify we don't jam up on many uninterrupted
* put callback request
*/
void verifyHighThroughputWriteCallback ( chid chan, unsigned interestLevel )
@@ -1239,7 +1239,7 @@ void verifyBadString ( chid chan, unsigned interestLevel )
verify ( status == ECA_NORMAL );
if ( strcmp ( stimStr, respStr ) ) {
printf (
- "Test fails if stim \"%s\" isnt roughly equiv to resp \"%s\"\n",
+ "Test fails if stim \"%s\" isn't roughly equiv to resp \"%s\"\n",
stimStr, respStr);
}
showProgressEnd ( interestLevel );
@@ -1395,7 +1395,7 @@ static void multiSubscrDestroyNoLateCallbackThread ( void * pParm )
/*
* raise the priority of the current thread hoping to improve our
- * likelyhood of detecting a bug
+ * likelihood of detecting a bug
*/
priorityOfTestThread = epicsThreadGetPrioritySelf ();
epicsThreadSetPriority ( epicsThreadGetIdSelf(), epicsThreadPriorityHigh );
@@ -1445,7 +1445,7 @@ static void multiSubscrDestroyNoLateCallbackThread ( void * pParm )
}
/*
- * verify that, in a preemtive callback mode client, a subscription callback never
+ * verify that, in a preemptive callback mode client, a subscription callback never
* comes after the subscription is destroyed
*/
static void multiSubscrDestroyNoLateCallbackTest ( const char *pName, unsigned interestLevel )
@@ -1563,7 +1563,7 @@ void multiSubscriptionDeleteTest ( chid chan, unsigned interestLevel )
/*
* singleSubscriptionDeleteTest
*
- * verify that we dont fail when we repeatedly create
+ * verify that we don't fail when we repeatedly create
* and delete only one subscription with a high level of
* traffic on it
*/
@@ -1617,7 +1617,7 @@ void singleSubscriptionDeleteTest ( chid chan, unsigned interestLevel )
/*
* channelClearWithEventTrafficTest
*
- * verify that we can delete a channel that has subcriptions
+ * verify that we can delete a channel that has subscriptions
* attached with heavy update traffic
*/
void channelClearWithEventTrafficTest ( const char *pName, unsigned interestLevel )
@@ -2481,7 +2481,7 @@ void monitorUpdateTest ( chid chan, unsigned interestLevel )
showProgress ( interestLevel );
/*
- * attempt to uncover problems where the last event isnt sent
+ * attempt to uncover problems where the last event isn't sent
* and hopefully get into a flow control situation
*/
prevPassCount = 0u;
@@ -2522,7 +2522,7 @@ void monitorUpdateTest ( chid chan, unsigned interestLevel )
ca_poll (); /* emulate typical GUI */
for ( j = 0; j < NELEMENTS ( test ); j++ ) {
/*
- * we shouldnt see old monitors because
+ * we shouldn't see old monitors because
* we resubscribed
*/
verify ( test[j].count <= i + 2 );
@@ -3016,7 +3016,7 @@ void testMultithreadSubscr ( void * pParm )
}
/*
- * test installation of subscriptions similar to usage paterns
+ * test installation of subscriptions similar to usage patterns
* employed by modern versions of the sequencer
*/
void verifyMultithreadSubscr ( const char * pName, unsigned interestLevel )
@@ -3111,7 +3111,7 @@ void fdManagerVerify ( const char * pName, unsigned interestLevel )
status = ca_flush_io ();
verify ( status == ECA_NORMAL );
- /* look for infinite loop in fd manager schedualing */
+ /* look for infinite loop in fd manager scheduling */
epicsTimeGetCurrent ( & begin );
eventCount = 0u;
while ( 1 ) {
@@ -3175,7 +3175,7 @@ void verifyConnectWithDisconnectedChannels (
* we should be able to connect to a valid
* channel within a reasonable delay even
* though there is one permanently
- * diasconnected channel
+ * disconnected channel
*/
status = ca_pend_io ( timeoutToPendIO );
verify ( status == ECA_NORMAL );
@@ -3491,7 +3491,7 @@ int acctst ( const char * pName, unsigned interestLevel, unsigned channelCount,
/*
* CA pend event delay accuracy test
- * (CA asssumes that search requests can be sent
+ * (CA assumes that search requests can be sent
* at least every 25 mS on all supported os)
*/
printf ( "\n" );
diff --git a/modules/ca/src/client/acctstRegister.cpp b/modules/ca/src/client/acctstRegister.cpp
index c619f97bb..8f69d25bc 100644
--- a/modules/ca/src/client/acctstRegister.cpp
+++ b/modules/ca/src/client/acctstRegister.cpp
@@ -32,7 +32,13 @@ static const iocshArg *acctstArgs[] =
&acctstArg3,
&acctstArg4
};
-static const iocshFuncDef acctstFuncDef = {"acctst", 5, acctstArgs};
+static const iocshFuncDef acctstFuncDef = {
+ "acctst",
+ 5,
+ acctstArgs,
+ "Execute a Channel Access regression test.\n\n"
+ "For more information, see the 'acctst' documentation in the Channel Access reference.\n",
+};
/* Wrapper called by iocsh, selects the argument types that print needs */
diff --git a/modules/ca/src/client/bhe.cpp b/modules/ca/src/client/bhe.cpp
index d7fb77785..3808f061c 100644
--- a/modules/ca/src/client/bhe.cpp
+++ b/modules/ca/src/client/bhe.cpp
@@ -130,8 +130,8 @@ bool bhe::updatePeriod (
guard.assertIdenticalMutex ( this->mutex );
//
- // this block is enetered if the beacon was created as a side effect of
- // creating a connection and so we dont yet know the first beacon time
+ // this block is entered if the beacon was created as a side effect of
+ // creating a connection and so we don't yet know the first beacon time
// and sequence number
//
if ( this->timeStamp == epicsTime () ) {
@@ -154,7 +154,7 @@ bool bhe::updatePeriod (
return false;
}
- // 1) detect beacon duplications due to redundant routes
+ // 1) detect beacon duplication due to redundant routes
// 2) detect lost beacons due to input queue overrun or damage
if ( CA_V410 ( protocolRevision ) ) {
unsigned beaconSeqAdvance;
@@ -175,7 +175,7 @@ bool bhe::updatePeriod (
// throw out sequence numbers that jump forward by only a few numbers
// (this situation is probably caused by a duplicate route
- // or a beacon due to input queue overun)
+ // or a beacon due to input queue overrun)
if ( beaconSeqAdvance > 1 && beaconSeqAdvance < 4 ) {
logBeaconDiscard ( beaconSeqAdvance, currentTime );
return false;
@@ -244,8 +244,8 @@ bool bhe::updatePeriod (
/*
* Is this an IOC seen because of an IOC reboot
* (beacon come at a higher rate just after the
- * IOC reboots). Lower tolarance here because we
- * dont have to worry about lost beacons.
+ * IOC reboots). Lower tolerance here because we
+ * don't have to worry about lost beacons.
*
* It may be possible to get false triggers here
* if the client is busy, but this does not cause
diff --git a/modules/ca/src/client/caProto.h b/modules/ca/src/client/caProto.h
index c6e211c11..57b280177 100644
--- a/modules/ca/src/client/caProto.h
+++ b/modules/ca/src/client/caProto.h
@@ -58,8 +58,8 @@
#define CA_REPEATER_PORT (CA_PORT_BASE+CA_MAJOR_PROTOCOL_REVISION*2u+1u)
/*
- * 1500 (max of ethernet and 802.{2,3} MTU) - 20(IP) - 8(UDP)
- * (the MTU of Ethernet is currently independent of its speed varient)
+ * 1500 (max of Ethernet and 802.{2,3} MTU) - 20(IP) - 8(UDP)
+ * (the MTU of Ethernet is currently independent of its speed variant)
*/
#define ETHERNET_MAX_UDP ( 1500u - 20u - 8u )
#define MAX_UDP_RECV ( 0xffff + 16u ) /* allow large frames to be received in the future */
@@ -117,7 +117,7 @@ typedef ca_uint32_t caResId;
/*
* for use with search and not_found (if search fails and
- * its not a broadcast tell the client to look elesewhere)
+ * its not a broadcast tell the client to look elsewhere)
*/
#define DOREPLY 10u
#define DONTREPLY 5u
@@ -176,7 +176,7 @@ typedef struct ca_hdr {
struct mon_info {
ca_float32_t m_lval; /* low delta */
ca_float32_t m_hval; /* high delta */
- ca_float32_t m_toval; /* period btween samples */
+ ca_float32_t m_toval; /* period between samples */
ca_uint16_t m_mask; /* event select mask */
ca_uint16_t m_pad; /* extend to 32 bits */
};
diff --git a/modules/ca/src/client/ca_client_context.cpp b/modules/ca/src/client/ca_client_context.cpp
index 1c2f985b5..1e830fd08 100644
--- a/modules/ca/src/client/ca_client_context.cpp
+++ b/modules/ca/src/client/ca_client_context.cpp
@@ -224,7 +224,7 @@ void ca_client_context::changeExceptionEvent (
epicsGuard < epicsMutex > guard ( this->mutex );
this->ca_exception_func = pfunc;
this->ca_exception_arg = arg;
-// should block here until releated callback in progress completes
+// should block here until related callback in progress completes
}
void ca_client_context::replaceErrLogHandler (
@@ -237,7 +237,7 @@ void ca_client_context::replaceErrLogHandler (
else {
this->pVPrintfFunc = epicsVprintf;
}
-// should block here until releated callback in progress completes
+// should block here until related callback in progress completes
}
void ca_client_context::registerForFileDescriptorCallBack (
@@ -252,7 +252,7 @@ void ca_client_context::registerForFileDescriptorCallBack (
// w/o having sent the wakeup message
this->_sendWakeupMsg ();
}
-// should block here until releated callback in progress completes
+// should block here until related callback in progress completes
}
int ca_client_context :: printFormated (
@@ -392,9 +392,19 @@ void ca_client_context :: vSignal (
}
epicsTime current = epicsTime::getCurrent ();
- char date[64];
- current.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f");
- this->printFormated ( " Current Time: %s\n", date );
+ try {
+ char date[64];
+ current.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f");
+ this->printFormated ( " Current Time: %s\n", date );
+ }
+ catch ( std::exception & except ) {
+ errlogPrintf (
+ "CA client library thread \"%s\" caught C++ exception \"%s\"\n",
+ epicsThreadGetNameSelf (), except.what () );
+ epicsTimeStamp now = current;
+ this->printFormated ( " Current Time: %u.%u\n",
+ now.secPastEpoch, now.nsec );
+ }
/*
* Terminate execution if unsuccessful
@@ -768,9 +778,9 @@ LIBCA_API int epicsStdCall ca_clear_subscription ( evid pMon )
// we will definately stall out here if all of the
// following are true
//
- // o user creates non-preemtive mode client library context
+ // o user creates non-preemptive mode client library context
// o user doesnt periodically call a ca function
- // o user calls this function from an auxiillary thread
+ // o user calls this function from an auxiliary thread
//
CallbackGuard cbGuard ( cac.cbMutex );
epicsGuard < epicsMutex > guard ( cac.mutex );
diff --git a/modules/ca/src/client/cac.cpp b/modules/ca/src/client/cac.cpp
index b1abfc42a..f8d1d0800 100644
--- a/modules/ca/src/client/cac.cpp
+++ b/modules/ca/src/client/cac.cpp
@@ -285,7 +285,7 @@ cac::~cac ()
// this blocks until the UDP thread exits so that
// it will not sneak in any new clients
//
- // lock intentionally not held here so that we dont deadlock
+ // lock intentionally not held here so that we don't deadlock
// waiting for the UDP thread to exit while it is waiting to
// get the lock.
{
@@ -312,7 +312,7 @@ cac::~cac ()
//
// wait for all tcp threads to exit
//
- // this will block for oustanding sends to go out so dont
+ // this will block for outstanding sends to go out so don't
// hold a lock while waiting
//
{
@@ -411,7 +411,7 @@ void cac::show (
::printf ( "Channel Access Client Context at %p for user %s\n",
static_cast ( this ), this->pUserName );
- // this also supresses the "defined, but not used"
+ // this also suppresses the "defined, but not used"
// warning message
::printf ( "\trevision \"%s\"\n", pVersionCAC );
diff --git a/modules/ca/src/client/cadef.h b/modules/ca/src/client/cadef.h
index f64e3899c..12861c384 100644
--- a/modules/ca/src/client/cadef.h
+++ b/modules/ca/src/client/cadef.h
@@ -129,7 +129,7 @@ typedef unsigned CA_SYNC_GID;
#define CA_OP_CONN_UP 6
#define CA_OP_CONN_DOWN 7
-/* depricated */
+/* deprecated */
#define CA_OP_SEARCH 2
/*
@@ -464,7 +464,7 @@ LIBCA_API int epicsStdCall ca_array_get_callback
/* Specify a function to be executed whenever significant changes */
/* occur to a channel. */
/* NOTES: */
-/* 1) Evid may be omited by passing a NULL pointer */
+/* 1) Evid may be omitted by passing a NULL pointer */
/* */
/* 2) An array count of zero specifies the native db count */
/* */
@@ -559,19 +559,19 @@ LIBCA_API chid epicsStdCall ca_evid_to_chid ( evid id );
/*
* ca_pend_event()
*
- * timeOut R wait for this delay in seconds
+ * timeout R wait for this delay in seconds
*/
-LIBCA_API int epicsStdCall ca_pend_event (ca_real timeOut);
+LIBCA_API int epicsStdCall ca_pend_event (ca_real timeout);
#define ca_poll() ca_pend_event(1e-12)
/*
* ca_pend_io()
*
- * timeOut R wait for this delay in seconds but return early
+ * timeout R wait for this delay in seconds but return early
* if all get requests (or search requests with null
* connection handler pointer have completed)
*/
-LIBCA_API int epicsStdCall ca_pend_io (ca_real timeOut);
+LIBCA_API int epicsStdCall ca_pend_io (ca_real timeout);
/* calls ca_pend_io() if early is true otherwise ca_pend_event() is called */
LIBCA_API int epicsStdCall ca_pend (ca_real timeout, int early);
@@ -837,7 +837,7 @@ LIBCA_API double epicsStdCall ca_beacon_period (chid chan);
LIBCA_API double epicsStdCall ca_receive_watchdog_delay (chid chan);
/*
- * used when an auxillary thread needs to join a CA client context started
+ * used when an auxiliary thread needs to join a CA client context started
* by another thread
*/
LIBCA_API struct ca_client_context * epicsStdCall ca_current_context ();
diff --git a/modules/ca/src/client/casw.cpp b/modules/ca/src/client/casw.cpp
index 54a14036c..c9ff465f7 100644
--- a/modules/ca/src/client/casw.cpp
+++ b/modules/ca/src/client/casw.cpp
@@ -188,7 +188,7 @@ int main ( int argc, char ** argv )
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
epicsSocketDestroy ( sock );
- errlogPrintf ("casw: error from recv was = \"%s\"\n",
+ errlogPrintf ("casw: " ERL_ERROR " from recv was = \"%s\"\n",
sockErrBuf );
return -1;
}
@@ -224,7 +224,7 @@ int main ( int argc, char ** argv )
* always set this field to INADDR_ANY
*
* clients always assume that if this
- * field is set to something that isnt INADDR_ANY
+ * field is set to something that isn't INADDR_ANY
* then it is the overriding IP address of the server.
*/
ina.sin_family = AF_INET;
@@ -235,7 +235,7 @@ int main ( int argc, char ** argv )
}
else {
/*
- * old servers dont supply this and the
+ * old servers don't supply this and the
* default port must be assumed
*/
ina.sin_port = htons ( serverPort );
diff --git a/modules/ca/src/client/comBuf.cpp b/modules/ca/src/client/comBuf.cpp
index 8f92170dd..5c9083ed7 100644
--- a/modules/ca/src/client/comBuf.cpp
+++ b/modules/ca/src/client/comBuf.cpp
@@ -45,7 +45,7 @@ bool comBuf::flushToWire ( wireSendAdapter & wire, const epicsTime & currentTime
return true;
}
-// throwing the exception from a function that isnt inline
+// throwing the exception from a function that isn't inline
// shrinks the GNU compiled object code
void comBuf::throwInsufficentBytesException ()
{
diff --git a/modules/ca/src/client/comBuf.h b/modules/ca/src/client/comBuf.h
index 6a4899414..3d5f58c45 100644
--- a/modules/ca/src/client/comBuf.h
+++ b/modules/ca/src/client/comBuf.h
@@ -88,6 +88,7 @@ public:
bool push ( const T & value );
template < class T >
unsigned push ( const T * pValue, unsigned nElem );
+ unsigned push ( const char * pValue, unsigned nElem );
unsigned push ( const epicsInt8 * pValue, unsigned nElem );
unsigned push ( const epicsUInt8 * pValue, unsigned nElem );
unsigned push ( const epicsOldString * pValue, unsigned nElem );
@@ -208,6 +209,11 @@ inline unsigned comBuf :: push ( const epicsUInt8 *pValue, unsigned nElem )
return copyInBytes ( pValue, nElem );
}
+inline unsigned comBuf :: push ( const char *pValue, unsigned nElem )
+{
+ return copyInBytes ( pValue, nElem );
+}
+
inline unsigned comBuf :: push ( const epicsOldString * pValue, unsigned nElem )
{
unsigned index = this->nextWriteIndex;
diff --git a/modules/ca/src/client/comQueRecv.cpp b/modules/ca/src/client/comQueRecv.cpp
index 026a84ced..d7d07361b 100644
--- a/modules/ca/src/client/comQueRecv.cpp
+++ b/modules/ca/src/client/comQueRecv.cpp
@@ -46,7 +46,7 @@ void comQueRecv::clear ()
this->nBytesPending = 0u;
}
-unsigned comQueRecv::copyOutBytes ( epicsInt8 *pBuf, unsigned nBytes )
+unsigned comQueRecv::copyOutBytes ( char *pBuf, unsigned nBytes )
{
unsigned totalBytes = 0u;
do {
@@ -196,7 +196,7 @@ epicsUInt16 comQueRecv::popUInt16 ()
if ( ! pComBuf ) {
comBuf::throwInsufficentBytesException ();
}
- // try first for all in one buffer efficent version
+ // try first for all in one buffer efficient version
epicsUInt16 tmp = 0;
comBuf::popStatus status = pComBuf->pop ( tmp );
if ( status.success ) {
@@ -215,7 +215,7 @@ epicsUInt32 comQueRecv::popUInt32 ()
if ( ! pComBuf ) {
comBuf::throwInsufficentBytesException ();
}
- // try first for all in one buffer efficent version
+ // try first for all in one buffer efficient version
epicsUInt32 tmp = 0;
comBuf::popStatus status = pComBuf->pop ( tmp );
if ( status.success ) {
@@ -230,7 +230,7 @@ epicsUInt32 comQueRecv::popUInt32 ()
bool comQueRecv::popOldMsgHeader ( caHdrLargeArray & msg )
{
- // try first for all in one buffer efficent version
+ // try first for all in one buffer efficient version
comBuf * pComBuf = this->bufs.first ();
if ( ! pComBuf ) {
return false;
diff --git a/modules/ca/src/client/comQueRecv.h b/modules/ca/src/client/comQueRecv.h
index ad8bead88..01c961e51 100644
--- a/modules/ca/src/client/comQueRecv.h
+++ b/modules/ca/src/client/comQueRecv.h
@@ -33,7 +33,7 @@ public:
comQueRecv ( comBufMemoryManager & );
~comQueRecv ();
unsigned occupiedBytes () const;
- unsigned copyOutBytes ( epicsInt8 *pBuf, unsigned nBytes );
+ unsigned copyOutBytes ( char *pBuf, unsigned nBytes );
unsigned removeBytes ( unsigned nBytes );
void pushLastComBufReceived ( comBuf & );
void clear ();
diff --git a/modules/ca/src/client/comQueSend.cpp b/modules/ca/src/client/comQueSend.cpp
index e782a7017..c0c535f42 100644
--- a/modules/ca/src/client/comQueSend.cpp
+++ b/modules/ca/src/client/comQueSend.cpp
@@ -27,17 +27,17 @@
// 1) Allow sufficent headroom so that users will be able to perform
// a reasonable amount of IO within CA callbacks without experiencing
// a push/pull deadlock. If a potential push/pull deadlock situation
-// occurs then detect and avoid it and provide diagnotic to the user
+// occurs then detect and avoid it and provide diagnostic to the user
// via special status.
-// 2) Return status to the user when there is insufficent memory to
+// 2) Return status to the user when there is insufficient memory to
// queue a complete message.
// 3) return status to the user when a message cant be flushed because
// a connection dropped.
-// 4) Do not allocate too much memory in exception situatons (such as
+// 4) Do not allocate too much memory in exception situations (such as
// after a circuit disconnect).
// 5) Avoid allocating more memory than is absolutely necessary to meet
// the above requirements.
-// 6) Message fragments must never be sent to the IOC when there isnt
+// 6) Message fragments must never be sent to the IOC when there isn't
// enough memory to queue part of a message (we also must not force
// a disconnect because the client is starved for memory).
// 7) avoid the need to check status for each byte pushed into the
@@ -45,7 +45,7 @@
//
// Implementation:
// 1) When queuing a complete message, first test to see if a flush is
-// required. If it is a receive thread scheduals the flush with the
+// required. If it is a receive thread schedules the flush with the
// send thread, and otherwise directly execute the system call. The
// send thread must run at a higher priority than the receive thread
// if we are to minimize memory consumption.
@@ -58,9 +58,9 @@
// a) A user is queuing more requests that demand a response from a
// callback than are removed by the response that initiated the
// callback, and this situation persists for many callbacks until
-// all buffering in the system is exausted.
+// all buffering in the system is exhausted.
// b) A user is queuing many requests that demand a response from one
-// callback until all buffering in the system is exausted.
+// callback until all buffering in the system is exhausted.
// c) Some combination of both (a) nad (b).
//
//
diff --git a/modules/ca/src/client/convert.cpp b/modules/ca/src/client/convert.cpp
index e043ee2fe..361b60222 100644
--- a/modules/ca/src/client/convert.cpp
+++ b/modules/ca/src/client/convert.cpp
@@ -35,7 +35,7 @@
#include "caerr.h"
/*
- * NOOP if this isnt required
+ * NOOP if this isn't required
*/
#ifdef EPICS_CONVERSION_REQUIRED
@@ -326,7 +326,7 @@ arrayElementCount num /* number of values */
** int encode; boolean, if true vax to ieee
** else ieee to vax
**
-** converts fields ofstruct in HOST format to ieee format
+** converts fields of struct in HOST format to ieee format
** or
** converts fields of struct in NET format to fields with HOST
** format
@@ -1022,7 +1022,7 @@ arrayElementCount num /* number of values */
** int encode; boolean, if true vax to ieee
** else ieee to vax
**
-** converts fields ofstruct in HOST format to ieee format
+** converts fields of struct in HOST format to ieee format
** or
** converts fields of struct in NET format to fields with HOST
** format
@@ -1056,7 +1056,7 @@ arrayElementCount num /* number of values */
/****************************************************************************
** cvrt_sts_long(s,d)
**
-** converts fields ofstruct in HOST format to ieee format
+** converts fields of struct in HOST format to ieee format
** or
** converts fields of struct in NET format to fields with HOST
** format
@@ -1118,7 +1118,7 @@ arrayElementCount num /* number of values */
/****************************************************************************
** cvrt_time_short(s,d)
**
-** converts fields ofstruct in HOST format to ieee format
+** converts fields of struct in HOST format to ieee format
** or
** converts fields of struct in NET format to fields with HOST
** format
@@ -1239,7 +1239,7 @@ arrayElementCount num /* number of values */
/****************************************************************************
** cvrt_sts_char(s,d)
**
-** converts fields ofstruct in HOST format to ieee format
+** converts fields of struct in HOST format to ieee format
** or
** converts fields of struct in NET format to fields with HOST
** format
@@ -1274,7 +1274,7 @@ arrayElementCount num /* number of values */
/****************************************************************************
** cvrt_time_long(s,d)
**
-** converts fields ofstruct in HOST format to ieee format
+** converts fields of struct in HOST format to ieee format
** or
** converts fields of struct in NET format to fields with HOST
** format
@@ -1325,7 +1325,7 @@ arrayElementCount num /* number of values */
for(i=0; i guard ( this->mutex );
// a few legacy clients have a direct pointer to this buffer so we
- // set the entrire string to nill terminators before we start copying
+ // set the entire string to nill terminators before we start copying
// in the name (this reduces the chance that another thread will see
// garbage characters).
size_t newNameLen = strlen ( pHostNameIn );
diff --git a/modules/ca/src/client/iocinf.cpp b/modules/ca/src/client/iocinf.cpp
index 7a0853f00..c760a0d34 100644
--- a/modules/ca/src/client/iocinf.cpp
+++ b/modules/ca/src/client/iocinf.cpp
@@ -79,7 +79,7 @@ extern "C" int epicsStdCall addAddrToChannelAccessAddressList
const char *pStr;
const char *pToken;
struct sockaddr_in addr;
- char buf[32u]; /* large enough to hold an IP address */
+ char buf[256u]; /* large enough to hold an IP address or hostname */
int status, ret = -1;
pStr = envGetConfigParamPtr (pEnv);
@@ -189,7 +189,7 @@ extern "C" void epicsStdCall configureChannelAccessAddressList
int yes;
/*
- * dont load the list twice
+ * don't load the list twice
*/
assert ( ellCount (pList) == 0 );
diff --git a/modules/ca/src/client/iocinf.h b/modules/ca/src/client/iocinf.h
index ae775241d..0dadf44b0 100644
--- a/modules/ca/src/client/iocinf.h
+++ b/modules/ca/src/client/iocinf.h
@@ -56,7 +56,7 @@ static const double CA_CONN_VERIFY_PERIOD = 30.0; /* (sec) how often to request
* monitor flow control
*
* turning this down effects maximum throughput
- * because we dont get an optimal number of bytes
+ * because we don't get an optimal number of bytes
* per network frame
*/
static const unsigned contiguousMsgCountWhichTriggersFlowControl = 10u;
diff --git a/modules/ca/src/client/msgForMultiplyDefinedPV.cpp b/modules/ca/src/client/msgForMultiplyDefinedPV.cpp
index f65b392ef..63c00d108 100644
--- a/modules/ca/src/client/msgForMultiplyDefinedPV.cpp
+++ b/modules/ca/src/client/msgForMultiplyDefinedPV.cpp
@@ -57,7 +57,7 @@ void msgForMultiplyDefinedPV::transactionComplete ( const char * pHostNameRej )
// calls into cac for the notification
// the msg object (= this) is being deleted as part of the notification
this->cb.pvMultiplyDefinedNotify ( *this, this->channel, this->acc, pHostNameRej );
- // !! dont touch 'this' pointer after this point because object has been deleted !!
+ // !! don't touch 'this' pointer after this point because object has been deleted !!
}
void * msgForMultiplyDefinedPV::operator new ( size_t size,
diff --git a/modules/ca/src/client/nciu.cpp b/modules/ca/src/client/nciu.cpp
index 61ccebafc..a862cb228 100644
--- a/modules/ca/src/client/nciu.cpp
+++ b/modules/ca/src/client/nciu.cpp
@@ -147,14 +147,14 @@ void nciu::connect ( unsigned nativeType,
guard, this->accessRightState );
}
- // channel uninstal routine grabs the callback lock so
+ // channel uninstall routine grabs the callback lock so
// a channel will not be deleted while a call back is
// in progress
//
// the callback lock is also taken when a channel
// disconnects to prevent a race condition with the
// code below - ie we hold the callback lock here
- // so a chanel cant be destroyed out from under us.
+ // so a channel cant be destroyed out from under us.
this->notify().connectNotify ( guard );
}
diff --git a/modules/ca/src/client/netIO.h b/modules/ca/src/client/netIO.h
index 2560663ba..e5b5814bc 100644
--- a/modules/ca/src/client/netIO.h
+++ b/modules/ca/src/client/netIO.h
@@ -32,7 +32,7 @@
// destructor is virtual (therefore it is protected).
// I assume that SUNPRO will fix this in future versions.
// With other compilers we get warnings (and
-// potential problems) if we dont make the baseNMIU
+// potential problems) if we don't make the baseNMIU
// destructor virtual.
#if defined ( __SUNPRO_CC ) && ( __SUNPRO_CC <= 0x540 )
# define NETIO_VIRTUAL_DESTRUCTOR
diff --git a/modules/ca/src/client/oldAccess.h b/modules/ca/src/client/oldAccess.h
index cace26ec1..2c39d2768 100644
--- a/modules/ca/src/client/oldAccess.h
+++ b/modules/ca/src/client/oldAccess.h
@@ -591,16 +591,16 @@ void ca_client_context :: whenThereIsAnExceptionDestroySyncGroupIO (
io.destroy ( *this->pCallbackGuard.get(), guard );
}
else {
- // dont reverse the lock hierarchy
+ // don't reverse the lock hierarchy
epicsGuardRelease < epicsMutex > guardRelease ( guard );
{
//
// we will definately stall out here if all of the
// following are true
//
- // o user creates non-preemtive mode client library context
+ // o user creates non-preemptive mode client library context
// o user doesnt periodically call a ca function
- // o user calls this function from an auxiillary thread
+ // o user calls this function from an auxiliary thread
//
CallbackGuard cbGuard ( this->cbMutex );
epicsGuard < epicsMutex > guard ( this->mutex );
diff --git a/modules/ca/src/client/oldChannelNotify.cpp b/modules/ca/src/client/oldChannelNotify.cpp
index 0716a430b..71688c94c 100644
--- a/modules/ca/src/client/oldChannelNotify.cpp
+++ b/modules/ca/src/client/oldChannelNotify.cpp
@@ -571,7 +571,7 @@ int epicsStdCall ca_create_subscription (
oldSubscription (
guard, *pChan, pChan->io, tmpType, count, mask,
pCallBack, pCallBackArg, monixptr );
- // dont touch object created after above new because
+ // don't touch object created after above new because
// the first callback might have canceled, and therefore
// destroyed, it
return ECA_NORMAL;
diff --git a/modules/ca/src/client/oldSubscription.cpp b/modules/ca/src/client/oldSubscription.cpp
index b1290b2b8..72a51d9ad 100644
--- a/modules/ca/src/client/oldSubscription.cpp
+++ b/modules/ca/src/client/oldSubscription.cpp
@@ -41,7 +41,7 @@ oldSubscription::oldSubscription (
*pEventId = this;
}
io.subscribe ( guard, type, nElem, mask, *this, &this->id );
- // Dont touch this pointer after this point because the
+ // Don't touch this pointer after this point because the
// 1st update callback might cancel the subscription and
// thereby destroy this object.
}
diff --git a/modules/ca/src/client/repeater.cpp b/modules/ca/src/client/repeater.cpp
index e597d66f6..e198a25d4 100644
--- a/modules/ca/src/client/repeater.cpp
+++ b/modules/ca/src/client/repeater.cpp
@@ -79,7 +79,7 @@
/*
* these can be external since there is only one instance
- * per machine so we dont care about reentrancy
+ * per machine so we don't care about reentrancy
*/
static tsDLList < repeaterClient > client_list;
@@ -335,7 +335,7 @@ static void fanOut ( const osiSockAddr & from, const void * pMsg,
while ( ( pclient = client_list.get () ) ) {
theClients.add ( *pclient );
- /* Dont reflect back to sender */
+ /* Don't reflect back to sender */
if ( pclient->identicalAddress ( from ) ) {
continue;
}
@@ -392,7 +392,7 @@ static void register_new_client ( osiSockAddr & from,
* repeater would not always allow the loopback address
* as a local client address so current clients alternate
* between the address of the first non-loopback interface
- * found and the loopback addresss when subscribing with
+ * found and the loopback address when subscribing with
* the CA repeater until all CA repeaters have been updated
* to current code.
*/
@@ -452,7 +452,7 @@ static void register_new_client ( osiSockAddr & from,
}
/*
- * send a noop message to all other clients so that we dont
+ * send a noop message to all other clients so that we don't
* accumulate sockets when there are no beacons
*/
caHdr noop;
diff --git a/modules/ca/src/client/searchTimer.cpp b/modules/ca/src/client/searchTimer.cpp
index 5a4fa446a..60b153639 100644
--- a/modules/ca/src/client/searchTimer.cpp
+++ b/modules/ca/src/client/searchTimer.cpp
@@ -279,8 +279,8 @@ epicsTimerNotify::expireStatus searchTimer::expire (
if ( this->searchAttempts ) {
char buf[64];
currentTime.strftime ( buf, sizeof(buf), "%M:%S.%09f");
- debugPrintf ( ("sent %u delay sec=%f Rts=%s\n",
- nFrameSent, this->period(), buf ) );
+ debugPrintf ( ("sent %u delay Rts=%s\n",
+ nFrameSent, buf ) );
}
# endif
@@ -317,7 +317,7 @@ void searchTimer :: show ( unsigned level ) const
//
// Reset the delay to the next search request if we get
-// at least one response. However, dont reset this delay if we
+// at least one response. However, don't reset this delay if we
// get a delayed response to an old search request.
//
void searchTimer::uninstallChanDueToSuccessfulSearchResponse (
diff --git a/modules/ca/src/client/searchTimer.h b/modules/ca/src/client/searchTimer.h
index 74c8bde6e..ca5d2c55d 100644
--- a/modules/ca/src/client/searchTimer.h
+++ b/modules/ca/src/client/searchTimer.h
@@ -82,8 +82,8 @@ private:
double framesPerTry; /* # of UDP frames per search try */
double framesPerTryCongestThresh; /* one half N tries w congest */
unsigned retry;
- unsigned searchAttempts; /* num search tries after last timer experation */
- unsigned searchResponses; /* num search resp after last timer experation */
+ unsigned searchAttempts; /* num search tries after last timer expiration */
+ unsigned searchResponses; /* num search resp after last timer expiration */
const unsigned index;
ca_uint32_t dgSeqNoAtTimerExpireBegin;
ca_uint32_t dgSeqNoAtTimerExpireEnd;
diff --git a/modules/ca/src/client/syncgrp.cpp b/modules/ca/src/client/syncgrp.cpp
index bee6ea8dc..4f79348fd 100644
--- a/modules/ca/src/client/syncgrp.cpp
+++ b/modules/ca/src/client/syncgrp.cpp
@@ -82,9 +82,9 @@ extern "C" int epicsStdCall ca_sg_delete ( const CA_SYNC_GID gid )
// we will definately stall out here if all of the
// following are true
//
- // o user creates non-preemtive mode client library context
+ // o user creates non-preemptive mode client library context
// o user doesnt periodically call a ca function
- // o user calls this function from an auxiillary thread
+ // o user calls this function from an auxiliary thread
//
CallbackGuard cbGuard ( pcac->cbMutex );
epicsGuard < epicsMutex > guard ( pcac->mutex );
@@ -106,9 +106,9 @@ void sync_group_reset ( ca_client_context & client, CASG & sg )
// we will definately stall out here if all of the
// following are true
//
- // o user creates non-preemtive mode client library context
+ // o user creates non-preemptive mode client library context
// o user doesnt periodically call a ca function
- // o user calls this function from an auxiillary thread
+ // o user calls this function from an auxiliary thread
//
CallbackGuard cbGuard ( client.cbMutex );
epicsGuard < epicsMutex > guard ( client.mutex );
@@ -219,9 +219,9 @@ extern "C" int epicsStdCall ca_sg_test ( const CA_SYNC_GID gid )
// we will definately stall out here if all of the
// following are true
//
- // o user creates non-preemtive mode client library context
+ // o user creates non-preemptive mode client library context
// o user doesnt periodically call a ca function
- // o user calls this function from an auxiillary thread
+ // o user calls this function from an auxiliary thread
//
CallbackGuard cbGuard ( pcac->cbMutex );
epicsGuard < epicsMutex > guard ( pcac->mutex );
diff --git a/modules/ca/src/client/tcpRecvWatchdog.cpp b/modules/ca/src/client/tcpRecvWatchdog.cpp
index 9e0bf0188..367e09237 100644
--- a/modules/ca/src/client/tcpRecvWatchdog.cpp
+++ b/modules/ca/src/client/tcpRecvWatchdog.cpp
@@ -176,7 +176,7 @@ void tcpRecvWatchdog::sendBacklogProgressNotify (
{
guard.assertIdenticalMutex ( this->mutex );
- // We dont set "beaconAnomaly" to be false here because, after we see a
+ // We don't set "beaconAnomaly" to be false here because, after we see a
// beacon anomaly (which could be transiently detecting a reboot) we will
// not trust the beacon as an indicator of a healthy server until we
// receive at least one message from the server.
diff --git a/modules/ca/src/client/tcpiiu.cpp b/modules/ca/src/client/tcpiiu.cpp
index e601c8d24..30f6fad17 100644
--- a/modules/ca/src/client/tcpiiu.cpp
+++ b/modules/ca/src/client/tcpiiu.cpp
@@ -81,7 +81,7 @@ void tcpSendThread::run ()
while ( true ) {
- // dont wait if there is still labor to be done below
+ // don't wait if there is still labor to be done below
if ( ! laborPending ) {
epicsGuardRelease < epicsMutex > unguard ( guard );
this->iiu.sendThreadFlushEvent.wait ();
@@ -124,7 +124,7 @@ void tcpSendThread::run ()
}
else {
// This wakes up the resp thread so that it can call
- // the connect callback. This isnt maximally efficent
+ // the connect callback. This isn't maximally efficient
// but it has the excellent side effect of not requiring
// that the UDP thread take the callback lock. There are
// almost no V42 servers left at this point.
@@ -178,7 +178,7 @@ void tcpSendThread::run ()
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- errlogPrintf ("CAC TCP clean socket shutdown error was %s\n",
+ errlogPrintf ("CAC TCP clean socket shutdown " ERL_ERROR " was %s\n",
sockErrBuf );
}
}
@@ -194,7 +194,7 @@ void tcpSendThread::run ()
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- errlogPrintf ("CAC TCP clean socket shutdown error was %s\n",
+ errlogPrintf ("CAC TCP clean socket shutdown " ERL_ERROR " was %s\n",
sockErrBuf );
}
}
@@ -204,7 +204,7 @@ void tcpSendThread::run ()
while ( ! this->iiu.recvThread.exitWait ( 30.0 ) ) {
// it is possible to get stuck here if the user calls
- // ca_context_destroy() when a circuit isnt known to
+ // ca_context_destroy() when a circuit isn't known to
// be unresponsive, but is. That situation is probably
// rare, and the IP kernel might have a timeout for
// such situations, nevertheless we will attempt to deal
@@ -283,7 +283,7 @@ unsigned tcpiiu::sendBytes ( const void *pBuf,
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- errlogPrintf ( "CAC: unexpected TCP send error: %s\n",
+ errlogPrintf ( "CAC: unexpected TCP send " ERL_ERROR ": %s\n",
sockErrBuf );
}
@@ -322,7 +322,7 @@ void tcpiiu::recvBytes (
return;
}
- // if the circuit was locally aborted then supress
+ // if the circuit was locally aborted then suppress
// warning messages about bad file descriptor etc
if ( this->state != iiucs_connected &&
this->state != iiucs_clean_shutdown ) {
@@ -358,9 +358,9 @@ void tcpiiu::recvBytes (
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- // the replacable printf handler isnt called here
- // because it reqires a callback lock which probably
- // isnt appropriate here
+ // the replaceable printf handler isn't called here
+ // because it requires a callback lock which probably
+ // isn't appropriate here
char name[64];
this->hostNameCacheInstance.getName (
name, sizeof ( name ) );
@@ -535,11 +535,11 @@ void tcpRecvThread::run ()
}
//
- // we dont feel comfortable calling this with a lock applied
+ // we don't feel comfortable calling this with a lock applied
// (it might block for longer than we like)
//
- // we would prefer to improve efficency by trying, first, a
- // recv with the new MSG_DONTWAIT flag set, but there isnt
+ // we would prefer to improve efficiency by trying, first, a
+ // recv with the new MSG_DONTWAIT flag set, but there isn't
// universal support
//
bool bytesArePending = this->iiu.bytesArePendingInOS ();
@@ -957,7 +957,7 @@ void tcpiiu::initiateAbortShutdown (
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- errlogPrintf ( "CAC TCP socket linger set error was %s\n",
+ errlogPrintf ( "CAC TCP socket linger set " ERL_ERROR " was %s\n",
sockErrBuf );
}
this->discardingPendingData = true;
@@ -988,7 +988,7 @@ void tcpiiu::initiateAbortShutdown (
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- errlogPrintf ("CAC TCP socket shutdown error was %s\n",
+ errlogPrintf ("CAC TCP socket shutdown " ERL_ERROR " was %s\n",
sockErrBuf );
}
}
@@ -1002,7 +1002,7 @@ void tcpiiu::initiateAbortShutdown (
};
//
- // wake up the send thread if it isnt blocking in send()
+ // wake up the send thread if it isn't blocking in send()
//
this->sendThreadFlushEvent.signal ();
this->flushBlockEvent.signal ();
@@ -1058,7 +1058,7 @@ void tcpiiu::show ( unsigned level ) const
this->_receiveThreadIsBusy );
}
if ( level > 2u ) {
- ::printf ( "\tvirtual circuit socket identifier %d\n", this->sock );
+ ::printf ( "\tvirtual circuit socket identifier %d\n", (int)this->sock );
::printf ( "\tsend thread flush signal:\n" );
this->sendThreadFlushEvent.show ( level-2u );
::printf ( "\tsend thread:\n" );
@@ -1580,7 +1580,7 @@ void tcpiiu::subscriptionRequest (
maxBytes = MAX_TCP;
}
unsigned dataType = subscr.getType ( guard );
- // data type bounds checked when sunscription created
+ // data type bounds checked when subscription created
arrayElementCount maxElem = ( maxBytes - dbr_size[dataType] ) / dbr_value_size[dataType];
if ( nElem > maxElem ) {
throw cacChannel::msgBodyCacheTooSmall ();
@@ -1633,7 +1633,7 @@ void tcpiiu::subscriptionUpdateRequest (
throw cacChannel::msgBodyCacheTooSmall ();
}
comQueSendMsgMinder minder ( this->sendQue, guard );
- // nElem boounds checked above
+ // nElem bounds checked above
this->sendQue.insertRequestHeader (
CA_PROTO_READ_NOTIFY, 0u,
static_cast < ca_uint16_t > ( dataType ),
@@ -1712,7 +1712,7 @@ void tcpiiu :: flush ( epicsGuard < epicsMutex > & guard )
this->flushRequest ( guard );
// the process thread is not permitted to flush as this
// can result in a push / pull deadlock on the TCP pipe.
- // Instead, the process thread scheduals the flush with the
+ // Instead, the process thread schedules the flush with the
// send thread which runs at a higher priority than the
// receive thread. The same applies to the UDP thread for
// locking hierarchy reasons.
@@ -1817,7 +1817,7 @@ void tcpiiu::disconnectAllChannels (
}
while ( nciu * pChan = this->createRespPend.get () ) {
- // we dont yet know the server's id so we cant
+ // we don't yet know the server's id so we cant
// send a channel delete request and will instead
// trust that the server can do the proper cleanup
// when the circuit disconnects
@@ -1848,7 +1848,7 @@ void tcpiiu::disconnectAllChannels (
while ( nciu * pChan = this->unrespCircuit.get () ) {
// if we know that the circuit is unresponsive
- // then we dont send a channel delete request and
+ // then we don't send a channel delete request and
// will instead trust that the server can do the
// proper cleanup when the circuit disconnects
pChan->disconnectAllIO ( cbGuard, guard );
@@ -1883,7 +1883,7 @@ void tcpiiu::unlinkAllChannels (
while ( nciu * pChan = this->createRespPend.get () ) {
pChan->channelNode::listMember =
channelNode::cs_none;
- // we dont yet know the server's id so we cant
+ // we don't yet know the server's id so we cant
// send a channel delete request and will instead
// trust that the server can do the proper cleanup
// when the circuit disconnects
@@ -1921,7 +1921,7 @@ void tcpiiu::unlinkAllChannels (
channelNode::cs_none;
pChan->disconnectAllIO ( cbGuard, guard );
// if we know that the circuit is unresponsive
- // then we dont send a channel delete request and
+ // then we don't send a channel delete request and
// will instead trust that the server can do the
// proper cleanup when the circuit disconnects
pChan->serviceShutdownNotify ( cbGuard, guard );
@@ -1951,7 +1951,7 @@ void tcpiiu::installChannel (
this->channelCountTot++;
chan.channelNode::listMember = channelNode::cs_createReqPend;
chan.searchReplySetUp ( *this, sidIn, typeIn, countIn, guard );
- // The tcp send thread runs at apriority below the udp thread
+ // The tcp send thread runs at a priority below the udp thread
// so that this will not send small packets
this->sendThreadFlushEvent.signal ();
}
@@ -2060,7 +2060,7 @@ bool tcpiiu::bytesArePendingInOS () const
}
return false;
#else
- osiSockIoctl_t bytesPending = 0; /* shut up purifys yapping */
+ osiSockIoctl_t bytesPending = 0; /* shut up Purify's yapping */
int status = socket_ioctl ( this->sock,
FIONREAD, & bytesPending );
if ( status >= 0 ) {
diff --git a/modules/ca/src/client/test/ca_test.c b/modules/ca/src/client/test/ca_test.c
index 0d1ec2f57..adae20b84 100644
--- a/modules/ca/src/client/test/ca_test.c
+++ b/modules/ca/src/client/test/ca_test.c
@@ -279,7 +279,7 @@ skip_rest:
/*
* wait for the operation to complete
- * (outstabnding decrements to zero)
+ * (outstanding decrements to zero)
*/
while(ntries){
ca_pend_event(1.0);
diff --git a/modules/ca/src/client/udpiiu.cpp b/modules/ca/src/client/udpiiu.cpp
index 723117a71..c6ffb6def 100644
--- a/modules/ca/src/client/udpiiu.cpp
+++ b/modules/ca/src/client/udpiiu.cpp
@@ -202,7 +202,7 @@ udpiiu::udpiiu (
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- errlogPrintf("CAC: failed to set mcast ttl %d\n", ttl);
+ errlogPrintf("CAC: failed to set mcast ttl %d\n", (int)ttl);
}
}
#endif
@@ -266,7 +266,7 @@ udpiiu::udpiiu (
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
epicsSocketDestroy ( this->sock );
- errlogPrintf ( "CAC: getsockname () error was \"%s\"\n", sockErrBuf );
+ errlogPrintf ( "CAC: getsockname () " ERL_ERROR " was \"%s\"\n", sockErrBuf );
throwWithLocation ( noSocket () );
}
if ( tmpAddr.sa.sa_family != AF_INET) {
@@ -428,7 +428,7 @@ void udpRecvThread::run ()
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- errlogPrintf ( "CAC: UDP recv error was \"%s\"\n",
+ errlogPrintf ( "CAC: UDP recv " ERL_ERROR " was \"%s\"\n",
sockErrBuf );
}
}
@@ -536,9 +536,9 @@ void epicsStdCall caRepeaterRegistrationMessage (
if ( status < 0 ) {
int errnoCpy = SOCKERRNO;
/*
- * Different OS return different codes when the repeater isnt running.
- * Its ok to supress these messages because I print another warning message
- * if we time out registerring with the repeater.
+ * Different OS return different codes when the repeater isn't running.
+ * Its ok to suppress these messages because I print another warning message
+ * if we time out registering with the repeater.
*
* Linux returns SOCK_ECONNREFUSED
* Windows 2000 returns SOCK_ECONNRESET
@@ -673,7 +673,7 @@ bool udpiiu :: searchRespAction (
const epicsTime & currentTime )
{
/*
- * we dont currently know what to do with channel's
+ * we don't currently know what to do with channel's
* found to be at non-IP type addresses
*/
if ( addr.sa.sa_family != AF_INET ) {
@@ -762,7 +762,7 @@ bool udpiiu::beaconAction (
* always set this field to INADDR_ANY
*
* clients always assume that if this
- * field is set to something that isnt INADDR_ANY
+ * field is set to something that isn't INADDR_ANY
* then it is the overriding IP address of the server.
*/
ina.sin_family = AF_INET;
@@ -772,7 +772,7 @@ bool udpiiu::beaconAction (
}
else {
/*
- * old servers dont supply this and the
+ * old servers don't supply this and the
* default port must be assumed
*/
ina.sin_port = htons ( this->serverPort );
@@ -874,7 +874,7 @@ void udpiiu::postMsg (
size = pCurMsg->m_postsize + sizeof ( *pCurMsg );
/*
- * dont allow msg body extending beyond frame boundary
+ * don't allow msg body extending beyond frame boundary
*/
if ( size > blockSize ) {
char buf[64];
@@ -1044,7 +1044,7 @@ void udpiiu :: SearchRespCallback :: notify (
const osiSockAddr & addr, const epicsTime & currentTime )
{
/*
- * we dont currently know what to do with channel's
+ * we don't currently know what to do with channel's
* found to be at non-IP type addresses
*/
if ( addr.sa.sa_family != AF_INET ) {
@@ -1119,7 +1119,7 @@ bool udpiiu :: datagramFlush (
{
guard.assertIdenticalMutex ( cacMutex );
- // dont send the version header by itself
+ // don't send the version header by itself
if ( this->nBytesInXmitBuf <= sizeof ( caHdr ) ) {
return false;
}
diff --git a/modules/ca/src/perl/Cap5.xs b/modules/ca/src/perl/Cap5.xs
index 6d06a15f1..5da894808 100644
--- a/modules/ca/src/perl/Cap5.xs
+++ b/modules/ca/src/perl/Cap5.xs
@@ -12,6 +12,13 @@
* here and just generates unnecessary compiler warnings. */
#define REENTRINC
+/* Clang-12 and later generates many warnings about compound token */
+#ifdef __has_warning
+# if __has_warning("-Wcompound-token-split-by-macro")
+# pragma clang diagnostic ignored "-Wcompound-token-split-by-macro"
+# endif
+#endif
+
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
@@ -201,6 +208,8 @@ SV * newSVdbr(struct event_handler_args *peha) {
if (is_primitive) {
if (value_type == DBR_CHAR) {
/* Long string => Perl scalar */
+ if (peha->count == 0)
+ return newSVpvn(peha->dbr, 0);
((char *)peha->dbr) [peha->count - 1] = 0;
return newSVpv(peha->dbr, 0);
}
@@ -271,8 +280,12 @@ SV * newSVdbr(struct event_handler_args *peha) {
char *str = dbr_value_ptr(peha->dbr, peha->type);
/* Long string => Perl scalar */
- str[peha->count - 1] = 0;
- val = newSVpv(str, 0);
+ if (peha->count == 0)
+ val = newSVpvn(str, 0);
+ else {
+ str[peha->count - 1] = 0;
+ val = newSVpv(str, 0);
+ }
} else if (peha->count == 1) {
/* Single value => Perl scalar */
val = newSVdbf(value_type,
diff --git a/modules/ca/src/perl/capr.pl b/modules/ca/src/perl/capr.pl
index ba331cf8e..fdb85dc7f 100644
--- a/modules/ca/src/perl/capr.pl
+++ b/modules/ca/src/perl/capr.pl
@@ -1,6 +1,6 @@
#!/usr/bin/env perl
#*************************************************************************
-# Copyright (c) 2005 UChicago Argonne LLC, as Operator of Argonne
+# Copyright (c) 2022 UChicago Argonne LLC, as Operator of Argonne
# National Laboratory.
# SPDX-License-Identifier: EPICS
# EPICS BASE is distributed subject to a Software License Agreement found
@@ -14,26 +14,24 @@
#
#######################################################################
+use 5.10.1;
use strict;
-use FindBin qw($Bin);
-use lib ("$Bin/../../lib/perl");
+use FindBin qw($RealBin);
+use lib ("$RealBin/../../lib/perl");
use Getopt::Std;
-use EPICS::Path;
+use Cwd 'abs_path';
use CA;
######### Globals ##########
-our ($opt_h, $opt_f, $opt_r);
-our $opt_d = $ENV{EPICS_CAPR_DBD_FILE} || "$Bin/../../dbd/softIoc.dbd";
-our $opt_w = 1;
+our ($opt_h, $opt_f, $opt_l, $opt_r, $opt_D);
+our $opt_d = $ENV{EPICS_CAPR_DBD_FILE} // abs_path("$RealBin/../../dbd/softIoc.dbd");
+our $opt_w = 2;
+our $opt_n = 10;
my %record = (); # Empty hash to put dbd data in
-my $iIdx = 0; # Array indexes for interest, data type and base
-my $tIdx = 1;
-my $bIdx = 2;
-my %device = (); # Empty hash to record which rec types have device support
# EPICS field types
my %fieldType = (
@@ -58,13 +56,14 @@ my %fieldType = (
);
# globals for sub caget
+my %connected;
+my $unconnected_count;
my %callback_data;
-my %timed_out;
my $callback_incomplete;
######### Main program ############
-HELP_MESSAGE() unless getopts('hd:f:rw:');
+HELP_MESSAGE() unless getopts('hDd:f:l:n:rw:');
HELP_MESSAGE() if $opt_h;
die "File $opt_d not found. (\"capr.pl -h\" gives help)\n"
@@ -76,7 +75,7 @@ print "Using $opt_d\n\n";
# Print a list of record types
if ($opt_r) {
print ("Record types found:\n");
- printList(0);
+ printList($opt_l);
exit;
}
@@ -100,7 +99,7 @@ if (@ARGV) {
} else {
# Drop any ".FIELD" part
s/\. \w+ $//x;
- printRecord($_, 0);
+ printRecord($_, $opt_l);
}
}
@@ -130,9 +129,11 @@ sub parseDbd {
my $thisRecord;
my $thisField;
my $thisType;
+ my $thisSize = 0;
my $field = {};
my $interest = 0;
my $thisBase = 'DECIMAL';
+ my $special = '';
while (@dbd) {
$_ = shift @dbd;
@@ -149,35 +150,47 @@ sub parseDbd {
$thisField = $1;
$thisType = $2;
$isAfield = 1;
+ $thisSize = 1024 if $thisType =~ m/^ DBF_(IN|OUT|FWD)LINK $/x;
}
elsif ( m/interest \s* \( \s* (\w+) \s* \)/x ) {
die "File format error at line $i of file\n $opt_d\n"
unless $level == 2 && $isAfield;
$interest = $1;
}
+ elsif ( m/size \s* \( \s* ([0-9]+) \s* \)/x ) {
+ die "File format error at line $i of file\n $opt_d\n"
+ unless $level == 2 && $isAfield;
+ $thisSize = $1;
+ }
+ elsif ( m/special \s* \( \s* (\w+) \s* \)/x ) {
+ die "File format error at line $i of file\n $opt_d\n"
+ unless $level == 2 && $isAfield;
+ $special = $1;
+ }
elsif ( m/base \s* \( \s* (\w+) \s* \)/x ) {
die "File format error at line $i of file\n $opt_d\n"
unless $level == 2 && $isAfield;
$thisBase = $1;
}
- elsif ( m/device \s* \( (\w+) \s* ,/x ) {
- die "File format error at line $i of file\n $opt_d\n"
- unless $level == 0;
- $device{$1}++;
- }
if ( m/\{/ ) {
$level++;
}
if ( m/\}/ ) {
if ($level == 2 && $isAfield) {
- my $params = [];
- $params->[$iIdx] = $interest;
- $params->[$tIdx] = $thisType;
- $params->[$bIdx] = $thisBase;
+ my $params = {};
+ $params->{interest} = $interest;
+ $params->{dbfType} = $thisType;
+ $params->{base} = $thisBase;
+ $params->{special} = $special;
+ $params->{size} = $thisSize;
$field->{$thisField} = $params;
+
+ # Reset default values
$isAfield = 0;
- $interest = 0; # reset default
- $thisBase = 'DECIMAL'; # reset default
+ $interest = 0;
+ $thisBase = 'DECIMAL';
+ $special = '';
+ $thisSize = 0;
}
elsif ($level == 1 && $isArecord) {
$isArecord = 0;
@@ -207,21 +220,23 @@ sub getRecType {
# Given the record type and field, returns the interest level, data type
# and number base for the field
-# Usage: ($dataType, $interest, $base) = getFieldParams($recType, $field);
+# Usage: ($dataType, $interest, $base, $special, $size) = getFieldParams($recType, $field);
sub getFieldParams {
my ($recType, $field) = @_;
my $params = $record{$recType}{$field} or
die "Can't find params for $recType.$field";
- exists($fieldType{$params->[$tIdx]}) ||
+ exists($fieldType{$params->{dbfType}}) ||
die "Field data type $field for $recType not found in dbd file --";
- exists($params->[$iIdx]) ||
+ exists($params->{interest}) ||
die "Interest level for $field in $recType not found in dbd file --";
- my $fType = $fieldType{$params->[$tIdx]};
- my $fInterest = $params->[$iIdx];
- my $fBase = $params->[$bIdx];
- return ($fType, $fInterest, $fBase);
+ my $fType = $fieldType{$params->{dbfType}};
+ my $fInterest = $params->{interest};
+ my $fBase = $params->{base};
+ my $fSpecial = $params->{special};
+ my $fSize = $params->{size};
+ return ($fType, $fInterest, $fBase, $fSpecial, $fSize);
}
# Prints field name and data for given field. Formats output so
@@ -233,27 +248,37 @@ sub printField {
my $screenWidth = 80;
my ($outStr, $wide);
- my $field = "$fieldName:";
- if ( $dataType eq 'DBF_STRING' ) {
- $outStr = sprintf('%-5s %s', $field, $fieldData);
- } elsif ( $base eq 'HEX' ) {
- my $val = ( $dataType eq 'DBF_CHAR' ) ? ord($fieldData) : $fieldData;
- $outStr = sprintf('%-5s 0x%x', $field, $val);
- } elsif ( $dataType eq 'DBF_DOUBLE' || $dataType eq 'DBF_FLOAT' ) {
- $outStr = sprintf('%-5s %.8f', $field, $fieldData);
- } elsif ( $dataType eq 'DBF_CHAR' ) {
- $outStr = sprintf('%-5s %d', $field, ord($fieldData));
- } else {
- # DBF_INT64, DBF_LONG, DBF_SHORT,
- # DBF_UINT64, DBF_ULONG, DBF_USHORT, DBF_UCHAR,
- $outStr = sprintf('%-5s %d', $field, $fieldData);
+ if (ref $fieldData eq 'ARRAY') {
+ my $elems = scalar @{$fieldData};
+ my $field = "$fieldName\[$elems\]:";
+ my $count = $elems > $opt_n ? $opt_n : $elems;
+ my @show = @{$fieldData}[0 .. $count - 1];
+ $outStr = sprintf('%-5s %s', $field, join(', ', @show));
+ $outStr .= ", ..." if $elems > $count;
+ }
+ else {
+ my $field = "$fieldName:";
+ if ( $dataType eq 'DBF_STRING' ) {
+ $outStr = sprintf('%-5s %s', $field, $fieldData);
+ } elsif ( $base eq 'HEX' ) {
+ my $val = ( $dataType eq 'DBF_CHAR' ) ? ord($fieldData) : $fieldData;
+ $outStr = sprintf('%-5s 0x%x', $field, $val);
+ } elsif ( $dataType eq 'DBF_DOUBLE' || $dataType eq 'DBF_FLOAT' ) {
+ $outStr = sprintf('%-5s %.8f', $field, $fieldData);
+ } elsif ( $dataType eq 'DBF_UCHAR' ) {
+ $outStr = sprintf('%-5s %d', $field, ord($fieldData));
+ } else {
+ # DBF_INT64, DBF_LONG, DBF_SHORT,
+ # DBF_UINT64, DBF_ULONG, DBF_USHORT, DBF_UCHAR,
+ $outStr = sprintf('%-5s %d', $field, $fieldData);
+ }
}
my $len = length($outStr);
- if ($len <= 20) { $wide = 20; }
- elsif ( $len <= 40 ) { $wide = 40; }
- elsif ( $len <= 60 ) { $wide = 60; }
+ if ($len < 20) { $wide = 20; }
+ elsif ( $len < 40 ) { $wide = 40; }
+ elsif ( $len < 60 ) { $wide = 60; }
else { $wide = 80;}
my $pad = $wide - $len;
@@ -269,49 +294,64 @@ sub printField {
return $col;
}
-# Query for a list of fields simultaneously.
-# The results are filled in the the %callback_data global hash
-# and the result of the operation is the number of read pvs
+# Query the native values of a list of PVs simultaneously.
+# The data is returned in the the %callback_data global hash.
+# The return value is the number of read pvs
#
# NOTE: Not re-entrant because results are written to global hash
# %callback_data
#
# Usage: $fields_read = caget( @pvlist )
sub caget {
- my @chans = map { CA->new($_); } @_;
-
#clear any previous results;
+ %connected = ();
+ $unconnected_count = scalar @_;
%callback_data = ();
- %timed_out = ();
+ $callback_incomplete = 0;
- eval { CA->pend_io($opt_w); };
- if ($@) {
- if ($@ =~ m/^ECA_TIMEOUT/) {
- my $name = $chans[0]->name;
- my $err = (@chans > 1) ? 'some fields' : "'$name'";
- print "Channel connect timed out: $err not found.\n";
- foreach my $chan (@chans) {
- $timed_out{$chan->name} = !$chan->is_connected;
- }
- @chans = grep { $_->is_connected } @chans;
- } else {
- die $@;
- }
- }
+ my @chans = map {
+ print " Creating channel for $_\n" if $opt_D;
+ CA->new($_, \&canew_callback);
+ } @_;
+ my $channel_count = scalar @chans;
+ return 0 unless $channel_count gt 0;
- map {
- $_->get_callback(\&caget_callback, $_->field_type);
- } @chans;
+ print " $channel_count channels created.\n" if $opt_D;
- my $fields_read = $callback_incomplete = @chans;
- CA->pend_event(0.1)
- while $callback_incomplete;
- return $fields_read;
+ my $elapsed = 0;
+ do {
+ print " Waiting for $unconnected_count channels to connect\n"
+ if $unconnected_count && $opt_D;
+ print " Waiting for data from $callback_incomplete channels\n"
+ if $callback_incomplete && $opt_D;
+ CA->pend_event(0.1);
+ $elapsed += 0.1;
+ } until (($elapsed > $opt_w) or
+ (scalar %connected && $callback_incomplete == 0));
+ my $data_count = scalar keys %callback_data;
+ printf " Got data from %d of %d channels\n", $data_count, $channel_count
+ if $opt_D;
+ return $data_count;
+}
+
+sub canew_callback {
+ my ($chan, $up) = @_;
+ return unless $up;
+ $connected{$chan->name} = $chan;
+ $unconnected_count--;
+ my $ftype = $chan->field_type;
+ my $count = $chan->element_count;
+ $ftype = 'DBR_LONG' if $ftype eq 'DBR_CHAR' && $count == 1;
+ print " Getting ${\$chan->name} as $ftype\n" if $opt_D;
+ # We have to fetch all elements so we can show how many there are
+ $chan->get_callback(\&caget_callback, $ftype);
+ $callback_incomplete++;
}
sub caget_callback {
my ($chan, $status, $data) = @_;
die $status if $status;
+ print " Got ${\$chan->name} = '$data'\n" if $opt_D;
$callback_data{$chan->name} = $data;
$callback_incomplete--;
}
@@ -333,69 +373,83 @@ sub printRecord {
my @ftypes = (); #types, from parser
my @bases = (); #bases, from parser
foreach my $field (sort keys %{$record{$recType}}) {
- # Skip DTYP field if this rec type doesn't have device support defined
- next if $field eq 'DTYP' && !exists($device{$recType});
-
- my ($fType, $fInterest, $base) = getFieldParams($recType, $field);
- # FIXME: Support waveform.VAL fields etc.
- unless( $fType eq 'DBF_NOACCESS' ) {
- if ($interest >= $fInterest ) {
- my $fToGet = "$name.$field";
- push @fields_pr, $field;
- push @readlist, $fToGet;
- push @ftypes, $fType;
- push @bases, $base;
- }
+ my ($fType, $fInterest, $base, $special, $size) =
+ getFieldParams($recType, $field);
+ next if $fInterest > $interest;
+ my $fToGet = "$name.$field";
+ if ($fType eq 'DBF_NOACCESS') {
+ next unless $special eq 'SPC_DBADDR';
+ $fType = 'DBF_STRING';
}
+ elsif ($fType eq 'DBF_STRING' && $size >= 40) {
+ $fToGet .= '$';
+ }
+ push @fields_pr, $field;
+ push @readlist, $fToGet;
+ push @ftypes, $fType;
+ push @bases, $base;
}
my $fields_read = caget( @readlist );
+ my @missing;
+
# print while iterating over lists gathered
my $col = 0;
for (my $i=0; $i < scalar @readlist; $i++) {
my $field = $fields_pr[$i];
my $fToGet = $readlist[$i];
my ($fType, $data, $base);
- next if $timed_out{$fToGet};
+ push @missing, $field unless exists $callback_data{$fToGet};
+ next unless exists $callback_data{$fToGet};
$fType = $ftypes[$i];
$base = $bases[$i];
$data = $callback_data{$fToGet};
$col = printField($field, $data, $fType, $base, $col);
}
print("\n"); # Final newline
+
+ printf "\nUnreadable fields: %s\n", join(', ', @missing) if @missing && $opt_D;
}
# Prints list of record types found in dbd file. If level > 0
-# then the fields of that record type, their interest levels and types are
-# also printed.
-# Diagnostic routine, usage: void printList(level);
+# then uses printRecordList to display all fields in each record type.
sub printList {
my $level = shift;
foreach my $rkey (sort keys(%record)) {
- print(" $rkey\n");
if ($level > 0) {
- foreach my $fkey (keys %{$record{$rkey}}) {
- print("\tField $fkey - interest $record{$rkey}{$fkey}[$iIdx] ");
- print("- type $record{$rkey}{$fkey}[$tIdx] ");
- print("- base $record{$rkey}{$fkey}[$bIdx]\n");
- }
+ printRecordList($rkey);
+ }
+ else {
+ print(" $rkey\n");
}
}
}
-# Prints list of fields with interest levels for given record type
-# Diagnostic routine, usage: void printRecordList("recordType");
+# Prints list of fields and metadata for given record type
sub printRecordList {
my $type = shift;
if (exists($record{$type}) ) {
print("Record type - $type\n");
foreach my $fkey (sort keys %{$record{$type}}) {
- printf('%-8s', $fkey);
- printf(" interest = $record{$type}{$fkey}[$iIdx]");
- printf(" type = %-12s ",$record{$type}{$fkey}[$tIdx]);
- print (" base = $record{$type}{$fkey}[$bIdx]\n");
+ my $param = $record{$type}{$fkey};
+ my $dbfType = $param->{dbfType};
+ printf " %-8s", $fkey;
+ printf " interest = %d", $param->{interest};
+ printf " type = %-12s", $dbfType;
+ if ($dbfType eq 'DBF_STRING') {
+ printf " size = %s\n", $param->{size};
+ }
+ elsif ($dbfType =~ m/DBF_U?(CHAR|SHORT|INT|LONG|INT64)/) {
+ printf " base = %s\n", $param->{base};
+ }
+ elsif ($dbfType eq 'DBF_NOACCESS') {
+ printf " special = %s\n", $param->{special};
+ }
+ else {
+ print "\n";
+ }
}
}
else {
@@ -411,16 +465,20 @@ sub HELP_MESSAGE {
" capr.pl [options] []\n",
"\n",
" -h Print this help message.\n",
+ " -D Print debug messages.\n",
"Channel Access options:\n",
" -w : Wait time, specifies CA timeout, default is $opt_w second\n",
"Database Definitions:\n",
" -d : The file containing record type definitions.\n",
" This can be set using the EPICS_CAPR_DBD_FILE environment variable.\n",
- " Default: ", AbsPath($opt_d), "\n",
+ " Default: ", abs_path($opt_d), "\n",
"Output Options:\n",
" -r Lists all record types in the selected dbd file.\n",
- " -f : Lists all fields with their interest level, data type\n",
- " and number base for the given record_type.\n",
+ " -f : Lists all fields with interest level, data type\n",
+ " and other information for the given record_type.\n",
+ " -l : interest level\n",
+ " -n : Maximum number of array elements to display\n",
+ " Default: $opt_n\n",
"\n",
"Base version: ", CA->version, "\n";
exit 1;
diff --git a/modules/ca/src/tools/caget.c b/modules/ca/src/tools/caget.c
index 0aa655ee5..e60f61922 100644
--- a/modules/ca/src/tools/caget.c
+++ b/modules/ca/src/tools/caget.c
@@ -256,7 +256,7 @@ static int caget (pv *pvs, int nPvs, RequestT request, OutputT format,
for (n = 0; n < nPvs; n++) {
switch (format) {
- case plain: /* Emulate old caget behaviour */
+ case plain: /* Emulate old caget behavior */
if (pvs[n].nElems <= 1 && fieldSeparator == ' ') printf("%-30s", pvs[n].name);
else printf("%s", pvs[n].name);
printf("%c", fieldSeparator);
diff --git a/modules/ca/src/tools/caput.c b/modules/ca/src/tools/caput.c
index 62def624e..05247643a 100644
--- a/modules/ca/src/tools/caput.c
+++ b/modules/ca/src/tools/caput.c
@@ -192,7 +192,7 @@ int caget (pv *pvs, int nPvs, OutputT format,
for (n = 0; n < nPvs; n++) {
switch (format) {
- case plain: /* Emulate old caput behaviour */
+ case plain: /* Emulate old caput behavior */
if (pvs[n].reqElems <= 1 && fieldSeparator == ' ') printf("%-30s", pvs[n].name);
else printf("%s", pvs[n].name);
printf("%c", fieldSeparator);
diff --git a/modules/database/src/ioc/as/asCa.c b/modules/database/src/ioc/as/asCa.c
index d2e9f7591..a250a0f7a 100644
--- a/modules/database/src/ioc/as/asCa.c
+++ b/modules/database/src/ioc/as/asCa.c
@@ -241,7 +241,7 @@ void asCaStart(void)
epicsThreadGetStackSize(epicsThreadStackBig),
(EPICSTHREADFUNC)asCaTask,0);
if(threadid==0) {
- errMessage(0,"asCaStart: taskSpawn Failure\n");
+ errMessage(0,"asCaStart: taskSpawn Failure");
}
}
epicsMutexMustLock(asCaTaskLock);
diff --git a/modules/database/src/ioc/as/asIocRegister.c b/modules/database/src/ioc/as/asIocRegister.c
index f4426786a..f97917d8e 100644
--- a/modules/database/src/ioc/as/asIocRegister.c
+++ b/modules/database/src/ioc/as/asIocRegister.c
@@ -16,7 +16,7 @@
#include "asIocRegister.h"
/* asSetFilename */
-static const iocshArg asSetFilenameArg0 = { "ascf",iocshArgString};
+static const iocshArg asSetFilenameArg0 = { "ascf",iocshArgStringPath};
static const iocshArg * const asSetFilenameArgs[] = {&asSetFilenameArg0};
static const iocshFuncDef asSetFilenameFuncDef =
{"asSetFilename",1,asSetFilenameArgs,
@@ -97,7 +97,7 @@ static void aspmemCallFunc(const iocshArgBuf *args)
}
/* astac */
-static const iocshArg astacArg0 = { "recordname",iocshArgString};
+static const iocshArg astacArg0 = { "recordname",iocshArgStringRecord};
static const iocshArg astacArg1 = { "user",iocshArgString};
static const iocshArg astacArg2 = { "host",iocshArgString};
static const iocshArg * const astacArgs[] = {&astacArg0,&astacArg1,&astacArg2};
diff --git a/modules/database/src/ioc/bpt/RULES b/modules/database/src/ioc/bpt/RULES
index cf6232bc6..7239efd02 100644
--- a/modules/database/src/ioc/bpt/RULES
+++ b/modules/database/src/ioc/bpt/RULES
@@ -11,5 +11,6 @@
# This is a Makefile fragment, see src/ioc/Makefile.
-$(patsubst %,$(COMMON_DIR)/%,$(BPT_DBD)) : \
- $(COMMON_DIR)/bpt%.dbd : $(MAKEBPT)
+$(addprefix $(COMMON_DIR)/,$(BPT_DBD)) : $(COMMON_DIR)/bpt%.dbd : \
+ $(EPICS_BASE_HOST_BIN)/makeBpt$(HOSTEXE)
+# Don't try to use $(MAKEBPT) above
diff --git a/modules/database/src/ioc/db/RULES b/modules/database/src/ioc/db/RULES
index c7dbfe8ef..85fa44a64 100644
--- a/modules/database/src/ioc/db/RULES
+++ b/modules/database/src/ioc/db/RULES
@@ -13,13 +13,15 @@
THESE_RULES := $(IOCDIR)/db/RULES
-dbCommon.h$(DEP): $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES)
+dbCommon.h$(DEP): $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES) \
+ $(DBDTORECTYPEH_dep)
@$(RM) $@
@$(DBTORECORDTYPEH) -D -I ../db -I $(COMMON_DIR) -o $(COMMONDEP_TARGET) $< > $@
$(COMMON_DIR)/dbCommonRecord.html: ../db/dbCommon.dbd.pod
-$(COMMON_DIR)/dbCommon.h: $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES)
+$(COMMON_DIR)/dbCommon.h: $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES) \
+ $(DBDTORECTYPEH_dep)
@$(RM) $(notdir $@)
$(DBTORECORDTYPEH) -I ../db -I $(COMMON_DIR) -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
diff --git a/modules/database/src/ioc/db/callback.c b/modules/database/src/ioc/db/callback.c
index 4a26e27ac..d58b8fc6f 100644
--- a/modules/database/src/ioc/db/callback.c
+++ b/modules/database/src/ioc/db/callback.c
@@ -85,7 +85,7 @@ static epicsEventId startStopEvent;
static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = {
"cbLow", "cbMedium", "cbHigh"
};
-#define FULL_MSG(name) "callbackRequest: " name " ring buffer full\n"
+#define FULL_MSG(name) "callbackRequest: " ERL_ERROR " " name " ring buffer full\n"
static char *fullMessage[NUM_CALLBACK_PRIORITIES] = {
FULL_MSG("cbLow"), FULL_MSG("cbMedium"), FULL_MSG("cbHigh")
};
@@ -326,17 +326,17 @@ int callbackRequest(epicsCallback *pcallback)
cbQueueSet *mySet;
if (!pcallback) {
- epicsInterruptContextMessage("callbackRequest: pcallback was NULL\n");
+ epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " pcallback was NULL\n");
return S_db_notInit;
}
priority = pcallback->priority;
if (priority < 0 || priority >= NUM_CALLBACK_PRIORITIES) {
- epicsInterruptContextMessage("callbackRequest: Bad priority\n");
+ epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " Bad priority\n");
return S_db_badChoice;
}
mySet = &callbackQueue[priority];
if (!mySet->queue) {
- epicsInterruptContextMessage("callbackRequest: Callbacks not initialized\n");
+ epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " Callbacks not initialized\n");
return S_db_notInit;
}
if (mySet->queueOverflow) return S_db_bufFull;
diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c
index 8aac52f00..8fcdab8a7 100644
--- a/modules/database/src/ioc/db/dbAccess.c
+++ b/modules/database/src/ioc/db/dbAccess.c
@@ -529,7 +529,7 @@ long dbProcess(dbCommon *precord)
}
}
- /* If already active dont process */
+ /* If already active don't process */
if (precord->pact) {
unsigned short monitor_mask;
@@ -931,7 +931,7 @@ long dbGet(DBADDR *paddr, short dbrType,
no_elements = capacity = pfl->no_elements;
}
- /* Update field info from record (if neccessary);
+ /* Update field info from record (if necessary);
* may modify paddr->pfield.
*/
if (!dbfl_has_copy(pfl) &&
@@ -1104,9 +1104,9 @@ static long dbPutFieldLink(DBADDR *paddr,
if (link_info.ltype == PV_LINK &&
(link_info.modifiers & (pvlOptCA | pvlOptCP | pvlOptCPP)) == 0) {
chan = dbChannelCreate(link_info.target);
- if (chan && dbChannelOpen(chan) != 0) {
- errlogPrintf("ERROR: dbPutFieldLink %s.%s=%s: dbChannelOpen() failed\n",
- precord->name, pfldDes->name, link_info.target);
+ if (chan && (status = dbChannelOpen(chan)) != 0) {
+ errlogPrintf(ERL_ERROR ": dbPutFieldLink %s.%s=%s: dbChannelOpen() failed w/ 0x%lx\n",
+ precord->name, pfldDes->name, link_info.target, status);
goto cleanup;
}
}
@@ -1325,7 +1325,6 @@ long dbPut(DBADDR *paddr, short dbrType,
void *pfieldsave = paddr->pfield;
rset *prset = dbGetRset(paddr);
long status = 0;
- long offset;
dbFldDes *pfldDes;
int isValueField;
@@ -1349,20 +1348,25 @@ long dbPut(DBADDR *paddr, short dbrType,
if (status) return status;
}
- if (paddr->pfldDes->special == SPC_DBADDR &&
- prset && prset->get_array_info) {
- long dummy;
+ if (nRequest>1 || paddr->pfldDes->special == SPC_DBADDR) {
+ long offset = 0;
+ if (paddr->pfldDes->special == SPC_DBADDR &&
+ prset && prset->get_array_info) {
+ long dummy;
- status = prset->get_array_info(paddr, &dummy, &offset);
- /* paddr->pfield may be modified */
- if (status) goto done;
+ status = prset->get_array_info(paddr, &dummy, &offset);
+ /* paddr->pfield may be modified */
+ if (status) goto done;
+ }
if (no_elements < nRequest)
nRequest = no_elements;
status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer,
nRequest, no_elements, offset);
/* update array info */
- if (!status && prset->put_array_info)
+ if (!status && paddr->pfldDes->special == SPC_DBADDR &&
+ prset && prset->put_array_info) {
status = prset->put_array_info(paddr, nRequest);
+ }
} else {
if (nRequest < 1) {
recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
diff --git a/modules/database/src/ioc/db/dbAccessDefs.h b/modules/database/src/ioc/db/dbAccessDefs.h
index 771084170..b26a1a56e 100644
--- a/modules/database/src/ioc/db/dbAccessDefs.h
+++ b/modules/database/src/ioc/db/dbAccessDefs.h
@@ -73,7 +73,7 @@ DBCORE_API extern int dbAccessDebugPUTF;
* options has a bit set for each option that was accepted
* number_elements is actual number of elements obtained
*
- * The individual items can be refered to by the expressions::
+ * The individual items can be referred to by the expressions::
*
* buffer.status
* buffer.severity
@@ -220,6 +220,8 @@ DBCORE_API long dbNameToAddr(const char *pname, struct dbAddr *paddr);
/** Initialize DBADDR from a dbEntry
* Also handles SPC_DBADDR processing. This is really an internal
* routine for use by dbNameToAddr() and dbChannelCreate().
+ *
+ * \since 7.0.2.1
*/
DBCORE_API long dbEntryToAddr(const struct dbEntry *pdbentry,
struct dbAddr *paddr);
@@ -227,6 +229,8 @@ DBCORE_API long dbEntryToAddr(const struct dbEntry *pdbentry,
/** Initialize DBENTRY from a valid dbAddr*
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
* and finally dbFollowAlias().
+ *
+ * \since 3.16.1
*/
DBCORE_API void dbInitEntryFromAddr(struct dbAddr *paddr,
struct dbEntry *pdbentry);
@@ -234,6 +238,8 @@ DBCORE_API void dbInitEntryFromAddr(struct dbAddr *paddr,
/** Initialize DBENTRY from a valid record (dbCommon*)
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
* and finally dbFollowAlias() when no field is specified.
+ *
+ * \since 3.16.1
*/
DBCORE_API void dbInitEntryFromRecord(struct dbCommon *prec,
struct dbEntry *pdbentry);
diff --git a/modules/database/src/ioc/db/dbBkpt.c b/modules/database/src/ioc/db/dbBkpt.c
index a32ebab4e..de6fa3f9b 100644
--- a/modules/database/src/ioc/db/dbBkpt.c
+++ b/modules/database/src/ioc/db/dbBkpt.c
@@ -97,7 +97,7 @@ static long FIND_CONT_NODE(
* processing in that lockset to this task. The separate task is
* used so that locksets that do not have breakpoints are isolated
* from locksets that do. This allows the processing of other
- * locksets to continue uninterupted, even if they exist on the same
+ * locksets to continue uninterrupted, even if they exist on the same
* scan list as a lockset containing a breakpoint.
*
* An entrypoint is the first record that gets processed in a lockset.
@@ -250,7 +250,7 @@ static long FIND_CONT_NODE(
}
/*
- * Initialise the breakpoint stack
+ * Initialize the breakpoint stack
*/
void dbBkptInit(void)
{
diff --git a/modules/database/src/ioc/db/dbCa.c b/modules/database/src/ioc/db/dbCa.c
index 70f939897..57cca404f 100644
--- a/modules/database/src/ioc/db/dbCa.c
+++ b/modules/database/src/ioc/db/dbCa.c
@@ -196,6 +196,73 @@ static void caLinkDec(caLink *pca)
if (callback) callback(userPvt);
}
+struct waitPvt {
+ caLink *pca;
+ epicsEventId evt;
+};
+enum testEvent {
+ testEventConnect,
+ testEventCount,
+};
+
+static
+void testdbCaWaitForEventCB(void *raw)
+{
+ struct waitPvt *pvt = raw;
+
+ epicsMutexMustLock(pvt->pca->lock);
+ epicsEventMustTrigger(pvt->evt);
+ epicsMutexUnlock(pvt->pca->lock);
+}
+
+static
+void testdbCaWaitForEvent(DBLINK *plink, unsigned long cnt, enum testEvent event)
+{
+ caLink *pca;
+ epicsEventId evt = epicsEventMustCreate(epicsEventEmpty);
+
+ dbScanLock(plink->precord);
+
+ assert(plink->type==CA_LINK);
+ pca = (caLink *)plink->value.pv_link.pvt;
+
+ epicsMutexMustLock(pca->lock);
+ assert(!pca->monitor && !pca->connect && !pca->userPvt);
+
+ while(!pca->isConnected || (event==testEventCount && pca->nUpdate < cnt)) {
+ struct waitPvt pvt = {pca, evt};
+ pca->connect = &testdbCaWaitForEventCB;
+ pca->monitor = &testdbCaWaitForEventCB;
+ pca->userPvt = &pvt;
+
+ epicsMutexUnlock(pca->lock);
+ dbScanUnlock(plink->precord);
+
+ epicsEventMustWait(evt);
+
+ dbScanLock(plink->precord);
+ epicsMutexMustLock(pca->lock);
+
+ pca->connect = NULL;
+ pca->monitor = NULL;
+ pca->userPvt = NULL;
+ }
+
+ epicsEventDestroy(evt);
+ epicsMutexUnlock(pca->lock);
+ dbScanUnlock(plink->precord);
+}
+
+void testdbCaWaitForConnect(DBLINK *plink)
+{
+ testdbCaWaitForEvent(plink, 0, testEventConnect);
+}
+
+void testdbCaWaitForUpdateCount(DBLINK *plink, unsigned long cnt)
+{
+ testdbCaWaitForEvent(plink, cnt, testEventCount);
+}
+
/* Block until worker thread has processed all previously queued actions.
* Does not prevent additional actions from being queued.
*/
@@ -232,22 +299,6 @@ void dbCaSync(void)
epicsEventDestroy(wake);
}
-DBCORE_API unsigned long dbCaGetUpdateCount(struct link *plink)
-{
- caLink *pca = (caLink *)plink->value.pv_link.pvt;
- unsigned long ret;
-
- if (!pca) return (unsigned long)-1;
-
- epicsMutexMustLock(pca->lock);
-
- ret = pca->nUpdate;
-
- epicsMutexUnlock(pca->lock);
-
- return ret;
-}
-
void dbCaCallbackProcess(void *userPvt)
{
struct link *plink = (struct link *)userPvt;
@@ -785,6 +836,8 @@ static void connectionCallback(struct connection_handler_args arg)
caLink *pca;
short link_action = 0;
struct link *plink;
+ dbCaCallback connect = 0;
+ void *userPvt = 0;
pca = ca_puser(arg.chid);
assert(pca);
@@ -851,11 +904,16 @@ static void connectionCallback(struct connection_handler_args arg)
}
pca->gotAttributes = 0;
if (pca->dbrType != DBR_STRING) {
+ /* will run connect() callback later */
link_action |= CA_GET_ATTRIBUTES;
+ } else {
+ connect = pca->connect;
+ userPvt = pca->userPvt;
}
done:
if (link_action) addAction(pca, link_action);
epicsMutexUnlock(pca->lock);
+ if (connect) connect(userPvt);
}
static void eventCallback(struct event_handler_args arg)
@@ -881,10 +939,10 @@ static void eventCallback(struct event_handler_args arg)
if (precord) {
if (arg.status != ECA_NORDACCESS &&
arg.status != ECA_GETFAIL)
- errlogPrintf("dbCa: eventCallback record %s error %s\n",
+ errlogPrintf("dbCa: eventCallback record %s " ERL_ERROR " %s\n",
precord->name, ca_message(arg.status));
} else {
- errlogPrintf("dbCa: eventCallback error %s\n",
+ errlogPrintf("dbCa: eventCallback " ERL_ERROR " %s\n",
ca_message(arg.status));
}
goto done;
@@ -1029,10 +1087,10 @@ static void getAttribEventCallback(struct event_handler_args arg)
if (arg.status != ECA_NORMAL) {
dbCommon *precord = plink->precord;
if (precord) {
- errlogPrintf("dbCa: getAttribEventCallback record %s error %s\n",
+ errlogPrintf("dbCa: getAttribEventCallback record %s " ERL_ERROR " %s\n",
precord->name, ca_message(arg.status));
} else {
- errlogPrintf("dbCa: getAttribEventCallback error %s\n",
+ errlogPrintf("dbCa: getAttribEventCallback " ERL_ERROR " %s\n",
ca_message(arg.status));
}
epicsMutexUnlock(pca->lock);
@@ -1058,6 +1116,7 @@ static void getAttribEventCallback(struct event_handler_args arg)
static void dbCaTask(void *arg)
{
+ epicsEventId requestSync = NULL;
taskwdInsert(0, NULL, NULL);
SEVCHK(ca_context_create(ca_enable_preemptive_callback),
"dbCaTask calling ca_context_create");
@@ -1078,13 +1137,20 @@ static void dbCaTask(void *arg)
epicsMutexMustLock(workListLock);
if (!(pca = (caLink *)ellGet(&workList))){ /* Take off list head */
+ if(requestSync) {
+ /* dbCaSync() requires workListLock to be held here */
+ epicsEventMustTrigger(requestSync);
+ requestSync = NULL;
+ }
epicsMutexUnlock(workListLock);
if (dbCaCtl == ctlExit) goto shutdown;
break; /* workList is empty */
}
link_action = pca->link_action;
- if (link_action&CA_SYNC)
- epicsEventMustTrigger((epicsEventId)pca->userPvt); /* dbCaSync() requires workListLock to be held here */
+ if (link_action&CA_SYNC) {
+ assert(!requestSync);
+ requestSync = pca->userPvt;
+ }
pca->link_action = 0;
if (link_action & CA_CLEAR_CHANNEL) --removesOutstanding;
epicsMutexUnlock(workListLock); /* Give back immediately */
diff --git a/modules/database/src/ioc/db/dbCa.h b/modules/database/src/ioc/db/dbCa.h
index 42ce1b338..7b6f9c090 100644
--- a/modules/database/src/ioc/db/dbCa.h
+++ b/modules/database/src/ioc/db/dbCa.h
@@ -48,8 +48,12 @@ DBCORE_API long dbCaPutLink(struct link *plink,short dbrType,
extern struct ca_client_context * dbCaClientContext;
#ifdef EPICS_DBCA_PRIVATE_API
+/* Wait CA link work queue to become empty. eg. after from dbPut() to OUT */
DBCORE_API void dbCaSync(void);
-DBCORE_API unsigned long dbCaGetUpdateCount(struct link *plink);
+/* Wait for the data update counter to reach the specified value. */
+DBCORE_API void testdbCaWaitForUpdateCount(DBLINK *plink, unsigned long cnt);
+/* Wait for CA link to become connected */
+DBCORE_API void testdbCaWaitForConnect(DBLINK *plink);
#endif
/* These macros are for backwards compatibility */
diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h
index ec86e9e28..23ffd7d09 100644
--- a/modules/database/src/ioc/db/dbChannel.h
+++ b/modules/database/src/ioc/db/dbChannel.h
@@ -9,14 +9,22 @@
* in file LICENSE that is included with this distribution.
\*************************************************************************/
-/*
- * Author: Andrew Johnson
- * Ralph Lange
- */
-
#ifndef INC_dbChannel_H
#define INC_dbChannel_H
+/** \file dbChannel.h
+ *
+ * \author Andrew Johnson (ANL)
+ * \author Ralph Lange (BESSY)
+ *
+ * \brief The dbChannel API gives access to record fields.
+ *
+ * The dbChannel API is used internally by the IOC and by link types, device
+ * support and IOC servers (RSRV and QSRV) to access record fields, either
+ * directly or through one or more server-side filters as specified in the
+ * channel name used when creating the channel.
+ */
+
#include "dbDefs.h"
#include "dbAddr.h"
#include "ellLib.h"
@@ -30,200 +38,531 @@
extern "C" {
#endif
-/*
+/**
* event subscription
*/
typedef struct evSubscrip {
- ELLNODE node;
- struct dbChannel *chan;
- EVENTFUNC *user_sub;
- void *user_arg;
- struct event_que *ev_que;
- db_field_log **pLastLog;
- unsigned long npend; /* n times this event is on the queue */
- unsigned long nreplace; /* n times replacing event on the queue */
- unsigned char select;
- char useValque;
- char callBackInProgress;
- char enabled;
+ ELLNODE node;
+ struct dbChannel * chan;
+ EVENTFUNC * user_sub;
+ void * user_arg;
+ struct event_que * ev_que;
+ db_field_log ** pLastLog;
+ unsigned long npend; /**< n times this event is on the queue */
+ unsigned long nreplace; /**< n times replacing event on the queue */
+ unsigned char select;
+ char useValque;
+ char callBackInProgress;
+ char enabled;
} evSubscrip;
typedef struct chFilter chFilter;
-/* A dbChannel points to a record field, and can have multiple filters */
+/** \brief A Database Channel object
+ *
+ * A dbChannel is created from a user-supplied channel name, and holds
+ * pointers to the record & field and information about any filters that
+ * were specified with it. The dbChannel macros defined in this header
+ * file should always be used to read data from a dbChannel object, the
+ * internal implementation may change without notice.
+ */
typedef struct dbChannel {
- const char *name;
- dbAddr addr; /* address structure for record/field */
- long final_no_elements; /* final number of elements (arrays) */
- short final_field_size; /* final size of element */
- short final_type; /* final type of database field */
- ELLLIST filters; /* list of filters as created from JSON */
- ELLLIST pre_chain; /* list of filters to be called pre-event-queue */
- ELLLIST post_chain; /* list of filters to be called post-event-queue */
+ const char *name; /**< Channel name */
+ dbAddr addr; /**< Pointers to record & field */
+ long final_no_elements; /**< Final number of array elements */
+ short final_field_size; /**< Final size of each element */
+ short final_type; /**< Final type of database field */
+ ELLLIST filters; /**< Filters used by dbChannel */
+ ELLLIST pre_chain; /**< Filters on pre-event-queue chain */
+ ELLLIST post_chain; /**< Filters on post-event-queue chain */
} dbChannel;
-/* Prototype for the channel event function that is called in filter stacks
+/** \brief Event filter function type
*
- * When invoked the scan lock for the record associated with 'chan' _may_ be locked.
- * Unless dbfl_has_copy(pLog), it must call dbScanLock before accessing the data,
- * as this indicates the data is still owned by the record.
+ * Prototype for channel event filter functions.
*
- * This function has ownership of the field log pLog, if it wishes to discard
- * this update it should free the field log with db_delete_field_log() and
- * then return NULL.
+ * When these functions are called the scan lock for the record associated
+ * with \p chan _may_ already be locked, but they must use dbfl_has_copy()
+ * to determine whether the data in \p pLog belongs to the record. If that
+ * returns 0 the function must call dbScanLock() before accessing the data.
+ *
+ * A filter function owns the field log \p pLog when called. To discard an
+ * update it should free the field log using db_delete_field_log() and
+ * return NULL.
*/
typedef db_field_log* (chPostEventFunc)(void *pvt, dbChannel *chan, db_field_log *pLog);
-/* Return values from chFilterIf->parse_* routines: */
+/** \brief Result returned by chFilterIf parse routines.
+ *
+ * The parsing functions from a chFilterIf must return either \p parse_stop
+ * (in event of an error) or \p parse_continue.
+ */
typedef enum {
parse_stop, parse_continue
} parse_result;
-/* These routines must be implemented by each filter plug-in */
+/** \brief Channel Filter Interface
+ *
+ * Routines to be implemented by each Channel Filter.
+ */
typedef struct chFilterIf {
- /* cleanup pointer passed to dbRegisterFilter().
- * Called during DB shutdown
+ /** \brief Release private filter data.
+ *
+ * Called during database shutdown to release resources allocated by
+ * the filter.
+ * \param puser The user-pointer passed into dbRegisterFilter().
*/
void (* priv_free)(void *puser);
- /* Parsing event handlers: */
- parse_result (* parse_start)(chFilter *filter);
- /* If parse_start() returns parse_continue for a filter, one of
+
+ /** \name Parsing event handlers
+ *
+ * A filter that doesn't accept a particular JSON value type may use a
+ * \p NULL pointer to the parsing handler for that value type, which is
+ * equivalent to a routine that always returns \p parse_stop.
+ */
+
+ /** \brief Create new filter instance.
+ *
+ * Called when a new filter instance is requested. Filter may allocate
+ * resources for this instance and store in \p filter->puser.
+ * If parse_start() returns \p parse_continue for a filter, one of
* parse_abort() or parse_end() will later be called for that same
* filter.
+ * \param filter Pointer to instance data.
+ * \returns \p parse_stop on error, or \p parse_continue
+ */
+ parse_result (* parse_start)(chFilter *filter);
+
+ /** \brief Parsing of filter instance is being cancelled.
+ *
+ * This function should release any memory allocated for the given
+ * \p filter instance; no further parsing handlers will be called for it.
+ * \param filter Pointer to instance data.
*/
void (* parse_abort)(chFilter *filter);
- /* If parse_abort() is called it should release any memory allocated
- * for this filter; no further parse_...() calls will be made;
+
+ /** \brief Parsing of filter instance has completed successfully.
+ *
+ * The parser has reached the end of this instance and no further parsing
+ * handlers will be called for it. The filter must check the instance
+ * data and indicate whether it was complete or not.
+ * \param filter Pointer to instance data.
+ * \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_end)(chFilter *filter);
- /* If parse_end() returns parse_stop it should have released any
- * memory allocated for this filter; no further parse_...() calls will
- * be made in this case.
+
+ /** \brief Parser saw \p null value.
+ *
+ * Optional.
+ * Null values are rarely accepted by channel filters.
+ * \param filter Pointer to instance data.
+ * \returns \p parse_stop on error, or \p parse_continue
*/
-
parse_result (* parse_null)(chFilter *filter);
- parse_result (* parse_boolean)(chFilter *filter, int boolVal);
- parse_result (* parse_integer)(chFilter *filter, long integerVal);
- parse_result (* parse_double)(chFilter *filter, double doubleVal);
- parse_result (* parse_string)(chFilter *filter, const char *stringVal,
- size_t stringLen); /* NB: stringVal is not zero-terminated: */
+ /** \brief Parser saw boolean value.
+ *
+ * Optional.
+ * \param filter Pointer to instance data.
+ * \param boolVal true/false Value.
+ * \returns \p parse_stop on error, or \p parse_continue
+ */
+ parse_result (* parse_boolean)(chFilter *filter, int boolVal);
+
+ /** \brief Parser saw integer value.
+ *
+ * Optional.
+ * \param filter Pointer to instance data.
+ * \param integerVal Value.
+ * \returns \p parse_stop on error, or \p parse_continue
+ */
+ parse_result (* parse_integer)(chFilter *filter, long integerVal);
+
+ /** \brief Parser saw double value.
+ *
+ * Optional.
+ * \param filter Pointer to instance data.
+ * \param doubleVal Value.
+ * \returns \p parse_stop on error, or \p parse_continue
+ */
+ parse_result (* parse_double)(chFilter *filter, double doubleVal);
+
+ /** \brief Parser saw string value.
+ *
+ * Optional.
+ * \param filter Pointer to instance data.
+ * \param stringVal Value, not zero-terminated.
+ * \param stringLen Number of chars in \p stringVal.
+ * \returns \p parse_stop on error, or \p parse_continue
+ */
+ parse_result (* parse_string)(chFilter *filter, const char *stringVal,
+ size_t stringLen);
+
+ /** \brief Parser saw start of a JSON map value.
+ *
+ * Optional.
+ * Inside a JSON map all data consists of key/value pairs.
+ * \param filter Pointer to instance data.
+ * \returns \p parse_stop on error, or \p parse_continue
+ */
parse_result (* parse_start_map)(chFilter *filter);
+
+ /** \brief Parser saw a JSON map key.
+ *
+ * Optional.
+ * \param filter Pointer to instance data.
+ * \param key Value not zero-terminated.
+ * \param stringLen Number of chars in \p key
+ * \returns \p parse_stop on error, or \p parse_continue
+ */
parse_result (* parse_map_key)(chFilter *filter, const char *key,
- size_t stringLen); /* NB: key is not zero-terminated: */
+ size_t stringLen);
+
+ /** \brief Parser saw end of a JSON map value.
+ *
+ * Optional.
+ * \param filter Pointer to instance data.
+ * \returns \p parse_stop on error, or \p parse_continue
+ */
parse_result (* parse_end_map)(chFilter *filter);
+ /** \brief Parser saw start of a JSON array value.
+ *
+ * Optional.
+ * Data inside a JSON array doesn't have to be all of the same type.
+ * \param filter Pointer to instance data.
+ * \returns \p parse_stop on error, or \p parse_continue
+ */
parse_result (* parse_start_array)(chFilter *filter);
+
+ /** \brief Parser saw end of a JSON array value.
+ *
+ * Optional.
+ * \param filter Pointer to instance data.
+ * \returns \p parse_stop on error, or \p parse_continue
+ */
parse_result (* parse_end_array)(chFilter *filter);
- /* Channel operations: */
+ /** \name Channel operations */
+
+ /** \brief Open filter on channel.
+ *
+ * Optional, initialize instance.
+ * \param filter Pointer to instance data.
+ * \returns 0, or an error status value.
+ */
long (* channel_open)(chFilter *filter);
- void (* channel_register_pre) (chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
- void (* channel_register_post)(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
- void (* channel_report)(chFilter *filter, int level, const unsigned short indent);
+
+ /** \brief Get pre-chain filter function.
+ *
+ * Optional.
+ * Returns pre-chain filter function and context.
+ * \param[in] filter Pointer to instance data.
+ * \param[out] cb_out Write filter function pointer here.
+ * \param[out] arg_out Write private data pointer here.
+ * \param[in,out] probe db_field_log with metadata for adjusting.
+ */
+ void (* channel_register_pre) (chFilter *filter,
+ chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
+
+ /** \brief Get post-chain filter function.
+ *
+ * Optional, return post-chain filter function and context.
+ * \param[in] filter Pointer to instance data.
+ * \param[out] cb_out Write filter function pointer here.
+ * \param[out] arg_out Write private data pointer here.
+ * \param[in,out] probe db_field_log with metadata for adjusting.
+ */
+ void (* channel_register_post)(chFilter *filter,
+ chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
+
+ /** \brief Print information about filter to stdout.
+ *
+ * Optional.
+ * \param filter Pointer to instance data.
+ * \param level Higher levels may provide more detail.
+ * \param indent Indent all lines by this many spaces.
+ */
+ void (* channel_report)(chFilter *filter,
+ int level, const unsigned short indent);
+
+ /** \brief Close filter.
+ *
+ * Optional, releases resources allocated for this instance.
+ * \param filter Pointer to instance data.
+ */
void (* channel_close)(chFilter *filter);
} chFilterIf;
-/* A chFilterPlugin holds data for a filter plugin */
+/** \brief Filter plugin data
+ *
+ * A chFilterPlugin object holds data about a filter plugin.
+ */
typedef struct chFilterPlugin {
- ELLNODE node;
- const char *name;
- const chFilterIf *fif;
- void *puser;
+ ELLNODE node; /**< \brief List node (dbBase->filterList) */
+ const char *name; /**< \brief Filter name */
+ const chFilterIf *fif; /**< \brief Filter interface routines */
+ void *puser; /**< \brief For use by the plugin */
} chFilterPlugin;
-/* A chFilter holds data for a single filter instance */
+/** \brief Filter instance data
+ *
+ * A chFilter holds data about a single filter instance.
+ */
struct chFilter {
- ELLNODE list_node;
- ELLNODE pre_node;
- ELLNODE post_node;
- dbChannel *chan;
- const chFilterPlugin *plug;
- chPostEventFunc *pre_func;
- void *pre_arg;
- chPostEventFunc *post_func;
- void *post_arg;
- void *puser;
+ ELLNODE list_node; /**< \brief List node (dbChannel->filters) */
+ ELLNODE pre_node; /**< \brief List node (dbChannel->pre_chain) */
+ ELLNODE post_node; /**< \brief List node (dbChannel->post_chain) */
+ dbChannel *chan; /**< \brief The dbChannel we belong to */
+ const chFilterPlugin *plug; /**< \brief The plugin that created us */
+ chPostEventFunc *pre_func; /**< \brief pre-chain filter function */
+ void *pre_arg; /**< \brief pre-chain context pointer */
+ chPostEventFunc *post_func; /**< \brief post-chain filter function */
+ void *post_arg; /**< \brief post-chain context pointer */
+ void *puser; /**< \brief For use by the plugin */
};
struct dbCommon;
struct dbFldDes;
-DBCORE_API void dbChannelInit (void);
+/** \brief Initialize the dbChannel subsystem. */
+DBCORE_API void dbChannelInit(void);
+
+/** \brief Cleanup the dbChannel subsystem. */
DBCORE_API void dbChannelExit(void);
+
+/** \brief Test the given PV name for existance.
+ *
+ * This routine looks up the given record and field name, but does not check
+ * whether any field modifiers given after the field name are correct.
+ * This is sufficient for the correct server to quickly direct searches to the
+ * IOC that owns that PV name. Field modifiers will be checked when
+ * dbChannelCreate() is later called with the same name.
+ * \param name Channel name.
+ * \returns 0, or an error status value.
+ */
DBCORE_API long dbChannelTest(const char *name);
+
+/** \brief Create a dbChannel object for the given PV name.
+ *
+ * \param name Channel name.
+ * \return Pointer to dbChannel object, or NULL if invalid.
+ */
DBCORE_API dbChannel * dbChannelCreate(const char *name);
+
+/** \brief Open a dbChannel for doing I/O.
+ *
+ * \param chan Pointer to the dbChannel object.
+ * \returns 0, or an error status value.
+ */
DBCORE_API long dbChannelOpen(dbChannel *chan);
-/*Following is also defined in db_convert.h*/
+/** \brief Request (DBR) type conversion array.
+ *
+ * This converter array is declared in db_convert.h but redeclared
+ * here as it is needed by the dbChannel...CAType macros defined here.
+ */
DBCORE_API extern unsigned short dbDBRnewToDBRold[];
-/* In the following macros pChan is dbChannel* */
+/** \name dbChannel Inspection Macros */
-/* evaluates to const char* */
+/** \brief Name that defined the channel.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns const char*
+ */
#define dbChannelName(pChan) ((pChan)->name)
-/* evaluates to struct dbCommon* */
+/** \brief Record the channel connects to.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns struct dbCommon*
+ */
#define dbChannelRecord(pChan) ((pChan)->addr.precord)
-/* evaluates to struct dbFldDes* */
+/** \brief Field descriptor for the field pointed to.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns struct dbFldDes*
+ */
#define dbChannelFldDes(pChan) ((pChan)->addr.pfldDes)
-/* evaluates to long */
+/** \brief Number of array elements in the field.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns long
+ */
#define dbChannelElements(pChan) ((pChan)->addr.no_elements)
-/* evaluates to short */
+/** \brief Data type (DBF type) of the field.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns short
+ */
#define dbChannelFieldType(pChan) ((pChan)->addr.field_type)
-/* evaluates to short */
+/** \brief Request type (DBR type) of the field.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns short
+ */
#define dbChannelExportType(pChan) ((pChan)->addr.dbr_field_type)
-/* evaluates to short */
+/** \brief CA data type of the field.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns short
+ */
#define dbChannelExportCAType(pChan) (dbDBRnewToDBRold[dbChannelExportType(pChan)])
-/* evaluates to short */
+/** \brief Field (element if array) size in bytes.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns short
+ */
#define dbChannelFieldSize(pChan) ((pChan)->addr.field_size)
-/* evaluates to long */
+/** \brief Array length after filtering.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns long
+ */
#define dbChannelFinalElements(pChan) ((pChan)->final_no_elements)
-/* evaluates to short */
+/** \brief Data type after filtering.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns short
+ */
#define dbChannelFinalFieldType(pChan) ((pChan)->final_type)
-/* evaluates to short */
+/** \brief Channel CA data type after filtering.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns short
+ */
#define dbChannelFinalCAType(pChan) (dbDBRnewToDBRold[(pChan)->final_type])
-/* evaluates to short */
+/** \brief Field/element size after filtering, in bytes.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns short */
#define dbChannelFinalFieldSize(pChan) ((pChan)->final_field_size)
-/* evaluates to short */
+/** \brief Field special attribute.
+ *
+ * \param pChan Pointer to the dbChannel object.
+ * \returns short
+ */
#define dbChannelSpecial(pChan) ((pChan)->addr.special)
-/* Channel filters do not get to interpose here since there are many
+/** \brief Pointer to the record field.
+ *
+ * Channel filters do not get to interpose here since there are many
* places where the field pointer is compared with the address of a
* specific record field, so they can't modify the pointer value.
+ * \param pChan Pointer to the dbChannel object.
+ * \returns void *
*/
-/* evaluates to void* */
#define dbChannelField(pChan) ((pChan)->addr.pfield)
+/** \name dbChannel Operation Functions */
+
+/** \brief dbGet() through a dbChannel.
+ *
+ * Calls dbGet() for the field that \p chan refers to.
+ * Only call this routine if the record is already locked.
+ * \param[in] chan Pointer to the dbChannel object.
+ * \param[in] type Request type from dbFldTypes.h.
+ * \param[out] pbuffer Pointer to data buffer.
+ * \param[in,out] options Request options from dbAccessDefs.h.
+ * \param[in,out] nRequest Pointer to the element count.
+ * \param[in] pfl Pointer to a db_field_log or NULL.
+ * \returns 0, or an error status value.
+ */
DBCORE_API long dbChannelGet(dbChannel *chan, short type,
void *pbuffer, long *options, long *nRequest, void *pfl);
+
+/** \brief dbGetField() through a dbChannel.
+ *
+ * Get values from a PV through a channel.
+ * This routine locks the record, calls
+ * dbChannelGet(), then unlocks the record again.
+ * \param[in] chan Pointer to the dbChannel object.
+ * \param[in] type Request type from dbFldTypes.h.
+ * \param[out] pbuffer Pointer to data buffer.
+ * \param[in,out] options Request options from dbAccessDefs.h.
+ * \param[in,out] nRequest Pointer to the element count.
+ * \param[in] pfl Pointer to a db_field_log or NULL.
+ * \returns 0, or an error status value.
+ */
DBCORE_API long dbChannelGetField(dbChannel *chan, short type,
void *pbuffer, long *options, long *nRequest, void *pfl);
+
+/** \brief dbPut() through a dbChannel.
+ *
+ * Put values to a PV through a channel. Only call this routine if the
+ * record is already locked.
+ * Calls dbPut() for the field that \p chan refers to.
+ * \param chan[in] Pointer to the dbChannel object.
+ * \param type[in] Request type from dbFldTypes.h.
+ * \param pbuffer[in] Pointer to data buffer.
+ * \param nRequest[in] Number of elements in pbuffer.
+ * \returns 0, or an error status value.
+ */
DBCORE_API long dbChannelPut(dbChannel *chan, short type,
const void *pbuffer, long nRequest);
+
+/** \brief dbPutField() through a dbChannel.
+ *
+ * Put values to a PV through a channel.
+ * This routine calls dbPutField() for the field that \p chan refers to.
+ * \param chan[in] Pointer to the dbChannel object.
+ * \param type[in] Request type from dbFldTypes.h.
+ * \param pbuffer[in] Pointer to data buffer.
+ * \param nRequest[in] Number of elements in pbuffer.
+ * \returns 0, or an error status value.
+ */
DBCORE_API long dbChannelPutField(dbChannel *chan, short type,
const void *pbuffer, long nRequest);
+
+/** \brief Print report on a channel.
+ *
+ * Print information about the channel to stdout.
+ * \param chan Pointer to the dbChannel object.
+ * \param level Higher levels may provide more detail.
+ * \param indent Indent all lines by this many spaces.
+ */
DBCORE_API void dbChannelShow(dbChannel *chan, int level,
const unsigned short indent);
+
+/** \brief Print report on a channel's filters.
+ *
+ * Print information about the channel's filters to stdout.
+ * \param chan Pointer to the dbChannel object.
+ * \param level Higher levels may provide more detail.
+ * \param indent Indent all lines by this many spaces.
+ */
DBCORE_API void dbChannelFilterShow(dbChannel *chan, int level,
const unsigned short indent);
+
+/** \brief Delete a channel.
+ *
+ * Releases resources owned by this channel and its filters.
+ * \param chan Pointer to the dbChannel object.
+ */
DBCORE_API void dbChannelDelete(dbChannel *chan);
-DBCORE_API void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser);
-DBCORE_API db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn);
-DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn);
+
+/** \name Other routines */
+
+DBCORE_API void dbRegisterFilter(const char *key,
+ const chFilterIf *fif, void *puser);
+DBCORE_API db_field_log* dbChannelRunPreChain(dbChannel *chan,
+ db_field_log *pLogIn);
+DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan,
+ db_field_log *pLogIn);
DBCORE_API const chFilterPlugin * dbFindFilter(const char *key, size_t len);
DBCORE_API void dbChannelGetArrayInfo(dbChannel *chan,
void **pfield, long *no_elements, long *offset);
diff --git a/modules/database/src/ioc/db/dbCommon.dbd.pod b/modules/database/src/ioc/db/dbCommon.dbd.pod
index 5ad4627c5..c8ae7e6ba 100644
--- a/modules/database/src/ioc/db/dbCommon.dbd.pod
+++ b/modules/database/src/ioc/db/dbCommon.dbd.pod
@@ -77,12 +77,12 @@ A set of periodic scan intervals
=back
Additional periodic scan rates may be defined for individual IOCs by making a
-local copy of menuScan.dbd and adding more choices as required. Scan rates
-should normally be defined in order, with the fastest rates appearing first.
-Scan periods may now be specified in seconds, minutes, hours or Hertz/Hz, and
-plural time units will also be accepted (seconds are used if no unit is
-mentioned in the choice string). For example the rates given below are all
-valid:
+local copy of menuScan.dbd and adding more choices as required. Periodic scan
+rates should normally be defined in order following the other scan types, with
+the longest periods appearing first. Scan periods can be specified with a unit
+string of C/C, C/C, C/C or
+C/C. Seconds are used if no unit is included in the choice string.
+For example these rates are all valid:
1 hour
0.5 hours
@@ -97,7 +97,7 @@ initialization (before the normal scan tasks are started).
The B field orders the records within a specific SCAN group. This is not
meaningful for passive records. All records of a specified phase are processed
-before those with higher phase number. Whenever possible it is better to use
+before those with higher phase number. It is generally better practice to use
linked passive records to enforce the order of processing rather than a phase
number.
@@ -109,23 +109,23 @@ The call to post_event is: post_event(short event_number).
The B field specifies the scheduling priority for processing records
with SCAN=C and asynchronous record completion tasks.
-The B field specifies a "disable value". Record processing is
-immediately terminated if the value of this field is equal to the value of the
-DISA field, i.e. the record is disabled. Note that field values of a record
-can be changed by database put or Channel Access, even if a record is
+The B field specifies a "disable value". Record processing cannot
+begin when the value of this field is equal to the value of the DISA
+field, meaning the record is disabled. Note that field values of a record
+can be changed by database or Channel Access puts, even if the record is
disabled.
-The B field contains the value that is compared with DISV to determine
-if the record is disabled. The value of the DISA field is obtained via SDIS if
-SDIS is a database or channel access link. If SDIS is not a database or
-channel access link, then DISA can be set via dbPutField or dbPutLink.
-
-If the B field of a record is written to, the record is processed.
+The B field contains the value that is compared with DISV to determine if
+the record is disabled. A value is obtained for the DISA field from the B
+link field before the IOC tries to process the record. If SDIS is not set, DISA
+may be set by some other method to enable and disable the record.
The B field defines the record's "disable severity". If this field is
not NO_ALARM and the record is disabled, the record will be put into alarm
with this severity and a status of DISABLE_ALARM.
+If the B field of a record is written to, the record is processed.
+
The B field contains the lock set to which this record belongs. All
records linked in any way via input, output, or forward database links belong
to the same lock set. Lock sets are determined at IOC initialization time, and
@@ -135,15 +135,18 @@ The B field counts the number of times dbProcess finds the record active
during successive scans, i.e. PACT is TRUE. If dbProcess finds the record
active MAX_LOCK times (currently set to 10) it raises a SCAN_ALARM.
-The B field is TRUE while the record is being processed. For
+The B field is TRUE while the record is active (being processed). For
asynchronous records PACT can be TRUE from the time record processing is
started until the asynchronous completion occurs. As long as PACT is TRUE,
dbProcess will not call the record processing routine. See Application
Developers Guide for details on usage of PACT.
-The B field is a database link to another record (the "target" record).
-Processing a record with a specified FLNK field will force processing of the
-target record, provided the target record's SCAN field is set to C.
+The B field is a link pointing to another record (the "target" record).
+Processing a record with the FLNK field set will trigger processing of the
+target record towards the end of processing the first record (but before PACT is
+cleared), provided the target record's SCAN field is set to C. If the
+FLNK field is a Channel Access link it must point to the PROC field of the
+target record.
The B field is for internal use by the scanning system.
@@ -227,6 +230,8 @@ The B field is for internal use by the scanning system.
}
field(DISP,DBF_UCHAR) {
prompt("Disable putField")
+ promptgroup("10 - Common")
+ interest(1)
}
field(PROC,DBF_UCHAR) {
prompt("Force Processing")
@@ -236,35 +241,46 @@ The B field is for internal use by the scanning system.
=head3 Alarm Fields
-These fields indicate the status and severity of alarms, or else determine the
+Alarm fields indicate the status and severity of record alarms, or determine
how and when alarms are triggered. Of course, many records have alarm-related
-fields not common to all records. These fields are listed and explained in the
+fields not common to all records. Those fields are listed and explained in the
appropriate section on each record.
The B field contains the current alarm status.
The B field contains the current alarm severity.
-These two fields are seen outside database access. The B and B
-fields are used by the database access, record support, and device support
-routines to set new alarm status and severity values. Whenever any software
-component discovers an alarm condition, it uses the following macro function:
-recGblSetSevr(precord,new_status,new_severity) This ensures that the current
-alarm severity is set equal to the highest outstanding alarm. The file alarm.h
-defines all allowed alarm status and severity values.
+The B string field may contain more detailed information about the alarm.
+
+The STAT, SEVR and AMSG fields hold alarm information as seen outside of the
+database. The B, B and B fields are used during record
+processing by the database access, record support, and device support routines
+to set new alarm status and severity values and message text. Whenever any
+software component discovers an alarm condition, it calls one of these routines
+to register the alarm:
+
+ recGblSetSevr(precord, new_status, new_severity);
+ recGblSetSevrMsg(precord, new_status, new_severity, "Message", ...);
+
+These check the current alarm severity and update the NSTA, NSEV and NAMSG
+fields if appropriate so they always relate to the highest severity alarm seen
+so far during record processing. The file alarm.h defines the allowed alarm
+status and severity values. Towards the end of record processing these fields
+are copied into the STAT, SEVR and AMSG fields and alarm monitors triggered.
The B field contains the highest unacknowledged alarm severity.
-The B field specifies if it is necessary to acknowledge transient
+The B field specifies whether it is necessary to acknowledge transient
alarms.
-The B indicates if the record's value is BnBeBined. Typically
-this is caused by a failure in device support, the fact that the record has
-never been processed, or that the VAL field currently contains a NaN (not a
-number). UDF is initialized to TRUE at IOC initialization. Record and device
-support routines which write to the VAL field are responsible for setting UDF.
+The B indicates if the record's value is BnBeBined. Typically this
+is caused by a failure in device support, the fact that the record has never
+been processed, or that the VAL field currently contains a NaN (not a number) or
+Inf (Infinite) value. UDF defaults to TRUE but can be set in a database file.
+Record and device support routines which write to the VAL field are generally
+responsible for setting and clearing UDF.
-=fields STAT, SEVR, NSTA, NSEV, ACKS, ACKT, UDF
+=fields STAT, SEVR, AMSG, NSTA, NSEV, NAMSG, ACKS, ACKT, UDF
=cut
@@ -422,9 +438,11 @@ The B field is is for private use of the device support modules.
=head3 Debugging Fields
-The B field is used for trace processing. If this field is non-zero a
-message is printed whenever this record is processed, and when any other
-record in the same lock-set is processed by a database link from this record.
+The B field can be used to trace record processing. When this field is
+non-zero and the record is processed, a trace message will be be printed for
+this record and any other record in the same lock-set that is triggered by a
+database link from this record. The trace message includes the name of the
+thread doing the processing, and the name of the record being processed.
The B field indicates if there is a breakpoint set at this record. This
supports setting a debug breakpoint in the record processing. STEP through
@@ -435,32 +453,27 @@ database processing can be supported using this.
=head3 Miscellaneous Fields
-The B field contains a character string value defining the access
-security group for this record. If left empty, the record is placed in group
-DEFAULT.
+The B string field sets the name of the access security group used for this
+record. If left empty, the record is placed in group C.
-The B field is a field for private use of the access security system.
+The B field is private for use by the access security system.
-The B field controls dbPutFields to this record which are normally
-issued by channel access. If the field is set to TRUE all dbPutFields
-directed to this record are ignored except to the field DISP itself.
+The B field can be set to a non-zero value to reject puts from outside of
+the IOC (i.e. via Channel Access or PV Access) to any field of the record other
+than to the DISP field itself. Field changes and record processing can still be
+instigated from inside the IOC using DB links and the IOC scan mechanisms.
-The B field specifies the device type for the record. Each record type
-has its own set of device support routines which are specified in
-devSup.ASCII. If a record type does not have any associated device support,
-DTYP and DSET are meaningless.
+The B field specifies the device type for the record. Most record types
+have their own set of device types which are specified in the IOC's database
+definition file. If a record type does not call any device support routines,
+the DTYP and DSET fields are not used.
-The B field contains the monitor lock. The lock used by the monitor
-routines when the monitor list is being used. The list is locked whenever
-monitors are being scheduled, invoked, or when monitors are being added to or
-removed from the list. This field is accessed only by the dbEvent routines.
+The B field contains a mutex which is locked by the monitor routines in
+dbEvent.c whenever the monitor list for this record is accessed.
-The B field is the head of the list of monitors connected to this
+The B field holds a linked list of client monitors connected to this
record. Each record support module is responsible for triggering monitors for
-any fields that change as a result of record processing. Monitors are present
-if mlis count is greater than zero. The call to trigger monitors is:
-db_post_event(precord,&data,mask), where "mask" is some combination of
-DBE_ALARM, DBE_VALUE, and DBE_LOG.
+any fields that change as a result of record processing.
The B field contains the address of a putNotify callback.
@@ -474,23 +487,44 @@ The B field contains the address of dbRecordType
The B field specifies a reprocessing of the record when current
processing completes.
-The B field contains the time when this record was last processed in
-standard format.
+The B field holds the time stamp when this record was last processed.
-The B field indicates the mechanism to use to get the time stamp. '0' -
-call get time as before '-1' - call the time stamp driver and use the best
-source available. '-2' - the device support provides the time stamp from the
-hardware. Values between 1-255 request the time of the last occurance of a
-generalTime event.
+The B field can be used to hold a site-specific 64-bit User Tag value
+that is associated with the record's time stamp.
+
+The B field value indicates the mechanism to use to get the time stamp:
+
+=over
+
+=item *
+
+C< 0> E Get the current time as normal
+
+=item *
+
+C<-1> E Ask the time stamp driver for its best source of the current time, if
+available.
+
+=item *
+
+C<-2> E Device support sets the time stamp and the optional User Tag from the
+hardware.
+
+=item *
+
+Positive values (normally between 1-255) get the time of the last occurance of
+the numbered generalTime event.
+
+=back
The B field contains an input link for obtaining the time stamp. If this
-link references the .TIME field of a record then the time stamp of the
-referenced record becomes the time stamp for this record as well. In this
-case, an internal flag is set and ".TIME" is then overwritten by ".VAL". If
-any other field is referenced, the field value is read and stored in the .TSE
-field which is then used to acquire a timestamp.
+link points to the TIME field of a record then the time stamp and User Tag of
+that record are copied directly into this record (Channel Access links can only
+copy the time stamp, not the User Tag). If the link points to any other field,
+that field's value is read and stored in the TSE field which is then used to
+provide the time stamp as described above.
-=fields ASG, ASP, DISP, DTYP, MLOK, MLIS, PPN, PPNR, PUTF, RDES, RPRO, TIME, TSE, TSEL
+=fields ASG, ASP, DISP, DTYP, MLOK, MLIS, PPN, PPNR, PUTF, RDES, RPRO, TIME, UTAG, TSE, TSEL
=cut
diff --git a/modules/database/src/ioc/db/dbCommonInput.pod b/modules/database/src/ioc/db/dbCommonInput.dbd.pod
similarity index 99%
rename from modules/database/src/ioc/db/dbCommonInput.pod
rename to modules/database/src/ioc/db/dbCommonInput.dbd.pod
index 079277002..5ce9136b1 100644
--- a/modules/database/src/ioc/db/dbCommonInput.pod
+++ b/modules/database/src/ioc/db/dbCommonInput.dbd.pod
@@ -3,7 +3,7 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-=head1 Fields Common to Input Record Types
+=title Fields Common to Input Record Types
This section describes fields that are found in many input record types.
These fields usually have the same meaning whenever they are used.
@@ -206,3 +206,5 @@ If SIMM is not YES, NO or RAW, a SOFT alarm with a severity of INVALID is
raised, and return status is set to -1.
=back
+
+=cut
diff --git a/modules/database/src/ioc/db/dbCommonOutput.pod b/modules/database/src/ioc/db/dbCommonOutput.dbd.pod
similarity index 99%
rename from modules/database/src/ioc/db/dbCommonOutput.pod
rename to modules/database/src/ioc/db/dbCommonOutput.dbd.pod
index b60468269..cfea659c0 100644
--- a/modules/database/src/ioc/db/dbCommonOutput.pod
+++ b/modules/database/src/ioc/db/dbCommonOutput.dbd.pod
@@ -3,7 +3,7 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-=head1 Fields Common to Output Record Types
+=title Fields Common to Output Record Types
This section describes fields that are found in many output record types.
These fields usually have the same meaning whenever they are used.
@@ -237,3 +237,5 @@ If SIMM is not YES or NO, a SOFT alarm with a severity of INVALID is
raised, and return status is set to -1.
=back
+
+=cut
diff --git a/modules/database/src/ioc/db/dbCommonRecord.dbd.pod b/modules/database/src/ioc/db/dbCommonRecord.dbd.pod
index e331b8e00..fcbea4200 100644
--- a/modules/database/src/ioc/db/dbCommonRecord.dbd.pod
+++ b/modules/database/src/ioc/db/dbCommonRecord.dbd.pod
@@ -3,7 +3,7 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
-=head1 Fields Common to All Record Types
+=title Fields Common to All Record Types
This section contains a description of the fields that are common to all record
types. These fields are defined in dbCommon.dbd.
diff --git a/modules/database/src/ioc/db/dbConstLink.c b/modules/database/src/ioc/db/dbConstLink.c
index 91478d942..ec2d3184f 100644
--- a/modules/database/src/ioc/db/dbConstLink.c
+++ b/modules/database/src/ioc/db/dbConstLink.c
@@ -64,7 +64,7 @@ cvt_st_ ## TYPE(const char *from, void *pfield, const dbAddr *paddr) { \
return epicsParse##TYPE(from, to, 0, &end); \
}
-/* Instanciate for CHAR, UCHAR, SHORT, USHORT and LONG */
+/* Instantiate for CHAR, UCHAR, SHORT, USHORT and LONG */
cvt_st_int(Int8)
cvt_st_int(UInt8)
cvt_st_int(Int16)
@@ -100,7 +100,7 @@ static long cvt_st_UInt32(const char *from, void *pfield, const dbAddr *paddr)
return status;
}
-/* Instanciate for INT64 and UINT64 */
+/* Instantiate for INT64 and UINT64 */
cvt_st_int(Int64)
cvt_st_int(UInt64)
@@ -118,7 +118,7 @@ cvt_st_ ## TYPE(const char *from, void *pfield, const dbAddr *paddr) { \
return epicsParse##TYPE(from, to, &end); \
}
-/* Instanciate for FLOAT32 and FLOAT64 */
+/* Instantiate for FLOAT32 and FLOAT64 */
cvt_st_float(Float32)
cvt_st_float(Float64)
diff --git a/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp b/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp
index efcfa1d74..1c5e0d0f9 100644
--- a/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp
+++ b/modules/database/src/ioc/db/dbContextReadNotifyCache.cpp
@@ -9,7 +9,7 @@
\*************************************************************************/
/*
- * Auther Jeff Hill
+ * Author Jeff Hill
*/
#include
diff --git a/modules/database/src/ioc/db/dbConvertJSON.h b/modules/database/src/ioc/db/dbConvertJSON.h
index e01ad29aa..045bd937b 100644
--- a/modules/database/src/ioc/db/dbConvertJSON.h
+++ b/modules/database/src/ioc/db/dbConvertJSON.h
@@ -16,7 +16,7 @@
extern "C" {
#endif
-/* This name should probably be changed to inclue "array" */
+/* This name should probably be changed to include "array" */
DBCORE_API long dbPutConvertJSON(const char *json, short dbrType,
void *pdest, long *psize);
DBCORE_API long dbLSConvertJSON(const char *json, char *pdest,
diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c
index f8cfd8b5e..77d926630 100644
--- a/modules/database/src/ioc/db/dbDbLink.c
+++ b/modules/database/src/ioc/db/dbDbLink.c
@@ -188,8 +188,11 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
&& dbChannelSpecial(chan) != SPC_ATTRIBUTE
&& ellCount(&chan->filters) == 0)
{
- /* simple scalar: set up shortcut */
- unsigned short dbfType = dbChannelFinalFieldType(chan);
+ /* Simple scalar w/o filters, so *Final* type has no additional information.
+ * Needed to correctly handle DBF_MENU fields, which become DBF_ENUM during
+ * probe of dbChannelOpen().
+ */
+ unsigned short dbfType = dbChannelFieldType(chan);
if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE)
return S_db_badDbrtype;
diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
index 4af4a8952..5891c0afe 100644
--- a/modules/database/src/ioc/db/dbEvent.c
+++ b/modules/database/src/ioc/db/dbEvent.c
@@ -90,7 +90,7 @@ struct event_user {
void *extralabor_arg;/* parameter to above */
epicsThreadId taskid; /* event handler task id */
- struct evSubscrip *pSuicideEvent; /* event that is deleteing itself */
+ struct evSubscrip *pSuicideEvent; /* event that is deleting itself */
unsigned queovr; /* event que overflow count */
unsigned char pendexit; /* exit pend task */
unsigned char extra_labor; /* if set call extra labor func */
@@ -102,7 +102,7 @@ struct event_user {
/*
* Reliable intertask communication requires copying the current value of the
- * channel for later queing so 3 stepper motor steps of 10 each do not turn
+ * channel for later queuing so 3 stepper motor steps of 10 each do not turn
* into only 10 or 20 total steps part of the time.
*/
@@ -585,7 +585,7 @@ void db_cancel_event (dbEventSubscription event)
/*
* flag the event as canceled by NULLing out the callback handler
*
- * make certain that the event isnt being accessed while
+ * make certain that the event isn't being accessed while
* its call back changes
*/
LOCKEVQUE (pevent->ev_que);
@@ -731,7 +731,7 @@ static db_field_log* db_create_field_log (struct dbChannel *chan, int use_val)
/* don't make a copy yet, just reference the field value */
pLog->u.r.field = dbChannelField(chan);
/* indicate field value still owned by record */
- pLog->u.r.dtor = NULL;
+ pLog->dtor = NULL;
/* no private data yet, may be set by a filter */
pLog->u.r.pvt = NULL;
}
@@ -790,8 +790,8 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog)
LOCKEVQUE (ev_que);
/* if we have an event on the queue and both the last
- * event on the queue and the current event are empty,
- * simply ignore duplicate events.
+ * event on the queue and the current event reference
+ * a record field, simply ignore duplicate events.
*/
if (pevent->npend > 0u
&& !dbfl_has_copy(*pevent->pLastLog)
@@ -823,7 +823,7 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog)
pevent->nreplace++;
/*
* the event task has already been notified about
- * this so we dont need to post the semaphore
+ * this so we don't need to post the semaphore
*/
firstEventFlag = 0;
}
@@ -856,7 +856,7 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog)
UNLOCKEVQUE (ev_que);
/*
- * its more efficent to notify the event handler
+ * its more efficient to notify the event handler
* only after the event is ready and the lock
* is off in case it runs at a higher priority
* than the caller here.
@@ -898,6 +898,8 @@ unsigned int caEventMask
if ( (dbChannelField(pevent->chan) == (void *)pField || pField==NULL) &&
(caEventMask & pevent->select)) {
db_field_log *pLog = db_create_event_log(pevent);
+ if(pLog)
+ pLog->mask = caEventMask & pevent->select;
pLog = dbChannelRunPreChain(pevent->chan, pLog);
if (pLog) db_queue_event_log(pevent, pLog);
}
@@ -986,7 +988,7 @@ static int event_read ( struct event_que *ev_que )
* Next event pointer can be used by event tasks to determine
* if more events are waiting in the queue
*
- * Must remove the lock here so that we dont deadlock if
+ * Must remove the lock here so that we don't deadlock if
* this calls dbGetField() and blocks on the record lock,
* dbPutField() is in progress in another task, it has the
* record lock, and it is calling db_post_events() waiting
@@ -1186,9 +1188,6 @@ void db_event_flow_ctrl_mode_on (dbEventCtx ctx)
* notify the event handler task
*/
epicsEventSignal(evUser->ppendsem);
-#ifdef DEBUG
- printf("fc on %lu\n", tickGet());
-#endif
}
/*
@@ -1205,9 +1204,6 @@ void db_event_flow_ctrl_mode_off (dbEventCtx ctx)
* notify the event handler task
*/
epicsEventSignal (evUser->ppendsem);
-#ifdef DEBUG
- printf("fc off %lu\n", tickGet());
-#endif
}
/*
@@ -1217,7 +1213,7 @@ void db_delete_field_log (db_field_log *pfl)
{
if (pfl) {
/* Free field if reference type field log and dtor is set */
- if (pfl->type == dbfl_type_ref && pfl->u.r.dtor) pfl->u.r.dtor(pfl);
+ if (pfl->type == dbfl_type_ref && pfl->dtor) pfl->dtor(pfl);
/* Free the field log chunk */
freeListFree(dbevFieldLogFreeList, pfl);
}
diff --git a/modules/database/src/ioc/db/dbFastLinkConv.c b/modules/database/src/ioc/db/dbFastLinkConv.c
index fd2defb32..39cddce5f 100644
--- a/modules/database/src/ioc/db/dbFastLinkConv.c
+++ b/modules/database/src/ioc/db/dbFastLinkConv.c
@@ -14,7 +14,6 @@
*/
#include
-#include
#include
#include
#include
@@ -24,6 +23,7 @@
#include "dbDefs.h"
#include "epicsConvert.h"
#include "epicsStdlib.h"
+#include "epicsStdio.h"
#include "errlog.h"
#include "errMdef.h"
@@ -1335,24 +1335,26 @@ static long cvt_menu_st(
epicsEnum16 *from,
char *to,
const dbAddr *paddr)
- {
- dbFldDes *pdbFldDes;
- dbMenu *pdbMenu;
- char **papChoiceValue;
- char *pchoice;
+{
+ dbFldDes *pdbFldDes;
+ dbMenu *pdbMenu;
- if(! paddr
- || !(pdbFldDes = paddr->pfldDes)
- || !(pdbMenu = (dbMenu *)pdbFldDes->ftPvt)
- || *from>=pdbMenu->nChoice
- || !(papChoiceValue = pdbMenu->papChoiceValue)
- || !(pchoice=papChoiceValue[*from])) {
- recGblDbaddrError(S_db_badChoice,paddr,"dbFastLinkConv(cvt_menu_st)");
- return(S_db_badChoice);
+ if (!paddr ||
+ !(pdbFldDes = paddr->pfldDes) ||
+ !(pdbMenu = (dbMenu *)pdbFldDes->ftPvt)) {
+ recGblDbaddrError(S_db_badChoice, paddr, "dbFastLinkConv(cvt_menu_st)");
+ return S_db_badChoice;
}
- strncpy(to,pchoice,MAX_STRING_SIZE);
- return(0);
- }
+
+ if (*from < pdbMenu->nChoice) {
+ strncpy(to, pdbMenu->papChoiceValue[*from], MAX_STRING_SIZE);
+ }
+ else {
+ /* Convert out-of-range values to numeric strings */
+ epicsSnprintf(to, MAX_STRING_SIZE, "%u", *from);
+ }
+ return 0;
+}
/* Get Device to String */
@@ -1366,10 +1368,17 @@ static long cvt_device_st(
char **papChoice;
char *pchoice;
- if(!paddr
- || !(pdbFldDes = paddr->pfldDes)
- || !(pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt)
- || *from>=pdbDeviceMenu->nChoice
+ if (!paddr
+ || !(pdbFldDes = paddr->pfldDes)) {
+ recGblDbaddrError(S_db_errArg, paddr, "dbFastLinkConv(cvt_device_st)");
+ return S_db_errArg;
+ }
+ if (!(pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt)) {
+ /* Valid, record type has no device support */
+ *to = '\0';
+ return 0;
+ }
+ if (*from >= pdbDeviceMenu->nChoice
|| !(papChoice= pdbDeviceMenu->papChoice)
|| !(pchoice=papChoice[*from])) {
recGblDbaddrError(S_db_badChoice,paddr,"dbFastLinkConv(cvt_device_st)");
diff --git a/modules/database/src/ioc/db/dbIocRegister.c b/modules/database/src/ioc/db/dbIocRegister.c
index ef57c5d44..8bea9c74d 100644
--- a/modules/database/src/ioc/db/dbIocRegister.c
+++ b/modules/database/src/ioc/db/dbIocRegister.c
@@ -8,10 +8,13 @@
* in file LICENSE that is included with this distribution.
\*************************************************************************/
+#define EPICS_PRIVATE_API
+
#include "iocsh.h"
#include "callback.h"
#include "dbAccess.h"
+#include "dbStaticPvt.h"
#include "dbBkpt.h"
#include "dbCaTest.h"
#include "dbEvent.h"
@@ -28,53 +31,66 @@
DBCORE_API extern int callbackParallelThreadsDefault;
/* dbLoadDatabase */
-static const iocshArg dbLoadDatabaseArg0 = { "file name",iocshArgString};
-static const iocshArg dbLoadDatabaseArg1 = { "path",iocshArgString};
+static const iocshArg dbLoadDatabaseArg0 = { "file name",iocshArgStringPath};
+static const iocshArg dbLoadDatabaseArg1 = { "path",iocshArgStringPath};
static const iocshArg dbLoadDatabaseArg2 = { "substitutions",iocshArgString};
static const iocshArg * const dbLoadDatabaseArgs[3] =
{
&dbLoadDatabaseArg0,&dbLoadDatabaseArg1,&dbLoadDatabaseArg2
};
-static const iocshFuncDef dbLoadDatabaseFuncDef =
- {"dbLoadDatabase",3,dbLoadDatabaseArgs};
+static const iocshFuncDef dbLoadDatabaseFuncDef = {
+ "dbLoadDatabase",
+ 3,
+ dbLoadDatabaseArgs,
+ "Load the given .dbd file, with 'path' added as a search path, with the given substitutions.\n\n"
+ "Substitutions are usually not needed for .dbd files.\n\n"
+ "Example: dbLoadDatabase dbd/my.dbd\n",
+};
static void dbLoadDatabaseCallFunc(const iocshArgBuf *args)
{
iocshSetError(dbLoadDatabase(args[0].sval,args[1].sval,args[2].sval));
}
/* dbLoadRecords */
-static const iocshArg dbLoadRecordsArg0 = { "file name",iocshArgString};
+static const iocshArg dbLoadRecordsArg0 = { "file name",iocshArgStringPath};
static const iocshArg dbLoadRecordsArg1 = { "substitutions",iocshArgString};
static const iocshArg * const dbLoadRecordsArgs[2] = {&dbLoadRecordsArg0,&dbLoadRecordsArg1};
-static const iocshFuncDef dbLoadRecordsFuncDef = {"dbLoadRecords",2,dbLoadRecordsArgs};
+static const iocshFuncDef dbLoadRecordsFuncDef = {
+ "dbLoadRecords",
+ 2,
+ dbLoadRecordsArgs,
+ "Load the given .db file, with the given substitutions.\n\n"
+ "Substitutions should be given in the format 'var1=value1,var2=value2'.\n\n"
+ "Example: dbLoadRecords db/myRecords.db 'user=myself,host=myhost'\n",
+};
static void dbLoadRecordsCallFunc(const iocshArgBuf *args)
{
iocshSetError(dbLoadRecords(args[0].sval,args[1].sval));
}
/* dbb */
-static const iocshArg dbbArg0 = { "record name",iocshArgString};
+static const iocshArg dbbArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbbArgs[1] = {&dbbArg0};
static const iocshFuncDef dbbFuncDef = {"dbb",1,dbbArgs,
"Add breakpoint to a lock set.\n"};
static void dbbCallFunc(const iocshArgBuf *args) { dbb(args[0].sval);}
/* dbd */
-static const iocshArg dbdArg0 = { "record name",iocshArgString};
+static const iocshArg dbdArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbdArgs[1] = {&dbdArg0};
static const iocshFuncDef dbdFuncDef = {"dbd",1,dbdArgs,
"Remove breakpoint from a record.\n"};
static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);}
/* dbc */
-static const iocshArg dbcArg0 = { "record name",iocshArgString};
+static const iocshArg dbcArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbcArgs[1] = {&dbcArg0};
static const iocshFuncDef dbcFuncDef = {"dbc",1,dbcArgs,
"Continue processing in a lock set.\n"};
static void dbcCallFunc(const iocshArgBuf *args) { dbc(args[0].sval);}
/* dbs */
-static const iocshArg dbsArg0 = { "record name",iocshArgString};
+static const iocshArg dbsArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbsArgs[1] = {&dbsArg0};
static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs,
"Step through record processing.\n"};
@@ -86,7 +102,7 @@ static const iocshFuncDef dbstatFuncDef = {"dbstat",0,0,
static void dbstatCallFunc(const iocshArgBuf *args) { dbstat();}
/* dbp */
-static const iocshArg dbpArg0 = { "record name",iocshArgString};
+static const iocshArg dbpArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbpArg1 = { "interest level",iocshArgInt};
static const iocshArg * const dbpArgs[2] = {&dbpArg0,&dbpArg1};
static const iocshFuncDef dbpFuncDef = {"dbp",2,dbpArgs,
@@ -95,7 +111,7 @@ static void dbpCallFunc(const iocshArgBuf *args)
{ dbp(args[0].sval,args[1].ival);}
/* dbap */
-static const iocshArg dbapArg0 = { "record name",iocshArgString};
+static const iocshArg dbapArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbapArgs[1] = {&dbapArg0};
static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs,
"toggle printing after processing a certain record.\n"};
@@ -109,7 +125,7 @@ static const iocshFuncDef dbsrFuncDef = {"dbsr",1,dbsrArgs,
static void dbsrCallFunc(const iocshArgBuf *args) { dbsr(args[0].ival);}
/* dbcar */
-static const iocshArg dbcarArg0 = { "record name",iocshArgString};
+static const iocshArg dbcarArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbcarArg1 = { "level",iocshArgInt};
static const iocshArg * const dbcarArgs[2] = {&dbcarArg0,&dbcarArg1};
static const iocshFuncDef dbcarFuncDef = {"dbcar",2,dbcarArgs,
@@ -124,7 +140,7 @@ static void dbcarCallFunc(const iocshArgBuf *args)
}
/* dbjlr */
-static const iocshArg dbjlrArg0 = { "record name",iocshArgString};
+static const iocshArg dbjlrArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbjlrArg1 = { "level",iocshArgInt};
static const iocshArg * const dbjlrArgs[2] = {&dbjlrArg0,&dbjlrArg1};
static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs,
@@ -135,7 +151,7 @@ static void dbjlrCallFunc(const iocshArgBuf *args)
}
/* dbel */
-static const iocshArg dbelArg0 = { "record name",iocshArgString};
+static const iocshArg dbelArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbelArg1 = { "level",iocshArgInt};
static const iocshArg * const dbelArgs[2] = {&dbelArg0,&dbelArg1};
static const iocshFuncDef dbelFuncDef = {"dbel",2,dbelArgs,
@@ -147,7 +163,7 @@ static void dbelCallFunc(const iocshArgBuf *args)
}
/* dba */
-static const iocshArg dbaArg0 = { "record name",iocshArgString};
+static const iocshArg dbaArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbaArgs[1] = {&dbaArg0};
static const iocshFuncDef dbaFuncDef = {"dba",1,dbaArgs,
"dbAddr info.\n"};
@@ -181,21 +197,21 @@ static const iocshFuncDef dbliFuncDef = {"dbli",1,dbliArgs,
static void dbliCallFunc(const iocshArgBuf *args) { dbli(args[0].sval);}
/* dbla */
-static const iocshArg dblaArg0 = { "pattern",iocshArgString};
+static const iocshArg dblaArg0 = { "pattern",iocshArgStringRecord};
static const iocshArg * const dblaArgs[1] = {&dblaArg0};
static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs,
"List record alias()s by alias name pattern.\n"};
static void dblaCallFunc(const iocshArgBuf *args) { dbla(args[0].sval);}
/* dbgrep */
-static const iocshArg dbgrepArg0 = { "pattern",iocshArgString};
+static const iocshArg dbgrepArg0 = { "pattern",iocshArgStringRecord};
static const iocshArg * const dbgrepArgs[1] = {&dbgrepArg0};
static const iocshFuncDef dbgrepFuncDef = {"dbgrep",1,dbgrepArgs,
"List record names matching pattern.\n"};
static void dbgrepCallFunc(const iocshArgBuf *args) { dbgrep(args[0].sval);}
/* dbgf */
-static const iocshArg dbgfArg0 = { "record name",iocshArgString};
+static const iocshArg dbgfArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbgfArgs[1] = {&dbgfArg0};
static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs,
"Database Get Field.\n"
@@ -203,7 +219,7 @@ static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs,
static void dbgfCallFunc(const iocshArgBuf *args) { dbgf(args[0].sval);}
/* dbpf */
-static const iocshArg dbpfArg0 = { "record name",iocshArgString};
+static const iocshArg dbpfArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbpfArg1 = { "value",iocshArgString};
static const iocshArg * const dbpfArgs[2] = {&dbpfArg0,&dbpfArg1};
static const iocshFuncDef dbpfFuncDef = {"dbpf",2,dbpfArgs,
@@ -213,7 +229,7 @@ static void dbpfCallFunc(const iocshArgBuf *args)
{ dbpf(args[0].sval,args[1].sval);}
/* dbpr */
-static const iocshArg dbprArg0 = { "record name",iocshArgString};
+static const iocshArg dbprArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbprArg1 = { "interest level",iocshArgInt};
static const iocshArg * const dbprArgs[2] = {&dbprArg0,&dbprArg1};
static const iocshFuncDef dbprFuncDef = {"dbpr",2,dbprArgs,
@@ -223,14 +239,14 @@ static void dbprCallFunc(const iocshArgBuf *args)
{ dbpr(args[0].sval,args[1].ival);}
/* dbtr */
-static const iocshArg dbtrArg0 = { "record name",iocshArgString};
+static const iocshArg dbtrArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbtrArgs[1] = {&dbtrArg0};
static const iocshFuncDef dbtrFuncDef = {"dbtr",1,dbtrArgs,
"Process record and then some fields.\n"};
static void dbtrCallFunc(const iocshArgBuf *args) { dbtr(args[0].sval);}
/* dbtgf */
-static const iocshArg dbtgfArg0 = { "record name",iocshArgString};
+static const iocshArg dbtgfArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbtgfArgs[1] = {&dbtgfArg0};
static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs,
"Database Test Get Field.\n"
@@ -238,7 +254,7 @@ static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs,
static void dbtgfCallFunc(const iocshArgBuf *args) { dbtgf(args[0].sval);}
/* dbtpf */
-static const iocshArg dbtpfArg0 = { "record name",iocshArgString};
+static const iocshArg dbtpfArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbtpfArg1 = { "value",iocshArgString};
static const iocshArg * const dbtpfArgs[2] = {&dbtpfArg0,&dbtpfArg1};
static const iocshFuncDef dbtpfFuncDef = {"dbtpf",2,dbtpfArgs,
@@ -261,14 +277,14 @@ static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0,
static void dbhcrCallFunc(const iocshArgBuf *args) { dbhcr();}
/* gft */
-static const iocshArg gftArg0 = { "record name",iocshArgString};
+static const iocshArg gftArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const gftArgs[1] = {&gftArg0};
static const iocshFuncDef gftFuncDef = {"gft",1,gftArgs,
"Report dbChannel info and value.\n"};
static void gftCallFunc(const iocshArgBuf *args) { gft(args[0].sval);}
/* pft */
-static const iocshArg pftArg0 = { "record name",iocshArgString};
+static const iocshArg pftArg0 = { "record name",iocshArgStringRecord};
static const iocshArg pftArg1 = { "value",iocshArgString};
static const iocshArg * const pftArgs[2] = {&pftArg0,&pftArg1};
static const iocshFuncDef pftFuncDef = {"pft",2,pftArgs,
@@ -277,7 +293,7 @@ static void pftCallFunc(const iocshArgBuf *args)
{ pft(args[0].sval,args[1].sval);}
/* dbtpn */
-static const iocshArg dbtpnArg0 = { "record name",iocshArgString};
+static const iocshArg dbtpnArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbtpnArg1 = { "value",iocshArgString};
static const iocshArg * const dbtpnArgs[2] = {&dbtpnArg0,&dbtpnArg1};
static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs,
@@ -305,7 +321,7 @@ static void dbPutAttrCallFunc(const iocshArgBuf *args)
{ dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);}
/* tpn */
-static const iocshArg tpnArg0 = { "record name",iocshArgString};
+static const iocshArg tpnArg0 = { "record name",iocshArgStringRecord};
static const iocshArg tpnArg1 = { "value",iocshArgString};
static const iocshArg * const tpnArgs[2] = {&tpnArg0,&tpnArg1};
static const iocshFuncDef tpnFuncDef = {"tpn",2,tpnArgs,
@@ -314,7 +330,7 @@ static void tpnCallFunc(const iocshArgBuf *args)
{ tpn(args[0].sval,args[1].sval);}
/* dblsr */
-static const iocshArg dblsrArg0 = { "record name",iocshArgString};
+static const iocshArg dblsrArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dblsrArg1 = { "interest level",iocshArgInt};
static const iocshArg * const dblsrArgs[2] = {&dblsrArg0,&dblsrArg1};
static const iocshFuncDef dblsrFuncDef = {"dblsr",2,dblsrArgs,
@@ -487,6 +503,8 @@ static void dbStateShowAllCallFunc (const iocshArgBuf *args)
void dbIocRegister(void)
{
+ iocshCompleteRecord = &dbCompleteRecord;
+
iocshRegister(&dbbFuncDef,dbbCallFunc);
iocshRegister(&dbdFuncDef,dbdCallFunc);
iocshRegister(&dbcFuncDef,dbcCallFunc);
diff --git a/modules/database/src/ioc/db/dbLock.c b/modules/database/src/ioc/db/dbLock.c
index 6d2acaf2f..8ddb5dfc8 100644
--- a/modules/database/src/ioc/db/dbLock.c
+++ b/modules/database/src/ioc/db/dbLock.c
@@ -121,8 +121,7 @@ void dbLockIncRef(lockSet* ls)
{
int cnt = epicsAtomicIncrIntT(&ls->refcount);
if(cnt<=1) {
- errlogPrintf("dbLockIncRef(%p) on dead lockSet. refs: %d\n", ls, cnt);
- cantProceed(NULL);
+ cantProceed("dbLockIncRef(%p) on dead lockSet. refs: %d\n", ls, cnt);
}
}
@@ -145,9 +144,8 @@ void dbLockDecRef(lockSet *ls)
epicsMutexMustLock(ls->lock);
if(ellCount(&ls->lockRecordList)!=0) {
- errlogPrintf("dbLockDecRef(%p) would free lockSet with %d records\n",
- ls, ellCount(&ls->lockRecordList));
- cantProceed(NULL);
+ cantProceed("dbLockDecRef(%p) would free lockSet with %d records\n",
+ ls, ellCount(&ls->lockRecordList));
}
epicsMutexUnlock(ls->lock);
@@ -421,9 +419,8 @@ retry:
#ifdef LOCKSET_DEBUG
if(plock->owner) {
if(plock->owner!=myself || plock->ownercount<1) {
- errlogPrintf("dbScanLockMany(%p) ownership violation %p (%p) %u\n",
- locker, plock->owner, myself, plock->ownercount);
- cantProceed(NULL);
+ cantProceed("dbScanLockMany(%p) ownership violation %p (%p) %u\n",
+ locker, plock->owner, myself, plock->ownercount);
}
plock->ownercount++;
} else {
@@ -444,8 +441,7 @@ retry:
/* if we have at least one lockRecord, then we will always lock
* at least its present lockSet
*/
- errlogPrintf("dbScanLockMany(%p) didn't lock anything\n", locker);
- cantProceed(NULL);
+ cantProceed("dbScanLockMany(%p) didn't lock anything\n", locker);
}
}
@@ -602,17 +598,15 @@ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond)
#ifdef LOCKSET_DEBUG
if(locker && (A->owner!=myself || B->owner!=myself)) {
- errlogPrintf("dbLockSetMerge(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n",
- locker, pfirst->name, psecond->name,
- A->owner, B->owner, myself);
- cantProceed(NULL);
+ cantProceed("dbLockSetMerge(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n",
+ locker, pfirst->name, psecond->name,
+ A->owner, B->owner, myself);
}
#endif
if(locker && (A->ownerlocker!=locker || B->ownerlocker!=locker)) {
- errlogPrintf("dbLockSetMerge(%p,\"%s\",\"%s\") locker ownership violation %p %p (%p)\n",
- locker, pfirst->name, psecond->name,
- A->ownerlocker, B->ownerlocker, locker);
- cantProceed(NULL);
+ cantProceed("dbLockSetMerge(%p,\"%s\",\"%s\") locker ownership violation %p %p (%p)\n",
+ locker, pfirst->name, psecond->name,
+ A->ownerlocker, B->ownerlocker, locker);
}
if(A==B)
@@ -688,19 +682,17 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond)
#ifdef LOCKSET_DEBUG
if(ls->owner!=myself || psecond->lset->plockSet->owner!=myself) {
- errlogPrintf("dbLockSetSplit(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n",
- locker, pfirst->name, psecond->name,
- ls->owner, psecond->lset->plockSet->owner, myself);
- cantProceed(NULL);
+ cantProceed("dbLockSetSplit(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n",
+ locker, pfirst->name, psecond->name,
+ ls->owner, psecond->lset->plockSet->owner, myself);
}
#endif
/* lockset consistency violation */
if(ls!=psecond->lset->plockSet) {
- errlogPrintf("dbLockSetSplit(%p,\"%s\",\"%s\") consistency violation %p %p\n",
- locker, pfirst->name, psecond->name,
- pfirst->lset->plockSet, psecond->lset->plockSet);
- cantProceed(NULL);
+ cantProceed("dbLockSetSplit(%p,\"%s\",\"%s\") consistency violation %p %p\n",
+ locker, pfirst->name, psecond->name,
+ pfirst->lset->plockSet, psecond->lset->plockSet);
}
diff --git a/modules/database/src/ioc/db/dbNotify.h b/modules/database/src/ioc/db/dbNotify.h
index 3262009c9..98408bf7e 100644
--- a/modules/database/src/ioc/db/dbNotify.h
+++ b/modules/database/src/ioc/db/dbNotify.h
@@ -130,7 +130,7 @@ DBCORE_API int dbNotifyDump(void);
* if a process request is issued and also calls the client callbacks.
*
* A process request is issued if any of the following is true.
- * 1) The requester has issued a processs request and record is passive.
+ * 1) The requester has issued a process request and record is passive.
* 2) The requester is doing a put, the record is passive, and either
* a) The field description is process passive.
* b) The field is PROC.
@@ -156,7 +156,7 @@ DBCORE_API int dbNotifyDump(void);
* As soon as a record completes processing the field is set NULL
* ppnr pointer to processNotifyRecord, which is a private structure
* owned by dbNotify.
- * dbNotify is reponsible for this structure.
+ * dbNotify is responsible for this structure.
*
*/
#ifdef __cplusplus
diff --git a/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp b/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp
index 394b50c50..fe42d094c 100644
--- a/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp
+++ b/modules/database/src/ioc/db/dbPutNotifyBlocker.cpp
@@ -110,7 +110,7 @@ extern "C" void putNotifyCompletion ( processNotify *ppn )
if ( pNtfy ) {
pBlocker->pNotify = 0;
// Its necessary to signal the initiators now before we call
- // the user callback. This is less efficent, and potentially
+ // the user callback. This is less efficient, and potentially
// causes more thread context switching, but its probably
// unavoidable because its possible that the use callback
// might destroy this object.
diff --git a/modules/database/src/ioc/db/dbScan.c b/modules/database/src/ioc/db/dbScan.c
index 0b951dd4b..f3e354e78 100644
--- a/modules/database/src/ioc/db/dbScan.c
+++ b/modules/database/src/ioc/db/dbScan.c
@@ -817,7 +817,7 @@ static void periodicTask(void *arg)
epicsTimeAddSeconds(&next, delay);
if (++overruns >= 10 &&
epicsTimeDiffInSeconds(&now, &reported) > report_delay) {
- errlogPrintf("\ndbScan warning from '%s' scan thread:\n"
+ errlogPrintf("\ndbScan " ERL_WARNING " from '%s' scan thread:\n"
"\tScan processing averages %.3f seconds (%.3f .. %.3f).\n"
"\tOver-runs have now happened %u times in a row.\n"
"\tTo fix this, move some records to a slower scan rate.\n",
diff --git a/modules/database/src/ioc/db/dbTest.c b/modules/database/src/ioc/db/dbTest.c
index a82b9eb6b..c1d27d0ec 100644
--- a/modules/database/src/ioc/db/dbTest.c
+++ b/modules/database/src/ioc/db/dbTest.c
@@ -1304,7 +1304,7 @@ static void dbpr_insert_msg(TAB_BUFFER *pMsgBuff,size_t len,int tab_size)
current_len = strlen(pMsgBuff->out_buff);
tot_line = current_len + len;
- /* flush buffer if overflow would occor */
+ /* flush buffer if overflow would occur */
if (tot_line > MAXLINE)
dbpr_msg_flush(pMsgBuff, tab_size);
diff --git a/modules/database/src/ioc/db/dbUnitTest.h b/modules/database/src/ioc/db/dbUnitTest.h
index 57ccd0259..f697b07fa 100644
--- a/modules/database/src/ioc/db/dbUnitTest.h
+++ b/modules/database/src/ioc/db/dbUnitTest.h
@@ -7,7 +7,7 @@
\*************************************************************************/
/** @file dbUnitTest.h
- * @brief Helpers for unitests of process database
+ * @brief Helpers for unittests of process database
* @author Michael Davidsaver, Ralph Lange
*
* @see @ref dbunittest
@@ -99,13 +99,13 @@ DBCORE_API void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap);
/** Assert that a dbPutField() array operation will complete successfully.
*
* @param pv a PV name, possibly including filter expression
- * @param a DBF_\* type code (cf. dbfType in dbFldTypes.h)
+ * @param dbrType a DBF_\* type code (cf. dbfType in dbFldTypes.h)
* @param count Number of elements in pbuf array
* @param pbuf Array of values to write
*
* @code
* static const epicsUInt32 putval[] = {1,2,3};
- * testdbVGetFieldEqual("some:wf", DBF_ULONG, NELEMENTS(putval), putval);
+ * testdbPutArrFieldOk("some:wf", DBF_ULONG, NELEMENTS(putval), putval);
* @endcode
*
* @see @ref dbtestactions
@@ -114,7 +114,7 @@ DBCORE_API void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long
/**
* @param pv PV name string
- * @param dbfType One of the DBF_* macros from dbAccess.h
+ * @param dbfType One of the DBF_\* macros from dbAccess.h
* @param nRequest Number of elements to request from pv
* @param pbufcnt Number of elements pointed to be pbuf
* @param pbuf Expected value buffer
@@ -149,7 +149,7 @@ DBCORE_API void testMonitorDestroy(testMonitor*);
*/
DBCORE_API void testMonitorWait(testMonitor*);
/** Return the number of monitor events which have occured since create,
- * or a pervious reset (called reset=1).
+ * 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
@@ -190,6 +190,7 @@ DBCORE_API void testGlobalUnlock(void);
*
* @code
* #include
+ * #include
*
* int mytest_registerRecordDeviceDriver(DBBASE *pbase);
* void testCase(void) {
@@ -204,7 +205,7 @@ DBCORE_API void testGlobalUnlock(void);
* }
*
* MAIN(mytestmain) {
- * testPlan(0);
+ * testPlan(0); // adjust number of tests
* testCase();
* testCase(); // may be repeated if desirable.
* return testDone();
@@ -212,6 +213,9 @@ DBCORE_API void testGlobalUnlock(void);
* @endcode
*
* @code
+ * TOP = ..
+ * include $(TOP)/configure/CONFIG
+ *
* TARGETS += $(COMMON_DIR)/mytest.dbd
* DBDDEPENDS_FILES += mytest.dbd$(DEP)
* TESTFILES += $(COMMON_DIR)/mytest.dbd
@@ -222,6 +226,8 @@ DBCORE_API void testGlobalUnlock(void);
* mytest_SRCS += mytestmain.c # see above
* mytest_SRCS += mytest_registerRecordDeviceDriver.cpp
* TESTFILES += some.db
+ *
+ * include $(TOP)/configure/RULES
* @endcode
*
* @section dbtestactions Actions
diff --git a/modules/database/src/ioc/db/db_access_routines.h b/modules/database/src/ioc/db/db_access_routines.h
index cd8b09c40..ee2f07940 100644
--- a/modules/database/src/ioc/db/db_access_routines.h
+++ b/modules/database/src/ioc/db/db_access_routines.h
@@ -27,7 +27,7 @@ DBCORE_API extern volatile int interruptAccept;
/*
- * Adaptors for db_access users
+ * Adapters for db_access users
*/
DBCORE_API struct dbChannel * dbChannel_create(const char *pname);
DBCORE_API int dbChannel_get(struct dbChannel *chan,
diff --git a/modules/database/src/ioc/db/db_field_log.h b/modules/database/src/ioc/db/db_field_log.h
index b9deb6fe9..6f57859e2 100644
--- a/modules/database/src/ioc/db/db_field_log.h
+++ b/modules/database/src/ioc/db/db_field_log.h
@@ -97,7 +97,6 @@ struct dbfl_val {
* data is still owned by a record. See the macro dbfl_has_copy below.
*/
struct dbfl_ref {
- dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */
void *pvt; /* Private pointer */
void *field; /* Field value */
};
@@ -120,6 +119,7 @@ typedef struct db_field_log {
short field_type; /* DBF type of data */
short field_size; /* Size of a single element */
long no_elements; /* No of valid array elements */
+ dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */
union {
struct dbfl_val v;
struct dbfl_ref r;
@@ -136,7 +136,7 @@ typedef struct db_field_log {
* the db_field_log still owns the (empty) data.
*/
#define dbfl_has_copy(p)\
- ((p) && ((p)->type==dbfl_type_val || (p)->u.r.dtor || (p)->no_elements==0))
+ ((p) && ((p)->type==dbfl_type_val || (p)->dtor || (p)->no_elements==0))
#define dbfl_pfield(p)\
((p)->type==dbfl_type_val ? &p->u.v.field : p->u.r.field)
diff --git a/modules/database/src/ioc/db/recGbl.c b/modules/database/src/ioc/db/recGbl.c
index 95387f5de..c9bb62fdb 100644
--- a/modules/database/src/ioc/db/recGbl.c
+++ b/modules/database/src/ioc/db/recGbl.c
@@ -56,63 +56,66 @@ static void getMaxRangeValues(short field_type, double *pupper_limit,
double *plower_limit);
-
-void recGblDbaddrError(long status, const struct dbAddr *paddr,
- const char *pmessage)
-{
- dbCommon *precord = 0;
- dbFldDes *pdbFldDes = 0;
-
- if(paddr) {
- pdbFldDes = paddr->pfldDes;
- precord = paddr->precord;
- }
- errPrintf(status,0,0,
- "PV: %s.%s "
- "error detected in routine: %s\n",
- (precord ? precord->name : "Unknown"),
- (pdbFldDes ? pdbFldDes->name : ""),
- (pmessage ? pmessage : "Unknown"));
- return;
-}
-
void recGblRecordError(long status, void *pdbc,
const char *pmessage)
{
- dbCommon *precord = pdbc;
+ dbCommon *precord = pdbc;
+ char errMsg[256] = "";
- errPrintf(status,0,0,
- "PV: %s %s\n",
- (precord ? precord->name : "Unknown"),
- (pmessage ? pmessage : ""));
- return;
+ if (status)
+ errSymLookup(status, errMsg, sizeof(errMsg));
+
+ errlogPrintf("recGblRecordError: %s %s PV: %s\n",
+ pmessage ? pmessage : "", errMsg,
+ precord ? precord->name : "Unknown");
}
-
+
+void recGblDbaddrError(long status, const struct dbAddr *paddr,
+ const char *pmessage)
+{
+ dbCommon *precord = 0;
+ dbFldDes *pdbFldDes = 0;
+ char errMsg[256] = "";
+
+ if (paddr) {
+ pdbFldDes = paddr->pfldDes;
+ precord = paddr->precord;
+ }
+ if (status)
+ errSymLookup(status, errMsg, sizeof(errMsg));
+
+ errlogPrintf("recGblDbaddrError: %s %s PV: %s.%s\n",
+ pmessage ? pmessage : "",errMsg,
+ precord ? precord->name : "Unknown",
+ pdbFldDes ? pdbFldDes->name : "");
+}
+
void recGblRecSupError(long status, const struct dbAddr *paddr,
const char *pmessage, const char *psupport_name)
{
dbCommon *precord = 0;
dbFldDes *pdbFldDes = 0;
dbRecordType *pdbRecordType = 0;
+ char errMsg[256] = "";
- if(paddr) {
+ if (paddr) {
precord = paddr->precord;
pdbFldDes = paddr->pfldDes;
- if(pdbFldDes) pdbRecordType = pdbFldDes->pdbRecordType;
+ if (pdbFldDes)
+ pdbRecordType = pdbFldDes->pdbRecordType;
}
- errPrintf(status,0,0,
- "Record Support Routine (%s) "
- "Record Type %s "
- "PV %s.%s "
- " %s\n",
- (psupport_name ? psupport_name : "Unknown"),
- (pdbRecordType ? pdbRecordType->name : "Unknown"),
- (precord ? precord->name : "Unknown"),
- (pdbFldDes ? pdbFldDes->name : ""),
- (pmessage ? pmessage : ""));
- return;
+
+ if (status)
+ errSymLookup(status, errMsg, sizeof(errMsg));
+
+ errlogPrintf("recGblRecSupError: %s %s %s::%s PV: %s.%s\n",
+ pmessage ? pmessage : "", errMsg,
+ pdbRecordType ? pdbRecordType->name : "Unknown",
+ psupport_name ? psupport_name : "Unknown",
+ precord ? precord->name : "Unknown",
+ pdbFldDes ? pdbFldDes->name : "");
}
-
+
void recGblGetPrec(const struct dbAddr *paddr, long *precision)
{
dbFldDes *pdbFldDes = paddr->pfldDes;
diff --git a/modules/database/src/ioc/dbStatic/Makefile b/modules/database/src/ioc/dbStatic/Makefile
index c962501d9..e9bd95b8d 100644
--- a/modules/database/src/ioc/dbStatic/Makefile
+++ b/modules/database/src/ioc/dbStatic/Makefile
@@ -28,5 +28,6 @@ dbCore_SRCS += dbYacc.c
dbCore_SRCS += dbPvdLib.c
dbCore_SRCS += dbStaticRun.c
dbCore_SRCS += dbStaticIocRegister.c
+dbCore_SRCS += dbCompleteRecord.cpp
CLEANS += dbLex.c dbYacc.c
diff --git a/modules/database/src/ioc/dbStatic/dbCompleteRecord.cpp b/modules/database/src/ioc/dbStatic/dbCompleteRecord.cpp
new file mode 100644
index 000000000..73ad6164f
--- /dev/null
+++ b/modules/database/src/ioc/dbStatic/dbCompleteRecord.cpp
@@ -0,0 +1,161 @@
+/*************************************************************************\
+* Copyright (c) 2022 Michael Davidsaver
+* SPDX-License-Identifier: EPICS
+* EPICS Base is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include "dbStaticPvt.h"
+
+namespace {
+
+// immutable C string slice. (avoid allocating many temporary std::string)
+class CStr {
+ const char* p;
+ size_t l;
+public:
+ CStr() :p(NULL), l(0u) {}
+ CStr(const CStr& o) :p(o.p), l(o.l) {}
+ explicit CStr(const char* p) :p(p), l(p ? strlen(p) : 0u) {}
+ CStr(const char* p, size_t n) :p(p), l(n) {}
+
+ CStr& operator=(const CStr& o) {
+ p = o.p;
+ l = o.l;
+ return *this;
+ }
+
+ bool operator==(const CStr& o) const {
+ return l==o.l && (p==o.p || memcmp(p, o.p, l)==0);
+ }
+ bool operator!=(const CStr& o) const {
+ return !(*this==o);
+ }
+ bool operator<(const CStr& o) const {
+ size_t pl = std::min(l, o.l);
+ int cmp = memcmp(p, o.p, pl);
+ return cmp<0 || (cmp==0 && l < o.l);
+ }
+
+ bool empty() const { return !l; }
+ size_t size() const { return l; }
+
+ bool prefixOf(const CStr& full) const {
+ return full.l >= l && memcmp(full.p, p, l)==0;
+ }
+
+ CStr commonPrefix(const CStr& o, size_t startFrom=0u) const {
+ size_t n, N;
+ for(n=startFrom, N=std::min(l, o.l); nrecordname);
+
+ if(!word.prefixOf(name))
+ continue;
+
+ if(first) { // first match
+ prefix = name;
+ first = false;
+
+ } else {
+ prefix = prefix.commonPrefix(name, word.size());
+ }
+ }
+ }
+
+ // with prefix size known, iterate again to find suggestions
+ typedef std::set suggestions_t;
+ suggestions_t suggestions;
+
+ for(long status = dbFirstRecordType(&ent); !status; status = dbNextRecordType(&ent)) {
+ for(status = dbFirstRecord(&ent); !status; status = dbNextRecord(&ent)) {
+ CStr name(ent.precnode->recordname);
+
+ if(!prefix.prefixOf(name))
+ continue;
+
+ name.chop_at_first_of(":<>{}-", prefix.size());
+ suggestions.insert(name);
+ }
+ }
+
+ dbFinishEntry(&ent);
+
+ char** ret = NULL;
+
+ if(!prefix.empty() || !suggestions.empty()) {
+ ret = (char**)malloc(sizeof(*ret)*(2u + suggestions.size()));
+ ret[0] = prefix.dup();
+ size_t n=1u;
+ for(suggestions_t::iterator it(suggestions.begin()), end(suggestions.end());
+ it!=end; ++it)
+ {
+ ret[n++] = it->dup();
+ }
+ ret[n] = NULL;
+ }
+
+ return ret;
+
+ } catch(std::exception& e){
+ fprintf(stderr, "dbCompleteRecord error: %s\n", e.what());
+ dbFinishEntry(&ent);
+ return NULL;
+ }
+
+}
diff --git a/modules/database/src/ioc/dbStatic/dbLex.l b/modules/database/src/ioc/dbStatic/dbLex.l
index 33185133c..52f4ec31e 100644
--- a/modules/database/src/ioc/dbStatic/dbLex.l
+++ b/modules/database/src/ioc/dbStatic/dbLex.l
@@ -15,7 +15,8 @@ doublequote "\""
comment "#"
whitespace [ \t\r\n]
escape {backslash}.
-stringchar [^"\n\\]
+sqschar [^'\n\\]
+dqschar [^"\n\\]
bareword [a-zA-Z0-9_\-+:.\[\]<>;]
punctuation [:,\[\]{}]
@@ -84,7 +85,7 @@ static int yyreset(void)
return(tokenSTRING);
}
-{doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */
+{doublequote}({dqschar}|{escape})*{doublequote} { /* quoted string */
yylval.Str = dbmfStrdup((char *) yytext+1);
yylval.Str[strlen(yylval.Str)-1] = '\0';
return(tokenSTRING);
@@ -101,10 +102,6 @@ static int yyreset(void)
")" return(yytext[0]);
"," return(yytext[0]);
-{doublequote}({stringchar}|{escape})*{newline} { /* bad string */
- yyerrorAbort("Newline in string, closing quote missing");
-}
-
"null" return jsonNULL;
"true" return jsonTRUE;
"false" return jsonFALSE;
@@ -130,6 +127,20 @@ static int yyreset(void)
{whitespace} ;
+
+ /* Error patterns */
+
+{doublequote}({dqschar}|{escape})*{newline} {
+ yyerrorAbort("Newline in string, closing quote missing");
+}
+
+{doublequote}({dqschar}|{escape})*{doublequote} {
+ yyerrorAbort("Bad character in JSON string");
+}
+{singlequote}({sqschar}|{escape})*{singlequote} {
+ yyerrorAbort("Bad character in JSON string");
+}
+
. {
char message[40];
YY_BUFFER_STATE *dummy=0;
diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c
index 525846186..719ffc9db 100644
--- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c
+++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c
@@ -37,7 +37,11 @@
#include "special.h"
#include "iocInit.h"
-
+/* This file is included from dbYacc.y
+ * Duplicate some declarations to avoid warnings from analysis tools which don't know about this.
+ */
+static int yyerror(char *str);
+static long pvt_yy_parse(void);
/*global declarations*/
char *makeDbdDepends=0;
@@ -99,8 +103,8 @@ static char *my_buffer_ptr=NULL;
static MAC_HANDLE *macHandle = NULL;
typedef struct inputFile{
ELLNODE node;
- char *path;
- char *filename;
+ const char *path;
+ const char *filename;
FILE *fp;
int line_num;
}inputFile;
@@ -155,7 +159,7 @@ static void *getLastTemp(void)
return(ptempListNode->item);
}
-static char *dbOpenFile(DBBASE *pdbbase,const char *filename,FILE **fp)
+const char *dbOpenFile(DBBASE *pdbbase,const char *filename,FILE **fp)
{
ELLLIST *ppathList = (ELLLIST *)pdbbase->pathPvt;
dbPathNode *pdbPathNode;
@@ -223,8 +227,10 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
epicsPrintf("dbReadCOM: Parser stack dirty %d\n", ellCount(&tempList));
}
- if (getIocState() != iocVoid)
- return -2;
+ if (getIocState() != iocVoid) {
+ status = -2;
+ goto cleanup;
+ }
if(*ppdbbase == 0) *ppdbbase = dbAllocBase();
pdbbase = *ppdbbase;
@@ -268,8 +274,8 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
pinputFile->path = dbOpenFile(pdbbase, pinputFile->filename, &fp1);
if (!pinputFile->filename || !fp1) {
errPrintf(0, __FILE__, __LINE__,
- "dbRead opening file %s",pinputFile->filename);
- free(pinputFile->filename);
+ "dbRead opening file %s\n",pinputFile->filename);
+ free((char*)pinputFile->filename);
free(pinputFile);
status = -1;
goto cleanup;
@@ -277,6 +283,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
pinputFile->fp = fp1;
} else {
pinputFile->fp = fp;
+ fp = NULL;
}
pinputFile->line_num = 0;
pinputFileNow = pinputFile;
@@ -332,6 +339,8 @@ cleanup:
if(my_buffer) free((void *)my_buffer);
my_buffer = NULL;
freeInputFileList();
+ if(fp)
+ fclose(fp);
return(status);
}
@@ -1063,7 +1072,7 @@ int dbRecordNameValidate(const char *name)
}
for(; *pos; i++, pos++) {
- char c = *pos;
+ unsigned char c = *pos;
if(i==0) {
/* first character restrictions */
if(c=='-' || c=='+' || c=='[' || c=='{') {
@@ -1072,8 +1081,8 @@ int dbRecordNameValidate(const char *name)
}
/* any character restrictions */
if(c < ' ') {
- errlogPrintf("Warning: Record/Alias name '%s' should not contain non-printable 0x%02u\n",
- name, (unsigned)c);
+ errlogPrintf("Warning: Record/Alias name '%s' should not contain non-printable 0x%02x\n",
+ name, c);
} else if(c==' ' || c=='\t' || c=='"' || c=='\'' || c=='.' || c=='$') {
epicsPrintf("Error: Bad character '%c' in Record/Alias name \"%s\"\n",
diff --git a/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c b/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c
index 6851d41fc..751b7b250 100644
--- a/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c
+++ b/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c
@@ -106,7 +106,13 @@ static void dbDumpDriverCallFunc(const iocshArgBuf *args)
/* dbDumpLink */
static const iocshArg * const dbDumpLinkArgs[] = { &argPdbbase};
-static const iocshFuncDef dbDumpLinkFuncDef = {"dbDumpLink",1,dbDumpLinkArgs};
+static const iocshFuncDef dbDumpLinkFuncDef = {
+ "dbDumpLink",
+ 1,
+ dbDumpLinkArgs,
+ "Dump list of registered links\n"
+ "Example: dbDumpLink pdbbase\n",
+};
static void dbDumpLinkCallFunc(const iocshArgBuf *args)
{
dbDumpLink(*iocshPpdbbase);
@@ -147,7 +153,13 @@ static void dbDumpVariableCallFunc(const iocshArgBuf *args)
static const iocshArg dbDumpBreaktableArg1 = { "tableName",iocshArgString};
static const iocshArg * const dbDumpBreaktableArgs[] =
{&argPdbbase,&dbDumpBreaktableArg1};
-static const iocshFuncDef dbDumpBreaktableFuncDef = {"dbDumpBreaktable",2,dbDumpBreaktableArgs};
+static const iocshFuncDef dbDumpBreaktableFuncDef = {
+ "dbDumpBreaktable",
+ 2,
+ dbDumpBreaktableArgs,
+ "Dump the given break table\n"
+ "Example: dbDumpBreaktable pdbbase typeKdegC\n",
+};
static void dbDumpBreaktableCallFunc(const iocshArgBuf *args)
{
dbDumpBreaktable(*iocshPpdbbase,args[1].sval);
@@ -157,7 +169,14 @@ static void dbDumpBreaktableCallFunc(const iocshArgBuf *args)
static const iocshArg dbPvdDumpArg1 = { "verbose",iocshArgInt};
static const iocshArg * const dbPvdDumpArgs[] = {
&argPdbbase,&dbPvdDumpArg1};
-static const iocshFuncDef dbPvdDumpFuncDef = {"dbPvdDump",2,dbPvdDumpArgs};
+static const iocshFuncDef dbPvdDumpFuncDef = {
+ "dbPvdDump",
+ 2,
+ dbPvdDumpArgs,
+ "Dump the various buckets of the process variable directory.\n"
+ "If verbose is greater than 0, also print the process variables in each bucket.\n"
+ "Example: dbPvdDump pdbbase 1\n",
+};
static void dbPvdDumpCallFunc(const iocshArgBuf *args)
{
dbPvdDump(*iocshPpdbbase,args[1].ival);
@@ -167,7 +186,16 @@ static void dbPvdDumpCallFunc(const iocshArgBuf *args)
static const iocshArg dbPvdTableSizeArg0 = { "size",iocshArgInt};
static const iocshArg * const dbPvdTableSizeArgs[1] =
{&dbPvdTableSizeArg0};
-static const iocshFuncDef dbPvdTableSizeFuncDef = {"dbPvdTableSize",1,dbPvdTableSizeArgs};
+static const iocshFuncDef dbPvdTableSizeFuncDef = {
+ "dbPvdTableSize",
+ 1,
+ dbPvdTableSizeArgs,
+ "Change the number of buckets in the process variable directory.\n\n"
+ "The process variable directory size should be set before loading the database.\n"
+ "The size of the process variable directory can automatically grow.\n"
+ "The size must be a power of 2.\n\n"
+ "Example: dbPvdTableSize 1024\n",
+};
static void dbPvdTableSizeCallFunc(const iocshArgBuf *args)
{
dbPvdTableSize(args[0].ival);
@@ -175,7 +203,15 @@ static void dbPvdTableSizeCallFunc(const iocshArgBuf *args)
/* dbReportDeviceConfig */
static const iocshArg * const dbReportDeviceConfigArgs[] = {&argPdbbase};
-static const iocshFuncDef dbReportDeviceConfigFuncDef = {"dbReportDeviceConfig",1,dbReportDeviceConfigArgs};
+static const iocshFuncDef dbReportDeviceConfigFuncDef = {
+ "dbReportDeviceConfig",
+ 1,
+ dbReportDeviceConfigArgs,
+ "Print the link type, link value, device type, record name,\n"
+ "and linearisation info (if applicable),\n"
+ "for every record using a specific device type.\n\n"
+ "Example: dbReportDeviceConfig pdbbase\n",
+};
static void dbReportDeviceConfigCallFunc(const iocshArgBuf *args)
{
dbReportDeviceConfig(*iocshPpdbbase,stdout);
diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.c b/modules/database/src/ioc/dbStatic/dbStaticLib.c
index bdc568b88..df1826384 100644
--- a/modules/database/src/ioc/dbStatic/dbStaticLib.c
+++ b/modules/database/src/ioc/dbStatic/dbStaticLib.c
@@ -441,6 +441,9 @@ void dbFreeBase(dbBase *pdbbase)
DBENTRY dbentry;
long status;
+ if(!pdbbase)
+ return;
+
dbInitEntry(pdbbase,&dbentry);
status = dbFirstRecordType(&dbentry);
while(!status) {
@@ -677,7 +680,7 @@ long dbAddPath(DBBASE *pdbbase,const char *path)
if (!path) return(0); /* Empty path strings are ignored */
/* care is taken to properly deal with white space
* 1) preceding and trailing white space is removed from paths
- * 2) white space inbetween path separator counts as an empty name
+ * 2) white space in between path separator counts as an empty name
* (see below)
*/
expectingPath = FALSE;
@@ -710,8 +713,8 @@ long dbAddPath(DBBASE *pdbbase,const char *path)
/*
* len is always nonzero because we found something that
- * 1) isnt white space
- * 2) isnt a path separator
+ * 1) isn't white space
+ * 2) isn't a path separator
*/
len = (plast - path) + 1;
if (dbAddOnePath (pdbbase, path, (unsigned) len)) return (-1);
@@ -722,7 +725,7 @@ long dbAddPath(DBBASE *pdbbase,const char *path)
}
/*
- * an empty name at beginning, middle, or end of a path string that isnt
+ * an empty name at beginning, middle, or end of a path string that isn't
* empty means current directory
*/
if (expectingPath||sawMissingPath) {
@@ -2038,13 +2041,17 @@ char *dbGetStringNum(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
void *pfield = pdbentry->pfield;
- char *message;
+ char *message = getpMessage(pdbentry);
unsigned char cvttype;
+ if (!pfield) {
+ dbMsgCpy(pdbentry, "Field not found");
+ return message;
+ }
+
/* the following assumes that messagesize is large enough
* to hold the base 10 encoded value of a 32-bit integer.
*/
- message = getpMessage(pdbentry);
cvttype = pflddes->base;
switch (pflddes->field_type) {
case DBF_CHAR:
@@ -2106,37 +2113,34 @@ char *dbGetStringNum(DBENTRY *pdbentry)
{
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
epicsEnum16 choice_ind;
- char *pchoice;
- if (!pfield) {
- dbMsgCpy(pdbentry, "Field not found");
- return message;
- }
- choice_ind = *((epicsEnum16 *) pdbentry->pfield);
- if (!pdbMenu || choice_ind < 0 || choice_ind >= pdbMenu->nChoice)
+ if (!pdbMenu)
return NULL;
- pchoice = pdbMenu->papChoiceValue[choice_ind];
- dbMsgCpy(pdbentry, pchoice);
+
+ choice_ind = *((epicsEnum16 *) pdbentry->pfield);
+ if (choice_ind >= pdbMenu->nChoice) {
+ dbMsgPrint(pdbentry, "%u", choice_ind);
+ }
+ else {
+ dbMsgCpy(pdbentry, pdbMenu->papChoiceValue[choice_ind]);
+ }
}
break;
case DBF_DEVICE:
{
- dbDeviceMenu *pdbDeviceMenu;
+ dbDeviceMenu *pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
epicsEnum16 choice_ind;
- char *pchoice;
- if (!pfield) {
- dbMsgCpy(pdbentry, "Field not found");
- return message;
+ if (!pdbDeviceMenu) {
+ dbMsgCpy(pdbentry, "");
+ break;
}
- pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
- if (!pdbDeviceMenu)
- return NULL;
+
choice_ind = *((epicsEnum16 *) pdbentry->pfield);
- if (choice_ind<0 || choice_ind>=pdbDeviceMenu->nChoice)
+ if (choice_ind>=pdbDeviceMenu->nChoice)
return NULL;
- pchoice = pdbDeviceMenu->papChoice[choice_ind];
- dbMsgCpy(pdbentry, pchoice);
+
+ dbMsgCpy(pdbentry, pdbDeviceMenu->papChoice[choice_ind]);
}
break;
default:
@@ -2333,11 +2337,11 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
}
pinfo->ltype = PV_LINK;
- pstr = strchr(pstr, ' '); /* find start of link modifiers (can't be seperated by tabs) */
+ pstr = strchr(pstr, ' '); /* find start of link modifiers (can't be separated by tabs) */
if (pstr) {
*pstr++ = '\0'; /* isolate modifiers. pinfo->target is PV name only for re-use in struct pv_link */
- /* Space seperation of modifiers isn't required, and other chars are ignored.
+ /* Space separation of modifiers isn't required, and other chars are ignored.
* Order of comparisons resolves ambiguity by checking for
* longer matches first.
* eg. "QQCPPXMSITT" is pvlOptCPP|pvlOptMSI
@@ -3293,11 +3297,11 @@ void dbDumpRecordType(DBBASE *pdbbase,const char *recordTypeName)
printf("name(%s) no_fields(%hd) no_prompt(%hd) no_links(%hd)\n",
pdbRecordType->name,pdbRecordType->no_fields,
pdbRecordType->no_prompt,pdbRecordType->no_links);
- printf("index name\tsortind sortname\n");
+ printf("index offset size name\tsortind sortname\n");
for(i=0; ino_fields; i++) {
pdbFldDes = pdbRecordType->papFldDes[i];
- printf("%5d %s\t%7d %s\n",
- i,pdbFldDes->name,
+ printf("%5d %6u %4u %s\t%7d %s\n",
+ i,pdbFldDes->offset,pdbFldDes->size, pdbFldDes->name,
pdbRecordType->sortFldInd[i],pdbRecordType->papsortFldName[i]);
}
printf("link_ind ");
diff --git a/modules/database/src/ioc/dbStatic/dbStaticLib.h b/modules/database/src/ioc/dbStatic/dbStaticLib.h
index ea42efccb..568517edd 100644
--- a/modules/database/src/ioc/dbStatic/dbStaticLib.h
+++ b/modules/database/src/ioc/dbStatic/dbStaticLib.h
@@ -57,8 +57,26 @@ DBCORE_API void dbCopyEntryContents(DBENTRY *pfrom,
DBCORE_API extern int dbBptNotMonotonic;
+/** \brief Open .dbd or .db file and read definitions.
+ * \param ppdbbase The database. Typically the "pdbbase" global
+ * \param filename Filename to read/search. May be absolute, or relative.
+ * \param path If !NULL, search path when filename is relative, of for 'include' statements.
+ * Split by ':' or ';' (cf. OSI_PATH_LIST_SEPARATOR)
+ * \param substitutions If !NULL, macro definitions like "NAME=VAL,OTHER=SOME"
+ * \return 0 on success
+ */
DBCORE_API long dbReadDatabase(DBBASE **ppdbbase,
const char *filename, const char *path, const char *substitutions);
+/** \brief Read definitions from already opened .dbd or .db file.
+ * \param ppdbbase The database. Typically the "&pdbbase" global
+ * \param fp FILE* from which to read definitions. Will always be fclose()'d
+ * \param path If !NULL, search path when filename is relative, of for 'include' statements.
+ * Split by ':' or ';' (cf. OSI_PATH_LIST_SEPARATOR)
+ * \param substitutions If !NULL, macro definitions like "NAME=VAL,OTHER=SOME"
+ * \return 0 on success
+ *
+ * \note This function will always close the provided 'fp'.
+ */
DBCORE_API long dbReadDatabaseFP(DBBASE **ppdbbase,
FILE *fp, const char *path, const char *substitutions);
DBCORE_API long dbPath(DBBASE *pdbbase, const char *path);
diff --git a/modules/database/src/ioc/dbStatic/dbStaticPvt.h b/modules/database/src/ioc/dbStatic/dbStaticPvt.h
index 0abc12864..cce4d8cb8 100644
--- a/modules/database/src/ioc/dbStatic/dbStaticPvt.h
+++ b/modules/database/src/ioc/dbStatic/dbStaticPvt.h
@@ -16,6 +16,9 @@
#ifndef INCdbStaticPvth
#define INCdbStaticPvth 1
+#include "dbCoreAPI.h"
+#include "dbStaticLib.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -40,6 +43,9 @@ void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) EPICS_PRINTF_STYLE(2,3)
void dbPutStringSuggest(DBENTRY *pdbentry, const char *pstring);
+DBCORE_API
+const char *dbOpenFile(DBBASE *pdbbase,const char *filename,FILE **fp);
+
struct jlink;
typedef struct dbLinkInfo {
@@ -109,6 +115,9 @@ PVDENTRY *dbPvdAdd(DBBASE *pdbbase,dbRecordType *precordType,dbRecordNode *precn
void dbPvdDelete(DBBASE *pdbbase,dbRecordNode *precnode);
void dbPvdFreeMem(DBBASE *pdbbase);
+DBCORE_API
+char** dbCompleteRecord(const char *word);
+
#ifdef __cplusplus
}
#endif
diff --git a/modules/database/src/ioc/dbStatic/dbYacc.y b/modules/database/src/ioc/dbStatic/dbYacc.y
index cc563bcfd..75fb86cf0 100644
--- a/modules/database/src/ioc/dbStatic/dbYacc.y
+++ b/modules/database/src/ioc/dbStatic/dbYacc.y
@@ -374,7 +374,7 @@ static int yyerror(char *str)
else
epicsPrintf("Error");
if (!yyFailed) { /* Only print this stuff once */
- epicsPrintf(" at or before \"%s\"", yytext);
+ epicsPrintf(" at or before '%s'", yytext);
dbIncludePrint();
yyFailed = TRUE;
}
diff --git a/modules/database/src/ioc/dbStatic/devSup.h b/modules/database/src/ioc/dbStatic/devSup.h
index c41b834ea..799569927 100644
--- a/modules/database/src/ioc/dbStatic/devSup.h
+++ b/modules/database/src/ioc/dbStatic/devSup.h
@@ -77,7 +77,7 @@ typedef struct typed_dset {
long (*init_record)(struct dbCommon *prec);
/** Called when SCAN="I/O Intr" on startup, or after SCAN is changed.
*
- * Caller must assign the third arguement (IOCSCANPVT*). eg.
+ * Caller must assign the third argument (IOCSCANPVT*). eg.
@code
struct mpvt {
IOSCANPVT drvlist;
@@ -173,7 +173,7 @@ DBCORE_API void dbInitDevSup(struct devSup *pdevSup, dset *pdset);
#define S_dev_badOutType (M_devSup| 9) /*Bad OUT link type*/
#define S_dev_badInitRet (M_devSup|11) /*Bad init_rec return value */
#define S_dev_badBus (M_devSup|13) /*Illegal bus type*/
-#define S_dev_badCard (M_devSup|15) /*Illegal or nonexistant module*/
+#define S_dev_badCard (M_devSup|15) /*Illegal or nonexistent module*/
#define S_dev_badSignal (M_devSup|17) /*Illegal signal*/
#define S_dev_NoInit (M_devSup|19) /*No init*/
#define S_dev_Conflict (M_devSup|21) /*Multiple records accessing same signal*/
diff --git a/modules/database/src/ioc/dbStatic/link.h b/modules/database/src/ioc/dbStatic/link.h
index c5481fd74..74607a1b1 100644
--- a/modules/database/src/ioc/dbStatic/link.h
+++ b/modules/database/src/ioc/dbStatic/link.h
@@ -180,7 +180,7 @@ union value {
struct vmeio vmeio; /* vme io point */
struct camacio camacio; /* camac io point */
struct rfio rfio; /* CEBAF RF buffer interface */
- struct abio abio; /* allen-bradley io point */
+ struct abio abio; /* Allen-Bradley io point */
struct gpibio gpibio;
struct bitbusio bitbusio;
struct instio instio; /* instrument io link */
diff --git a/modules/database/src/ioc/dbStatic/special.h b/modules/database/src/ioc/dbStatic/special.h
index 575294f3c..f340ca757 100644
--- a/modules/database/src/ioc/dbStatic/special.h
+++ b/modules/database/src/ioc/dbStatic/special.h
@@ -21,14 +21,14 @@
extern "C" {
#endif
-/*NOTE Do NOT add aditional definitions with out modifying dbLexRoutines.c */
+/*NOTE Do NOT add additional definitions with out modifying dbLexRoutines.c */
/* types 1-99 are global. Record specific must start with 100 */
#define SPC_NOMOD 1 /*Field must not be modified*/
#define SPC_DBADDR 2 /*db_name_to_addr must call cvt_dbaddr*/
#define SPC_SCAN 3 /*A scan related field is being changed*/
#define SPC_ALARMACK 5 /*Special Alarm Acknowledgement*/
#define SPC_AS 6 /* Access Security*/
-#define SPC_ATTRIBUTE 7 /* psuedo field, i.e. attribute field*/
+#define SPC_ATTRIBUTE 7 /* pseudo field, i.e. attribute field*/
/* useful when record support must be notified of a field changing value*/
#define SPC_MOD 100
/* used by all records that support a reset field*/
diff --git a/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c b/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c
index 8ee6b2c40..f4cfaf5ab 100644
--- a/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c
+++ b/modules/database/src/ioc/dbtemplate/dbtoolsIocRegister.c
@@ -13,13 +13,22 @@
/* dbLoadTemplate */
-static const iocshArg dbLoadTemplateArg0 = {"filename", iocshArgString};
-static const iocshArg dbLoadTemplateArg1 = {"var=value", iocshArgString};
+static const iocshArg dbLoadTemplateArg0 = {"filename", iocshArgStringPath};
+static const iocshArg dbLoadTemplateArg1 = {"var1=value1,var2=value2", iocshArgString};
static const iocshArg * const dbLoadTemplateArgs[2] = {
&dbLoadTemplateArg0, &dbLoadTemplateArg1
};
-static const iocshFuncDef dbLoadTemplateFuncDef =
- {"dbLoadTemplate", 2, dbLoadTemplateArgs};
+static const iocshFuncDef dbLoadTemplateFuncDef = {
+ "dbLoadTemplate",
+ 2,
+ dbLoadTemplateArgs,
+ "Load the substitution file given as first argument, apply the substitutions\n"
+ "for each template in the substitution file, and load them using 'dbLoadRecords'.\n\n"
+ "The second argument provides extra variables to substitute in the\n"
+ "template files (not the substitution file).\n\n"
+ "See 'help dbLoadRecords' for more information.\n\n"
+ "Example: dbLoadTemplate db/my.substitutions 'user=myself,host=myhost'\n",
+};
static void dbLoadTemplateCallFunc(const iocshArgBuf *args)
{
iocshSetError(dbLoadTemplate(args[0].sval, args[1].sval));
diff --git a/modules/database/src/ioc/dbtemplate/msi.cpp b/modules/database/src/ioc/dbtemplate/msi.cpp
index 6445c22f1..d504e3ea4 100644
--- a/modules/database/src/ioc/dbtemplate/msi.cpp
+++ b/modules/database/src/ioc/dbtemplate/msi.cpp
@@ -317,7 +317,7 @@ static void makeSubstitutions(inputData * const inputPvt,
pstart = ++p;
/*Look for end quote*/
while (*p && (*p != '"')) {
- /*allow escape for embeded quote*/
+ /*allow escape for embedded quote*/
if ((p[0] == '\\') && p[1] == '"') {
p += 2;
continue;
@@ -1028,7 +1028,7 @@ static tokenType subGetNextToken(subFile *psubFile)
subFileErrPrint(psubFile, "Strings must be on single line\n");
abortExit(1);
}
- /*allow escape for embeded quote*/
+ /*allow escape for embedded quote*/
if ((p[0] == '\\') && p[1] == '"') {
*pto++ = *p++;
*pto++ = *p++;
diff --git a/modules/database/src/ioc/misc/dlload.c b/modules/database/src/ioc/misc/dlload.c
index a03685526..8b2a76371 100644
--- a/modules/database/src/ioc/misc/dlload.c
+++ b/modules/database/src/ioc/misc/dlload.c
@@ -17,9 +17,15 @@ IOCSH_STATIC_FUNC void dlload(const char* name)
}
}
-static const iocshArg dlloadArg0 = { "path/library.so", iocshArgString};
+static const iocshArg dlloadArg0 = { "path/library.so", iocshArgStringPath};
static const iocshArg * const dlloadArgs[] = {&dlloadArg0};
-static const iocshFuncDef dlloadFuncDef = {"dlload", 1, dlloadArgs};
+static const iocshFuncDef dlloadFuncDef = {
+ "dlload",
+ 1,
+ dlloadArgs,
+ "Load the given shared library.\n\n"
+ "Example: dlload myLibrary.so\n",
+};
static void dlloadCallFunc(const iocshArgBuf *args)
{
dlload(args[0].sval);
diff --git a/modules/database/src/ioc/misc/epicsRelease.c b/modules/database/src/ioc/misc/epicsRelease.c
index c1801179f..a1783fb8c 100644
--- a/modules/database/src/ioc/misc/epicsRelease.c
+++ b/modules/database/src/ioc/misc/epicsRelease.c
@@ -23,6 +23,7 @@ DBCORE_API int coreRelease(void)
printf ( "############################################################################\n" );
printf ( "## %s\n", epicsReleaseVersion );
printf ( "## %s\n", "Rev. " EPICS_VCS_VERSION );
+ printf ( "## %s\n", "Rev. Date " EPICS_VCS_VERSION_DATE );
printf ( "############################################################################\n" );
return 0;
}
diff --git a/modules/database/src/ioc/misc/iocshRegisterCommon.c b/modules/database/src/ioc/misc/iocshRegisterCommon.c
index eba0678fa..c23510800 100644
--- a/modules/database/src/ioc/misc/iocshRegisterCommon.c
+++ b/modules/database/src/ioc/misc/iocshRegisterCommon.c
@@ -28,8 +28,19 @@
/* registerAllRecordDeviceDrivers */
static const iocshArg rrddArg0 = {"pdbbase", iocshArgPdbbase};
static const iocshArg *rrddArgs[] = {&rrddArg0};
-static const iocshFuncDef rrddFuncDef =
- {"registerAllRecordDeviceDrivers", 1, rrddArgs};
+static const iocshFuncDef rrddFuncDef = {
+ "registerAllRecordDeviceDrivers",
+ 1,
+ rrddArgs,
+ "Register all records, devices, from all DBD available.\n\n"
+ "Calling this function is equivalent to calling every\n"
+ "'_registerRecordDeviceDriver' which has been linked\n"
+ "into the process, e.g. by dynamic loading, or by linking with\n"
+ "a generated '_registerRecordDeviceDriver.cpp' files.\n\n"
+ "These are registered into the database given as first argument,\n"
+ "which should always be 'pdbbase'.\n\n"
+ "Example: registerAllRecordDeviceDrivers pdbbase\n",
+};
static void rrddCallFunc(const iocshArgBuf *args)
{
iocshSetError(registerAllRecordDeviceDrivers(*iocshPpdbbase));
diff --git a/modules/database/src/ioc/registry/registryIocRegister.c b/modules/database/src/ioc/registry/registryIocRegister.c
index e45534fa7..250037ff2 100644
--- a/modules/database/src/ioc/registry/registryIocRegister.c
+++ b/modules/database/src/ioc/registry/registryIocRegister.c
@@ -21,28 +21,48 @@ static const iocshArg * const registryXxxFindArgs[1] = {®istryXxxFindArg0};
/* registryRecordTypeFind */
static const iocshFuncDef registryRecordTypeFindFuncDef = {
- "registryRecordTypeFind",1,registryXxxFindArgs};
+ "registryRecordTypeFind",
+ 1,
+ registryXxxFindArgs,
+ "Prints the registry address of the record type given as first argument.\n\n"
+ "Example: registryRecordTypeFind ai\n",
+};
static void registryRecordTypeFindCallFunc(const iocshArgBuf *args) {
printf("%p\n", (void*) registryRecordTypeFind(args[0].sval));
}
/* registryDeviceSupportFind */
static const iocshFuncDef registryDeviceSupportFindFuncDef = {
- "registryDeviceSupportFind",1,registryXxxFindArgs};
+ "registryDeviceSupportFind",
+ 1,
+ registryXxxFindArgs,
+ "Prints the registry address of the device support given as first argument.\n\n"
+ "Example: registryDeviceSupportFind devAaiSoft\n",
+};
static void registryDeviceSupportFindCallFunc(const iocshArgBuf *args) {
printf("%p\n", (void*) registryDeviceSupportFind(args[0].sval));
}
/* registryDriverSupportFind */
static const iocshFuncDef registryDriverSupportFindFuncDef = {
- "registryDriverSupportFind",1,registryXxxFindArgs};
+ "registryDriverSupportFind",
+ 1,
+ registryXxxFindArgs,
+ "Prints the registry address of the driver support given as first argument.\n\n"
+ "Example: registryDriverSupportFind stream\n",
+};
static void registryDriverSupportFindCallFunc(const iocshArgBuf *args) {
printf("%p\n", (void*) registryDriverSupportFind(args[0].sval));
}
/* registryFunctionFind */
static const iocshFuncDef registryFunctionFindFuncDef = {
- "registryFunctionFind",1,registryXxxFindArgs};
+ "registryFunctionFind",
+ 1,
+ registryXxxFindArgs,
+ "Prints the registry address of the registered function given as first argument.\n\n"
+ "Example: registryFunctionFind registryFunctionFind\n",
+};
static void registryFunctionFindCallFunc(const iocshArgBuf *args) {
printf("%p\n", (void*) registryFunctionFind(args[0].sval));
}
diff --git a/modules/database/src/ioc/rsrv/camessage.c b/modules/database/src/ioc/rsrv/camessage.c
index cd5f266c3..56fbde088 100644
--- a/modules/database/src/ioc/rsrv/camessage.c
+++ b/modules/database/src/ioc/rsrv/camessage.c
@@ -97,7 +97,7 @@ typedef struct rsrv_put_notify {
/*
* casCalloc()
*
- * (dont drop below some max block threshold)
+ * (don't drop below some max block threshold)
*/
static void *casCalloc(size_t count, size_t size)
{
@@ -386,7 +386,7 @@ static int bad_tcp_cmd_action ( caHdrLargeArray *mp, void *pPayload,
log_header ( pCtx, client, mp, pPayload, 0 );
/*
- * by default, clients dont recover
+ * by default, clients don't recover
* from this
*/
SEND_LOCK (client);
@@ -550,7 +550,7 @@ static void read_reply ( void *pArg, struct dbChannel *dbch,
cid = ECA_NORMAL;
/* If the client has requested a zero element count we interpret this as a
- * request for all avaiable elements. In this case we initialise the
+ * request for all available elements. In this case we initialize the
* header with the maximum element size specified by the database. */
autosize = pevext->msg.m_count == 0;
item_count =
@@ -917,7 +917,7 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload,
}
/*
- * user name will not change if there isnt enough memory
+ * user name will not change if there isn't enough memory
*/
pMalloc = malloc(size);
if(!pMalloc){
@@ -996,7 +996,7 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload,
}
/*
- * user name will not change if there isnt enough memory
+ * user name will not change if there isn't enough memory
*/
pMalloc = malloc(size);
if(!pMalloc){
@@ -1063,7 +1063,7 @@ unsigned cid
* NOTE: This detects the case where the PV id wraps
* around and we attempt to have two resources on the same id.
* The lock is applied here because on some architectures the
- * ++ operator isnt atomic.
+ * ++ operator isn't atomic.
*/
LOCK_CLIENTQ;
@@ -1293,8 +1293,8 @@ static int claim_ciu_action ( caHdrLargeArray *mp,
return RSRV_OK;
}
- DLOG ( 2, ("CAS: claim_ciu_action found '%s', type %d, count %d\n",
- pName, dbChannelCAType(dbch), dbChannelElements(dbch)) );
+ DLOG ( 2, ("CAS: claim_ciu_action found '%s', type %d, count %ld\n",
+ pName, dbChannelExportCAType(dbch), dbChannelElements(dbch)) );
pciu = casCreateChannel (
client,
@@ -1484,7 +1484,7 @@ static void write_notify_reply ( struct client * pClient )
msgtmp.m_available, 0 );
if ( localStatus != ECA_NORMAL ) {
/*
- * inability to aquire buffer space
+ * inability to acquire buffer space
* Indicates corruption
*/
errlogPrintf("CA server corrupted - put call back(s) discarded\n");
@@ -1618,7 +1618,7 @@ static int rsrvExpandPutNotify (
if ( sizeNeeded > pNotify->valueSize ) {
/*
- * try to use the union embeded in the free list
+ * try to use the union embedded in the free list
* item, but allocate a random sized block if they
* writing a vector.
*/
@@ -1673,7 +1673,7 @@ void rsrvFreePutNotify ( client *pClient,
epicsMutexUnlock ( pClient->putNotifyLock );
/*
- * if any possiblity that the put notify is
+ * if any possibility that the put notify is
* outstanding then cancel it
*/
if ( busyTmp ) {
@@ -1784,7 +1784,7 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload,
if ( ! pciu->pPutNotify ) {
/*
* send error and go to next request
- * if there isnt enough memory left
+ * if there isn't enough memory left
*/
log_header ( "no memory to initiate put notify",
client, mp, pPayload, 0 );
@@ -1922,7 +1922,7 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client
* messages sent by the server).
*/
- DLOG ( 3, ("event_add_action: db_post_single_event (0x%X)\n",
+ DLOG ( 3, ("event_add_action: db_post_single_event (%p)\n",
pevext->pdbev) );
db_post_single_event(pevext->pdbev);
@@ -2194,7 +2194,7 @@ int rsrv_version_reply ( struct client *client )
SEND_LOCK ( client );
/*
* sequence number is specified zero when we copy in the
- * header because we dont know it until we receive a datagram
+ * header because we don't know it until we receive a datagram
* from the client
*/
status = cas_copy_in_header ( client, CA_PROTO_VERSION,
@@ -2244,7 +2244,7 @@ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client
/* Exit quickly if channel not on this node */
if (dbChannelTest(pName)) {
- DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) );
+ DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pName ) );
return RSRV_OK;
}
@@ -2263,7 +2263,7 @@ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client
* starting with V4.4 the count field is used (abused)
* to store the minor version number of the client.
*
- * New versions dont alloc the channel in response
+ * New versions don't alloc the channel in response
* to a search request.
* For these, allocation has been moved to claim_ciu_action().
*
@@ -2334,14 +2334,14 @@ static int search_reply_tcp (
/* Exit quickly if channel not on this node */
if (dbChannelTest(pName)) {
- DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) );
+ DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pName ) );
if (mp->m_dataType == DOREPLY)
search_fail_reply ( mp, pPayload, client );
return RSRV_OK;
}
/*
- * stop further use of server if memory becomes scarse
+ * stop further use of server if memory becomes scarce
*/
spaceAvailOnFreeList = freeListItemsAvail ( rsrvChanFreeList ) > 0
&& freeListItemsAvail ( rsrvEventFreeList ) > reasonableMonitorSpace;
@@ -2453,7 +2453,7 @@ int camessage ( struct client *client )
assert(pCaBucket);
- /* drain remnents of large messages that will not fit */
+ /* drain remnants of large messages that will not fit */
if ( client->recvBytesToDrain ) {
if ( client->recvBytesToDrain >= client->recv.cnt ) {
client->recvBytesToDrain -= client->recv.cnt;
@@ -2466,7 +2466,7 @@ int camessage ( struct client *client )
}
}
- DLOG ( 2, ( "CAS: Parsing %d(decimal) bytes\n", recv->cnt ) );
+ DLOG ( 2, ( "CAS: Parsing %d(decimal) bytes\n", client->recv.cnt ) );
while ( 1 )
{
@@ -2530,7 +2530,7 @@ int camessage ( struct client *client )
#endif
/*
- * disconnect clients that dont send 8 byte
+ * disconnect clients that don't send 8 byte
* aligned payloads
*/
if ( msgsize & 0x7 ) {
diff --git a/modules/database/src/ioc/rsrv/camsgtask.c b/modules/database/src/ioc/rsrv/camsgtask.c
index bcd30d1bd..24df11d39 100644
--- a/modules/database/src/ioc/rsrv/camsgtask.c
+++ b/modules/database/src/ioc/rsrv/camsgtask.c
@@ -50,7 +50,7 @@ void camsgtask ( void *pParm )
int status;
/*
- * allow message to batch up if more are comming
+ * allow message to batch up if more are coming
*/
status = socket_ioctl (client->sock, FIONREAD, &check_nchars);
if (status < 0) {
@@ -58,7 +58,7 @@ void camsgtask ( void *pParm )
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- errlogPrintf("CAS: FIONREAD error: %s\n",
+ errlogPrintf("CAS: FIONREAD " ERL_ERROR ": %s\n",
sockErrBuf);
cas_send_bs_msg(client, TRUE);
}
diff --git a/modules/database/src/ioc/rsrv/caserverio.c b/modules/database/src/ioc/rsrv/caserverio.c
index 9703197a0..4e953ef85 100644
--- a/modules/database/src/ioc/rsrv/caserverio.c
+++ b/modules/database/src/ioc/rsrv/caserverio.c
@@ -139,7 +139,7 @@ void cas_send_bs_msg ( struct client *pclient, int lock_needed )
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- errlogPrintf ("CAS: Socket shutdown error: %s\n",
+ errlogPrintf ("CAS: Socket shutdown " ERL_ERROR ": %s\n",
sockErrBuf );
}
}
diff --git a/modules/database/src/ioc/rsrv/caservertask.c b/modules/database/src/ioc/rsrv/caservertask.c
index 4b37ab7e9..8e347deb8 100644
--- a/modules/database/src/ioc/rsrv/caservertask.c
+++ b/modules/database/src/ioc/rsrv/caservertask.c
@@ -86,7 +86,7 @@ static void req_server (void *pParm)
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- errlogPrintf("CAS: Client accept error: %s (%d)\n",
+ errlogPrintf("CAS: Client accept " ERL_ERROR ": %s (%d)\n",
sockErrBuf, (int)addLen );
epicsThreadSleep(15.0);
continue;
@@ -131,7 +131,7 @@ int tryBind(SOCKET sock, const osiSockAddr* addr, const char *name)
{
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- errlogPrintf ( "CAS: %s bind error: %s\n",
+ errlogPrintf ( "CAS: %s bind " ERL_ERROR ": %s\n",
name, sockErrBuf );
epicsThreadSuspendSelf ();
}
@@ -196,7 +196,7 @@ SOCKET* rsrv_grab_tcp(unsigned short *port)
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
- errlogPrintf ( "CAS: getsockname error: %s\n",
+ errlogPrintf ( "CAS: getsockname " ERL_ERROR ": %s\n",
sockErrBuf );
epicsThreadSuspendSelf ();
ok = 0;
@@ -545,7 +545,7 @@ void rsrv_init (void)
beacon_startStopEvent = epicsEventMustCreate(epicsEventEmpty);
castcp_ctl = ctlPause;
- /* Thread priorites
+ /* Thread priorities
* Now starting per interface
* TCP Listener: epicsThreadPriorityCAServerLow-2
* Name receiver: epicsThreadPriorityCAServerLow-4
@@ -572,17 +572,22 @@ void rsrv_init (void)
{
unsigned short sport = ca_server_port;
+ char buf[6]; /* space for 0 - 65535 */
socks = rsrv_grab_tcp(&sport);
if ( sport != ca_server_port ) {
ca_server_port = sport;
- errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n");
- errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n",
+ errlogPrintf ( "cas " ERL_WARNING ": Configured TCP port was unavailable.\n");
+ errlogPrintf ( "cas " ERL_WARNING ": Using dynamically assigned TCP port %hu,\n",
ca_server_port );
- errlogPrintf ( "cas warning: but now two or more servers share the same UDP port.\n");
- errlogPrintf ( "cas warning: Depending on your IP kernel this server may not be\n" );
- errlogPrintf ( "cas warning: reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST)\n" );
+ errlogPrintf ( "cas " ERL_WARNING ": but now two or more servers share the same UDP port.\n");
+ errlogPrintf ( "cas " ERL_WARNING ": Depending on your IP kernel this server may not be\n" );
+ errlogPrintf ( "cas " ERL_WARNING ": reachable with UDP unicast (a host's IP in EPICS_CA_ADDR_LIST)\n" );
}
+
+ epicsSnprintf(buf, sizeof(buf)-1u, "%u", ca_server_port);
+ buf[sizeof(buf)-1u] = '\0';
+ epicsEnvSet("RSRV_SERVER_PORT", buf);
}
/* start servers (TCP and UDP(s) for each interface.
@@ -1204,7 +1209,7 @@ void destroy_tcp_client ( struct client *client )
assert ( ! status );
/*
- * wait for extra labor in progress to comple
+ * wait for extra labor in progress to complete
*/
db_flush_extra_labor_event ( client->evuser );
}
@@ -1229,7 +1234,7 @@ struct client * create_client ( SOCKET sock, int proto )
size_t spaceNeeded;
/*
- * stop further use of server if memory becomes scarse
+ * stop further use of server if memory becomes scarce
*/
spaceAvailOnFreeList = freeListItemsAvail ( rsrvClientFreeList ) > 0
&& freeListItemsAvail ( rsrvSmallBufFreeListTCP ) > 0;
diff --git a/modules/database/src/ioc/rsrv/cast_server.c b/modules/database/src/ioc/rsrv/cast_server.c
index 0668c63a9..a18c41015 100644
--- a/modules/database/src/ioc/rsrv/cast_server.c
+++ b/modules/database/src/ioc/rsrv/cast_server.c
@@ -16,7 +16,7 @@
* Improvements
* ------------
* .01
- * Dont send channel found message unless there is memory, a task slot,
+ * Don't send channel found message unless there is memory, a task slot,
* and a TCP socket available. Send a diagnostic instead.
* Or ... make the timeout shorter? This is only a problem if
* they persist in trying to make a connection after getting no
@@ -269,9 +269,9 @@ void cast_server(void *pParm)
}
/*
- * allow messages to batch up if more are comming
+ * allow messages to batch up if more are coming
*/
- nchars = 0; /* supress purify warning */
+ nchars = 0; /* suppress purify warning */
status = socket_ioctl(recv_sock, FIONREAD, &nchars);
if (status<0) {
errlogPrintf ("CA cast server: Unable to fetch N characters pending\n");
diff --git a/modules/database/src/ioc/rsrv/online_notify.c b/modules/database/src/ioc/rsrv/online_notify.c
index 7f75bde37..e192e7461 100644
--- a/modules/database/src/ioc/rsrv/online_notify.c
+++ b/modules/database/src/ioc/rsrv/online_notify.c
@@ -94,7 +94,7 @@ void rsrv_online_notify_task(void *pParm)
epicsSocketConvertErrorToString(sockErrBuf, sizeof(sockErrBuf), err);
ipAddrToDottedIP(&pAddr->addr.ia, sockDipBuf, sizeof(sockDipBuf));
- errlogPrintf ( "CAS: CA beacon send to %s error: %s\n",
+ errlogPrintf ( "CAS: CA beacon send to %s " ERL_ERROR ": %s\n",
sockDipBuf, sockErrBuf);
lastError[i] = err;
diff --git a/modules/database/src/ioc/rsrv/server.h b/modules/database/src/ioc/rsrv/server.h
index c0593739e..604880c12 100644
--- a/modules/database/src/ioc/rsrv/server.h
+++ b/modules/database/src/ioc/rsrv/server.h
@@ -239,7 +239,7 @@ void initializePutNotifyFreeList (void);
unsigned rsrvSizeOfPutNotify ( struct rsrv_put_notify *pNotify );
/*
- * inclming protocol maintetnance
+ * incoming protocol maintenance
*/
void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size );
diff --git a/modules/database/src/std/dev/devBoSoft.c b/modules/database/src/std/dev/devBoSoft.c
index 59c402367..c20e5d2cd 100644
--- a/modules/database/src/std/dev/devBoSoft.c
+++ b/modules/database/src/std/dev/devBoSoft.c
@@ -43,7 +43,7 @@ static long init_record(dbCommon *pcommon)
{
long status=0;
- /* dont convert */
+ /* don't convert */
status=2;
return status;
diff --git a/modules/database/src/std/filters/arr.c b/modules/database/src/std/filters/arr.c
index ffe3fce8f..a579f7782 100644
--- a/modules/database/src/std/filters/arr.c
+++ b/modules/database/src/std/filters/arr.c
@@ -109,7 +109,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
break;
case dbfl_type_ref:
- must_lock = !pfl->u.r.dtor;
+ must_lock = !pfl->dtor;
if (must_lock) {
dbScanLock(dbChannelRecord(chan));
dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset);
@@ -123,9 +123,9 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl)
offset = (offset + start) % pfl->no_elements;
dbExtractArray(pSource, pTarget, pfl->field_size,
nTarget, pfl->no_elements, offset, my->incr);
- if (pfl->u.r.dtor) pfl->u.r.dtor(pfl);
+ if (pfl->dtor) pfl->dtor(pfl);
pfl->u.r.field = pTarget;
- pfl->u.r.dtor = freeArray;
+ pfl->dtor = freeArray;
pfl->u.r.pvt = my->arrayFreeList;
}
/* adjust no_elements (even if zero elements remain) */
diff --git a/modules/database/src/std/filters/dbnd.c b/modules/database/src/std/filters/dbnd.c
index 2a94f146e..fdbc612f8 100644
--- a/modules/database/src/std/filters/dbnd.c
+++ b/modules/database/src/std/filters/dbnd.c
@@ -20,7 +20,7 @@
#include
#include
#include
-#include
+#include
#include
typedef struct myStruct {
@@ -81,8 +81,8 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
status = dbFastGetConvertRoutine[pfl->field_type][DBR_DOUBLE]
(localAddr.pfield, (void*) &val, &localAddr);
if (!status) {
- send = 0;
- recGblCheckDeadband(&my->last, val, my->hyst, &send, 1);
+ send = pfl->mask & ~(DBE_VALUE|DBE_LOG);
+ recGblCheckDeadband(&my->last, val, my->hyst, &send, pfl->mask & (DBE_VALUE|DBE_LOG));
if (send && my->mode == 1) {
my->hyst = val * my->cval/100.;
}
diff --git a/modules/database/src/std/filters/decimate.c b/modules/database/src/std/filters/decimate.c
index e9d31bf84..7ce12cb66 100644
--- a/modules/database/src/std/filters/decimate.c
+++ b/modules/database/src/std/filters/decimate.c
@@ -19,6 +19,7 @@
#include "freeList.h"
#include "db_field_log.h"
#include "chfPlugin.h"
+#include "epicsExit.h"
#include "epicsExport.h"
typedef struct myStruct {
@@ -102,17 +103,20 @@ static chfPluginIf pif = {
NULL /* channel_close */
};
+static void decShutdown(void *ignore)
+{
+ if (myStructFreeList)
+ freeListCleanup(myStructFreeList);
+ myStructFreeList = NULL;
+}
+
static void decInitialize(void)
{
- static int firstTime = 1;
-
- if (!firstTime) return;
- firstTime = 0;
-
if (!myStructFreeList)
freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64);
chfPluginRegister("dec", &pif, opts);
+ epicsAtExit(decShutdown, NULL);
}
epicsExportRegistrar(decInitialize);
diff --git a/modules/database/src/std/filters/filters.dbd.pod b/modules/database/src/std/filters/filters.dbd.pod
index 7b3146609..7f72ebe6c 100644
--- a/modules/database/src/std/filters/filters.dbd.pod
+++ b/modules/database/src/std/filters/filters.dbd.pod
@@ -1,59 +1,214 @@
-=head1 Channel Filters
+=title Field Modifiers and Channel Filters
-Channel Filters can be applied to Channel Access channels by a client, using
-a JSON Field Modifier to select the filter and any parameters.
-The following filters are available in this release:
+=head2 Contents
=over
-=item * L
+=item * L
-=item * L
+=item * L
-=item * L
+=item * L
-=item * L
+=item * L
-=item * L
+=over
-=item * L
+=item * L
+ |/"Long String Field Modifier $">
+
+=item * L] >>>
+ |/"Subarray Field Modifier [start:increment:end]">
=back
-=head2 Using Filters
+=item * L
-Channel filters can be added to any Channel Access channel name.
-There can be more than one filter applied to the same channel, in which case the
-order that they are specified will control the order in which they are applied
-to the resulting data-stream.
-The filter specification must appear after the field name, or if the default
-(VAL) field is used after a dot C<.> appended to the record name.
-With the exception of the array short-hand which is described below, all filters
-must appear inside a pair of braces C< {} > after the dot expressed as a JSON
-(L ) object, which allows filter
-parameters to be included as needed.
+=over
-Each filter is given as a name/value pair. The filter name (given in parentheses
-in the titles below) is a string, and must be enclosed inside double-quotes C<">
-characters as per the JSON specification.
-Parameters to that filter are provided as the value part of the name/value pair,
-and will normally appear as a child JSON object consisting of name/value pairs
-inside a nested pair of braces C< {} >.
+=item * L>>
+ |/"TimeStamp Filter ts">
-=head4 Example Filter
+=item * L}} >>>
+ |/"Deadband Filter dbnd">
-Given a record called C the following would apply a filter C to
-the VAL field of that record, giving the filter two numeric parameters named
-C and C:
+=item * L}} >>>
+ |/"Array Filter arr">
- test:channel.{"f":{"lo":0,"hi":10}}
+=item * L}} >>>
+ |/"Synchronize Filter sync">
-Note that due to the required presence of the double-quote characters in the
-JSON strings in the name string, it will usually be necessary to enclose a
-filtered name within single-quotes C<< ' ... ' >> when typing it as an
-argument to a Unix shell command.
+=item * L}} >>>
+ |/"Decimation Filter dec">
-=head2 Filter Reference
+=item * L}} >>>
+ |/"User Tag Filter utag">
+
+=back
+
+=back
+
+
+=head2 Introduction
+
+A Field Modifier is a string that is appended to the field name part of a
+Channel Access or PV Access channel name of an IOC-based server.
+The IOC currently recognizes 3 different kinds of field modifier, which are
+described below.
+
+A Channel Filter is an IOC plugin that can be attached to an IOC process
+variable channel using a field modifier, and can alter the data updates that are
+served to the client that requested the filtering.
+Clients that use a channel filter have no effect on other clients connected to
+the same PV.
+Filters can only modify or drop monitor events that the IOC posts; introducing
+extra monitor events (in between the events posted by the IOC itself) is not
+currently possible.
+
+Most Channel Filters are configured by a field modifier that uses JSON5
+syntax to select the type of filter and provide any parameters it accepts.
+
+
+=head2 Using Field Modifiers and Channel Filters
+
+Modifiers can be added to any Channel Access or PV Access channel name.
+There can be more than one modifier or filter applied to a channel.
+Modifiers must appear immediately after the field name if one is included in the
+channel name.
+If no field is named because the default (VAL) field is the target, the
+modifiers must come immediately after a dot C<.> appended to the record name.
+
+The order that modifiers and filters are specified controls the order in which
+they are applied to the resulting data-stream.
+If used, the Long String and Subarray field modifiers must appear first and in
+that order, followed by any channel filters inside a pair of braces C< {} > that
+form a JSON5 (L ) map, which
+allows filters and their parameters to be specified as needed.
+
+Each JSON5 filter is specified as a name:value pair.
+The filter name is a map key that may be an unquoted identifier name (which all
+of the filter names given below are), or a string enclosed inside either single-
+or double-quote characters C<'> or C<"> as per the JSON5 specification.
+Parameters to the filter are provided in the value part of the key:value pair
+after the colon C<:> character, and will normally be an embedded JSON5 map
+containing zero or more key/value pairs inside a nested pair of braces C< {} >.
+
+Unless included inside a quoted string, white space characters are ignored and
+skipped over by the JSON5 parser between other token characters.
+This includes horizontal and vertical tabs, line feed/form feed/carriage return,
+space and the non-breaking space character C< \xA0 >.
+Within a quoted string, line breaks may escaped with a backslash C<\> to be
+omitted from the parsed string.
+
+An IOC Channel Access link can include filters in its channel name, but it is
+important to not include any spaces at all in the filter specification.
+If a filter name or parameter must contain a space it will be necessary to
+express that space character as an escaped character C<\x20> or C<\u0020> inside
+a quoted string, otherwise the space will mark the end of the channel name to
+the link parsing code inside the IOC.
+
+=head4 Example Filters
+
+Given a record called C the following would all apply a channel
+filter C to the VAL field of that record, giving the filter two numeric
+parameters named C and C:
+
+ test:channel.{f:{lo:0,hi:10}}
+ test:channel.{"f":{"lo":0, "hi":10}}
+ test:channel.{'f': {'lo':0, 'hi':10} }
+
+When typing a filtered channel name as an argument to a Unix shell command, if
+quote characters are used for keys or values in JSON strings within the channel
+name string it may be necessary to enclose the name within quotes C<'> or C<">
+or to use back-slash escapes before them.
+Quotes may not be required when the Long String modifier C<$> is used at the end
+of a field name with nothing following it, but will be necessary for a square
+bracketted Subarray filter or if a dollar sign is followed by something else.
+
+ Hal$ caget test:channel.{f:{lo:0,hi:10}}
+ ...
+ Hal$ caget 'test:channel.{"f":{"lo":0, "hi":10}}'
+ ...
+ Hal$ caget -S calc:record.CALC$
+ ...
+ Hal$ caget -S 'test:channel.NAME$[0:4]'
+ test:channel.NAME$[0:4] test
+
+=head2 Field Modifier Reference
+
+The two built-in field modifiers use a simplified syntax following the record
+field name.
+
+=head3 Long String Field Modifier C<$>
+
+Appending a dollar sign C<$> to the name of a C field causes the IOC
+to change the representation of that field into an array of characters, which
+allows strings longer than 40 character to be transported through Channel
+Access.
+Long strings are particularly useful for the CALC fields of a calculation or
+calcout record, which can hold up to 80 characters, or the VAL fields of an lsi
+(Long String Input) or lso (Long String Output) record which can be any length
+as chosen by the database designer.
+
+ Hal$ cainfo test:channel.NAME
+ test:channel.NAME
+ State: connected
+ Host: 10.234.56.78:5064
+ Access: read, no write
+ Native data type: DBF_STRING
+ Request type: DBR_STRING
+ Element count: 1
+ Hal$ cainfo test:channel.NAME$
+ test:channel.NAME$
+ State: connected
+ Host: 10.234.56.78:5064
+ Access: read, no write
+ Native data type: DBF_CHAR
+ Request type: DBR_CHAR
+ Element count: 61
+
+A CA client accessing a channel that uses the Long String field modifier will
+have to be specifically configured to treat the data as a string instead of the
+array of C that it looks like.
+CA clients should not attempt to parse the channel name themselves to recognize
+this field modifier in the name.
+All long string values returned by the IOC should include a trailing zero byte
+in their data as is standard for strings in the C language.
+For the catools programs provided with Base, the flag C<-S> indicates that a
+channel containing a character array should be treated as a long string.
+
+ Hal$ caget test:channel.NAME
+ test:channel.NAME test:channel
+ Hal$ caget test:channel.NAME$
+ test:channel.NAME$ 61 116 101 115 116 58 99 104 97 110 110 101 108 0 0 ...
+ Hal$ caget -S test:channel.NAME$
+ test:channel.NAME$ test:channel
+
+
+=head3 Subarray Field Modifier C<[start:increment:end]>
+
+This square-bracket field modifier syntax gets translated within the IOC into
+calls to the L, see that section below for
+details of this shorthand.
+
+The subarray field modifier syntax can immediately follow a Long String field
+modifier, which permits fetching various kinds of substrings from the field.
+This syntax cannot appear after a JSON filter specification though, the JSON
+"arr" filter syntax must be used to apply an array filter after any other JSON
+filter type.
+
+ Hal$ caget -S 'test:channel.NAME$[0:4]'
+ test:channel.NAME$[0:4] test
+ Hal$ caget -S 'test:channel.NAME$[5:-1]'
+ test:channel.NAME$[5:-1] channel
+
+
+=head2 JSON5 Channel Filters
=cut
@@ -61,11 +216,11 @@ registrar(tsInitialize)
=head3 TimeStamp Filter C<"ts">
-This filter is used to set the timestamp of the value fetched through
-the channel to the time the value was fetched (or an update was sent),
-rather than the time the record last
-processed which could have been days or even weeks ago for some records, or set
-to the EPICS epoch if the record has never processed.
+This filter replaces the timestamp in the data fetched through the channel with
+the time the value was fetched (or an update was sent). The record's timestamp
+indicates when the record last processed, which could have been days or even
+weeks ago for some records, or set to the EPICS epoch if the record has never
+processed.
=head4 Parameters
@@ -125,7 +280,7 @@ The default mode is C if no mode parameter is included.
test:channel 2012-09-01 22:10:23.601023 5
test:channel 2012-09-01 22:10:24.601136 6 HIGH MINOR
^C
- Hal$ camonitor 'test:channel.{"dbnd":{"abs":1.5}}'
+ Hal$ camonitor 'test:channel.{"dbnd":{"d":1.5}}'
test:channel.{"dbnd":{"d":1.5}} 2012-09-01 22:11:49.613341 1 LOLO MAJOR
test:channel.{"dbnd":{"d":1.5}} 2012-09-01 22:11:51.613615 3 LOW MINOR
test:channel.{"dbnd":{"d":1.5}} 2012-09-01 22:11:53.613804 5
@@ -144,16 +299,21 @@ subarrays).
=head4 Parameters
-Note: Negative index numbers address from the end of the array, with C<-1> being the last element.
-
=over
=item Square bracket notation C<[start:increment:end]> (shorthand)
-The common square bracket notation which can be used in place of JSON.
+This much shorter square bracket notation can be used in place of JSON.
Any parameter may be omitted (keeping the colons) to use the default value.
-If only one colon is included, this means C<[start:end]> with an increment of 1.
-If only a single parameter is used C<[index]> the filter returns one element.
+If only one colon is included, it means C<[start:end]> with an increment of 1.
+If only a single parameter is given C<[index]> the filter returns one element.
+
+Index numbers for the start and end parameters must be integers, with the first
+array element being found at index C<0>.
+The value of an index may be negative, in which case the indexing is counted
+backwards from the end of the array, with C<-1> being the last element.
+If the start index selects an element that comes after the end index element,
+the subarray returned will always be empty.
=item Start index C<"s">
@@ -161,8 +321,8 @@ Index of the first original array element to retrieve.
=item Increment C<"i">
-Index increment between retrieved elements of the original array; must be
-a positive number.
+The stride or increment to apply between elements of the original array to be
+retrieved. This value must be a positive integer.
=item End index C<"e">
@@ -176,9 +336,9 @@ C (first element), C (fetch all elements), C
=head4 Example
- Hal$ caget test:channel 'test:channel.{"arr":{"s":2,"i":2,"e":8}}' test:channel.[3:5] test:channel.[3:2:-3]
+ Hal$ caget test:channel 'test:channel.{"arr":{s:2,i:2,e:8}}' test:channel.[3:5] test:channel.[3:2:-3]
test:channel 10 0 1 2 3 4 5 6 7 8 9
- test:channel.{"arr":{"s":2,"i":2,"e":8}} 4 2 4 6 8
+ test:channel.{"arr":{s:2,i:2,e:8}} 4 2 4 6 8
test:channel.[3:5] 3 3 4 5
test:channel.[3:2:-3] 3 3 5 7
@@ -202,13 +362,14 @@ C.
=item Mode+State
Mode and state can be specified in one definition (shorthand).
-The desired mode is given as parameter name (C<"before"> / C<"first"> /
-C<"while"> / C<"last"> / C<"after"> / C<"unless">), with the state name
-(enclosed in double quotes C<">) as value.
+The desired mode is given as the parameter name (C<"before"> / C<"first"> /
+C<"while"> / C<"last"> / C<"after"> / C<"unless"> which may be unquoted), with
+the state name (enclosed in single or double quotes C<"> or C<'>) as the value.
=item Mode C<"m">
-A single word from the list below, enclosed in double quotes C<">.
+A single word from the list below, enclosed in single or double quotes C<'> or
+C<">.
This controls how the state value should affect the monitor stream.
=over
@@ -235,7 +396,7 @@ as the state is false.
=item State C<"s">
-The name of a state variable, enclosed in double quotes C<">.
+The name of a state variable, enclosed in single or double quotes C<"> or C<'>.
=back
@@ -245,7 +406,7 @@ Assuming there is a system state called "blue", that is being controlled by
some other facility such as a timing system, updates could be restricted to
periods only when "blue" is true by using
- Hal$ camonitor 'test:channel' 'test:channel.{"sync":{"while":"blue"}}'
+ Hal$ camonitor 'test:channel' 'test:channel.{sync:{while:"blue"}}'
...
=cut
@@ -283,17 +444,18 @@ client connects.
To sample a 60Hz channel at 1Hz, a 10Hz channel every 6 seconds or a 1Hz channel
once every minute:
- Hal$ camonitor 'test:channel' 'test:channel.{"dec":{"n":60}}'
+ Hal$ camonitor 'test:channel' 'test:channel.{dec:{n:60}}'
...
=cut
registrar(utagInitialize)
-=head3 UTag Filter C<"utag">
+=head3 User Tag Filter C<"utag">
-This filter applies a test UTAG&M==V to the value taken from the UTAG record field
-and drops those updates which evaluate as false.
+This filter applies a test C< (UTAG & M) == V > to the value of the record's
+UTAG field at the time each monitor event is posted, and drops all updates for
+which this expression is false.
=head4 Parameters
@@ -301,12 +463,19 @@ and drops those updates which evaluate as false.
=item Mask C<"M">
-Bit mask.
+An integer to be used as a bit mask.
=item Value C<"V">
-Required value.
+The integer value to be matched after applying the mask to the UTAG value.
=back
+=head4 Example
+
+To read a channel only when the UTAG value is even (bit 0 is 0):
+
+ Hal$ camonitor 'test:channel' 'test:channel.{utag:{m:1, v:0}}'
+ ...
+
=cut
diff --git a/modules/database/src/std/filters/ts.c b/modules/database/src/std/filters/ts.c
index 0feadb0f5..0a0a4d3ba 100644
--- a/modules/database/src/std/filters/ts.c
+++ b/modules/database/src/std/filters/ts.c
@@ -35,7 +35,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
/* If reference and not already copied,
must make a copy (to ensure coherence between time and data) */
- if (pfl->type == dbfl_type_ref && !pfl->u.r.dtor) {
+ if (pfl->type == dbfl_type_ref && !pfl->dtor) {
void *pTarget = calloc(pfl->no_elements, pfl->field_size);
void *pSource = pfl->u.r.field;
if (pTarget) {
@@ -46,7 +46,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
dbExtractArray(pSource, pTarget, pfl->field_size,
nSource, pfl->no_elements, offset, 1);
pfl->u.r.field = pTarget;
- pfl->u.r.dtor = freeArray;
+ pfl->dtor = freeArray;
pfl->u.r.pvt = pvt;
dbScanUnlock(dbChannelRecord(chan));
}
diff --git a/modules/database/src/std/link/links.dbd.pod b/modules/database/src/std/link/links.dbd.pod
index a4744ff93..5247f6950 100644
--- a/modules/database/src/std/link/links.dbd.pod
+++ b/modules/database/src/std/link/links.dbd.pod
@@ -1,4 +1,4 @@
-=head1 Extensible Links
+=title Extensible IOC Database Links
The extensible link mechanism allows new kinds of record links to be created,
using JSON for the link address syntax.
@@ -24,13 +24,11 @@ The following additional link types are available in this release:
=head2 Using JSON Links
When setting a record link field to a JSON link address, the link specification
-must appear inside a pair of braces C< {} > expressed as a JSON (L ) object, which allows link parameters to
-be defined as needed by the particular link type. When link fields are set from
-an IOC database file at initialization time, the field definitions may take
-advantage of a "relaxed JSON" syntax that reduces the number of double-quote
-characters required and maintains backwards compatibility with the older
-database file syntax.
+must appear inside a pair of braces C< {} > expressed as a JSON5 (L ) object, which allows link parameters
+to be defined as needed by the particular link type. When link fields are set in
+an IOC database file, the field value may take advantage of the JSON5 syntax to
+reduce the number of double-quote characters required.
=head2 Link Type Reference
@@ -68,10 +66,10 @@ results in an error.
{const: "Pi"}
{const: [1, 2.718281828459, 3.14159265358979]}
{const: ["One", "e", "Pi"]}
+ {const:[Inf, -Inf]})
-The JSON syntax does not support Infinity or NaN values when parsing numbers,
-but (for scalars) it is possible to provide these in a string which will be
-converted to the desired double value at initialization, for example:
+The newer JSON5 syntax supports Infinity values when parsing numbers, so it is
+no longer necessary to quote these in a string, although that still works:
field(INP, {const:"Inf"})
diff --git a/modules/database/src/std/rec/aSubRecord.c b/modules/database/src/std/rec/aSubRecord.c
index 5d380d725..c6ba13038 100644
--- a/modules/database/src/std/rec/aSubRecord.c
+++ b/modules/database/src/std/rec/aSubRecord.c
@@ -275,8 +275,11 @@ static long fetch_values(aSubRecord *prec)
/* Get the input link values */
for (i = 0; i < NUM_ARGS; i++) {
+ DBLINK *plink = &(&prec->inpa)[i];
long nRequest = (&prec->noa)[i];
- status = dbGetLink(&(&prec->inpa)[i], (&prec->fta)[i], (&prec->a)[i], 0,
+ if(dbLinkIsConstant(plink))
+ continue;
+ status = dbGetLink(plink, (&prec->fta)[i], (&prec->a)[i], 0,
&nRequest);
if (status)
return status;
diff --git a/modules/database/src/std/rec/aSubRecord.dbd.pod b/modules/database/src/std/rec/aSubRecord.dbd.pod
index 18375534b..b03073f2c 100644
--- a/modules/database/src/std/rec/aSubRecord.dbd.pod
+++ b/modules/database/src/std/rec/aSubRecord.dbd.pod
@@ -736,6 +736,10 @@ The choices can be found by following the link to the menuFtype definition.
These fields specify how many array elements the input value fields may hold.
+Note that access to the C field from C code must use the field name in
+upper case, e.g. C<< prec->NOT >> since the lower-case C is a reserved
+word in C++ and cannot be used as an identifier.
+
=fields NOA, NOB, NOC, NOD, NOE, NOF, NOG, NOH, NOI, NOJ, NOK, NOL, NOM, NON, NOO, NOP, NOQ, NOR, NOS, NOT, NOU
=cut
@@ -2349,10 +2353,12 @@ value, you would either delete the NOA specification, or specify it as "1".
The associated subroutine code that uses the A field might look like this:
static long my_asub_routine(aSubRecord *prec) {
- long i, *a;
+ long i;
+ epicsInt32 *a;
+
double sum=0;
...
- a = (long *)prec->a;
+ a = (epicsInt32 *)prec->a;
for (i=0; inoa; i++) {
sum += a[i];
}
diff --git a/modules/database/src/std/rec/aaoRecord.c b/modules/database/src/std/rec/aaoRecord.c
index e96f2b7d0..1aae712d0 100644
--- a/modules/database/src/std/rec/aaoRecord.c
+++ b/modules/database/src/std/rec/aaoRecord.c
@@ -44,6 +44,7 @@
#include "special.h"
#include "cantProceed.h"
#include "menuYesNo.h"
+#include "menuOmsl.h"
#define GEN_SIZE_OFFSET
#include "aaoRecord.h"
@@ -92,6 +93,7 @@ rset aaoRSET={
epicsExportAddress(rset,aaoRSET);
static void monitor(aaoRecord *);
+static long fetchValue(aaoRecord *, int);
static long writeValue(aaoRecord *);
static long init_record(struct dbCommon *pcommon, int pass)
@@ -142,7 +144,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
recGblRecordError(S_dev_missingSup, prec, "aao: init_record");
return S_dev_missingSup;
}
- return 0;
+ return fetchValue(prec, 1);
}
static long process(struct dbCommon *pcommon)
@@ -161,8 +163,11 @@ static long process(struct dbCommon *pcommon)
if ( !pact ) {
prec->udf = FALSE;
+ if(!!(status = fetchValue(prec, 0)))
+ return status;
+
/* Update the timestamp before writing output values so it
- * will be uptodate if any downstream records fetch it via TSEL */
+ * will be up to date if any downstream records fetch it via TSEL */
recGblGetTimeStampSimm(prec, prec->simm, NULL);
}
@@ -339,6 +344,34 @@ static void monitor(aaoRecord *prec)
db_post_events(prec, &prec->val, monitor_mask);
}
+static long fetchValue(aaoRecord *prec, int init)
+{
+ int isConst;
+ long status;
+ long nReq = prec->nelm;
+
+ if(prec->omsl!=menuOmslclosed_loop)
+ return 0;
+
+ isConst = dbLinkIsConstant(&prec->dol);
+
+ if(init && isConst) {
+ status = dbLoadLinkArray(&prec->dol, prec->ftvl, prec->bptr, &nReq);
+
+ } else if(!init && !isConst) {
+ status = dbGetLink(&prec->dol, prec->ftvl, prec->bptr, 0, &nReq);
+
+ } else {
+ return 0;
+ }
+
+ if(!status) {
+ prec->nord = nReq;
+ prec->udf = FALSE;
+ }
+ return status;
+}
+
static long writeValue(aaoRecord *prec)
{
aaodset *pdset = (aaodset *) prec->dset;
@@ -357,7 +390,7 @@ static long writeValue(aaoRecord *prec)
case menuYesNoYES: {
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
if (prec->pact || (prec->sdly < 0.)) {
- /* Device suport is responsible for buffer
+ /* Device support is responsible for buffer
which might be write-only so we may not be
allowed to call dbPutLink on it.
Maybe also device support has an advanced
diff --git a/modules/database/src/std/rec/aaoRecord.dbd.pod b/modules/database/src/std/rec/aaoRecord.dbd.pod
index d4e8c5b59..cfb70af2b 100644
--- a/modules/database/src/std/rec/aaoRecord.dbd.pod
+++ b/modules/database/src/std/rec/aaoRecord.dbd.pod
@@ -10,9 +10,9 @@
=title Array Analog Output (aao)
The array analog output record type is used to write array data. The array data
-can contain any of the supported data types. The record is in many ways similar to
-the waveform record but outputs arrays instead of reading them. It also allows the
-device support to allocate the array storage.
+can contain any of the supported data types. The record is in many ways similar
+to the waveform record but outputs arrays instead of reading them. It also
+allows the device support to allocate the array storage.
=recordtype aao
@@ -32,28 +32,30 @@ The record-specific fields are described below, grouped by functionality.
=head3 Scan Parameters
The array analog output record has the standard fields for specifying under what
-circumstances the record will be processed. These fields are listed in L. In addition, L explains how these fields are
-used. I/O event scanning is only available when supported by device support.
+circumstances the record will be processed.
+These fields are described in L.
+
+=fields SCAN, PHAS, EVNT, PRIO, PINI
=head3 Write Parameters
These fields are configurable by the user to specify how and where to the record
-writes its data. The OUT field determines where the array analog output writes 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 for information on the format
-of hardware addresses and database links.
+writes its data. The OUT field determines where the array analog output writes
+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
+for information on the format of hardware addresses and database links.
=head4 Fields related to array writing
-The DTYP field must contain the name of the appropriate device support module. The
-values in the array referenced by are written to the location specified in the OUT
-field. (If the OUT link is a constant, no data are written.) NELM specifies the
-maximum number of elements that the array can hold, while FTVL specifies the data
-type of the elements (follow the link in the table below for a list of the
-available choices).
+The DTYP field must contain the name of the appropriate device support module.
+The values in the array referenced by are written to the location specified in
+the OUT field. (If the OUT link is a constant, no data are written.) NELM
+specifies the maximum number of elements that the array can hold, while FTVL
+specifies the data type of the elements (follow the link in the table below for
+a list of the available choices).
=fields DTYP, OUT, NELM, FTVL
@@ -117,13 +119,34 @@ These parameters are used by the run-time code for processing the array analog
output record. They are not configured using a configuration tool. Only the VAL
field is modifiable at run-time.
-VAL references the array where the array analog output record stores its data. The
-BPTR field holds the address of the array.
+VAL references the array where the array analog output record stores its data.
+The BPTR field holds the address of the array.
-The NORD field holds a counter of the number of elements that have been written to
-the output,
+The NORD field holds a counter of the number of elements that have been written
+to the output,
-=fields VAL, BPTR, NORD
+=fields VAL, BPTR, NORD, OMSL, DOL
+
+The following steps are performed in order during record processing.
+
+=head4 Fetch Value
+
+The OMSL menu field is used to determine whether the DOL link
+field should be used during processing or not:
+
+=over
+
+=item *
+If OMSL is C the DOL field are not used.
+The new output value is taken from the VAL field, which may have been set from
+elsewhere.
+
+=item *
+If OMSL is C the DOL link field is read to obtain a value.
+
+=back
+
+Note: The OMSL and DOL fields were added to the aaoRecord in Base 7.0.7.
=head3 Simulation Mode Parameters
@@ -325,6 +348,17 @@ Scan forward link if necessary, set PACT FALSE, and return.
promptgroup("50 - Output")
interest(1)
}
+ field(DOL,DBF_INLINK) {
+ prompt("Desired Output Link")
+ promptgroup("40 - Input")
+ interest(1)
+ }
+ field(OMSL,DBF_MENU) {
+ prompt("Output Mode Select")
+ promptgroup("50 - Output")
+ interest(1)
+ menu(menuOmsl)
+ }
field(EGU,DBF_STRING) {
prompt("Engineering Units")
promptgroup("80 - Display")
@@ -438,9 +472,9 @@ Scan forward link if necessary, set PACT FALSE, and return.
=head3 Fields Of Interest To Device Support
Each array analog output record record must have an associated set of device
-support routines. The primary responsibility of the device support routines is to
-write the array data value whenever C is called. The device support
-routines are primarily interested in the following fields:
+support routines. The primary responsibility of the device support routines is
+to write the array data value whenever C is called. The device
+support routines are primarily interested in the following fields:
=fields PACT, DPVT, NSEV, NSTA, OUT, NELM, FTVL, BPTR, NORD
@@ -490,7 +524,8 @@ provided for any device type that can use the ioEvent scanner.
long write_aao(dbCommon *precord)
-This routine must write the array data to output. It returns the following values:
+This routine must write the array data to output. It returns the following
+values:
=over
diff --git a/modules/database/src/std/rec/aiRecord.c b/modules/database/src/std/rec/aiRecord.c
index 8d31b07ff..72a4c61f5 100644
--- a/modules/database/src/std/rec/aiRecord.c
+++ b/modules/database/src/std/rec/aiRecord.c
@@ -43,7 +43,7 @@
#undef GEN_SIZE_OFFSET
#include "epicsExport.h"
-/* Hysterisis for alarm filtering: 1-1/e */
+/* Hysteresis for alarm filtering: 1-1/e */
#define THRESHOLD 0.6321
/* Create RSET - Record Support Entry Table*/
diff --git a/modules/database/src/std/rec/aiRecord.dbd.pod b/modules/database/src/std/rec/aiRecord.dbd.pod
index 5ade85126..4515368b2 100644
--- a/modules/database/src/std/rec/aiRecord.dbd.pod
+++ b/modules/database/src/std/rec/aiRecord.dbd.pod
@@ -45,8 +45,9 @@ hardware address information that the device support uses to determine where the
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 for a description of the various hardware
-address formats supported.
+See L
+for a description of the various hardware address formats supported.
=head3 Units Conversion
@@ -157,6 +158,10 @@ They do not affect the functioning of the record at all.
=over
+=item *
+NAME is the record's name, and can be useful when the PV name that a client
+knows is an alias for the record.
+
=item *
DESC is a string that is usually used to briefly describe the record.
@@ -175,7 +180,10 @@ DOUBLE fields.
=back
-=fields DESC, EGU, HOPR, LOPR, PREC
+See L for more about the record name (NAME) and description (DESC) fields.
+
+=fields NAME, DESC, EGU, HOPR, LOPR, PREC
=head3 Alarm Limits
@@ -198,6 +206,11 @@ 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
+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.
+
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, AFTC, LALM
=head3 Monitor Parameters
diff --git a/modules/database/src/std/rec/aoRecord.c b/modules/database/src/std/rec/aoRecord.c
index 022424054..333a80c4e 100644
--- a/modules/database/src/std/rec/aoRecord.c
+++ b/modules/database/src/std/rec/aoRecord.c
@@ -37,7 +37,7 @@
#include "recGbl.h"
#include "menuConvert.h"
#include "menuOmsl.h"
-#include "menuYesNo.h"
+#include "menuSimm.h"
#include "menuIvoa.h"
#define GEN_SIZE_OFFSET
@@ -188,7 +188,7 @@ static long process(struct dbCommon *pcommon)
if(!status) convert(prec, value);
prec->udf = isnan(prec->val);
/* Update the timestamp before writing output values so it
- * will be uptodate if any downstream records fetch it via TSEL */
+ * will be up to date if any downstream records fetch it via TSEL */
recGblGetTimeStampSimm(prec, prec->simm, NULL);
}
@@ -561,14 +561,20 @@ static long writeValue(aoRecord *prec)
}
switch (prec->simm) {
- case menuYesNoNO:
+ case menuSimmNO:
status = pdset->write_ao(prec);
break;
- case menuYesNoYES: {
+ case menuSimmYES:
+ case menuSimmRAW:
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
if (prec->pact || (prec->sdly < 0.)) {
- status = dbPutLink(&prec->siol, DBR_DOUBLE, &prec->oval, 1);
+ if (prec->simm == menuSimmYES)
+ /* don't convert */
+ status = dbPutLink(&prec->siol, DBR_DOUBLE, &prec->oval, 1);
+ else /* prec->simm == menuSimmRAW*/
+ /* convert */
+ status = dbPutLink(&prec->siol, DBR_LONG, &prec->rval, 1);
prec->pact = FALSE;
} else { /* !prec->pact && delay >= 0. */
epicsCallback *pvt = prec->simpvt;
@@ -580,7 +586,6 @@ static long writeValue(aoRecord *prec)
prec->pact = TRUE;
}
break;
- }
default:
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
diff --git a/modules/database/src/std/rec/aoRecord.dbd.pod b/modules/database/src/std/rec/aoRecord.dbd.pod
index 5fef0d09e..ad40c75f7 100644
--- a/modules/database/src/std/rec/aoRecord.dbd.pod
+++ b/modules/database/src/std/rec/aoRecord.dbd.pod
@@ -161,9 +161,9 @@ The analog output record sends its desired output to the address in the
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
-Address Specification for information on the format of hardware
-addresses.
+that the address format differs according to the I/O bus used. See L
+for information on the format of hardware addresses.
For soft records the output link can be a database link, a channel
access link, or a constant value. If the link is a constant, no output
@@ -263,8 +263,9 @@ processing.
The following fields are used to operate the record in simulation mode.
-If SIMM (fetched through SIML) is YES, the record is put in SIMS
+If SIMM (fetched through SIML, if populated) is YES, the record is put in SIMS
severity and the value is written through SIOL, without conversion.
+If SIMM is RAW, the value is converted and RVAL is written.
SSCN sets a different SCAN mechanism to use in simulation mode.
SDLY sets a delay (in sec) that is used for asynchronous simulation
processing.
@@ -307,7 +308,7 @@ for more information on simulation mode and its fields.
interest(1)
}
field(DOL,DBF_INLINK) {
- prompt("Desired Output Loc")
+ prompt("Desired Output Link")
promptgroup("40 - Input")
interest(1)
}
@@ -557,7 +558,7 @@ for more information on simulation mode and its fields.
prompt("Simulation Mode")
special(SPC_MOD)
interest(1)
- menu(menuYesNo)
+ menu(menuSimm)
}
field(SIMS,DBF_MENU) {
prompt("Simulation Mode Severity")
diff --git a/modules/database/src/std/rec/biRecord.dbd.pod b/modules/database/src/std/rec/biRecord.dbd.pod
index 449a68441..40ecd0293 100644
--- a/modules/database/src/std/rec/biRecord.dbd.pod
+++ b/modules/database/src/std/rec/biRecord.dbd.pod
@@ -37,6 +37,8 @@ The binary input record has the standard fields for specifying under what
circumstances the record will be processed.
These fields are described in L.
+=fields SCAN, PHAS, EVNT, PRIO, PINI
+
=head3 Read and Convert Parameters
The read and convert fields determine where the binary input gets its
@@ -44,14 +46,15 @@ input from and how to convert the raw signal to engineering units. The INP
field contains the address from where device support retrieves the value.
If the binary input record gets its value from hardware, the address of the
card must be entered in the INP field, and the name of the device support
-module must be entered in the DTYP field. See L for
-information on the format of the hardware address. Be aware that the format
-differs between types of cards.
+module must be entered in the DTYP field. See L
+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 for information on the format of database and
+constant. If a constant, VAL can be changed directly by dbPuts. See L
+for information on the format of database and
channel access addresses. Also, see L in
this chapter for information on soft device support.
@@ -102,9 +105,10 @@ C. The ZSV field holds the severity for the zero state; OSV, for
the one state. COSV causes an alarm whenever the state changes between
0 and 1 and the severity is configured as MINOR or MAJOR.
-See L for a complete explanation of the discrete alarm
-states. L lists other fields related to alarms that are
-common to all record types.
+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.
=fields ZSV, OSV, COSV
diff --git a/modules/database/src/std/rec/boRecord.c b/modules/database/src/std/rec/boRecord.c
index 05195b880..b58b4d816 100644
--- a/modules/database/src/std/rec/boRecord.c
+++ b/modules/database/src/std/rec/boRecord.c
@@ -34,7 +34,7 @@
#include "special.h"
#include "menuIvoa.h"
#include "menuOmsl.h"
-#include "menuYesNo.h"
+#include "menuSimm.h"
#define GEN_SIZE_OFFSET
#include "boRecord.h"
@@ -211,7 +211,7 @@ static long process(struct dbCommon *pcommon)
} else prec->rval = (epicsUInt32)prec->val;
/* Update the timestamp before writing output values so it
- * will be uptodate if any downstream records fetch it via TSEL */
+ * will be up to date if any downstream records fetch it via TSEL */
recGblGetTimeStampSimm(prec, prec->simm, NULL);
}
@@ -427,14 +427,18 @@ static long writeValue(boRecord *prec)
}
switch (prec->simm) {
- case menuYesNoNO:
+ case menuSimmNO:
status = pdset->write_bo(prec);
break;
- case menuYesNoYES: {
+ case menuSimmYES:
+ case menuSimmRAW:
recGblSetSevr(prec, SIMM_ALARM, prec->sims);
if (prec->pact || (prec->sdly < 0.)) {
- status = dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1);
+ if (prec->simm == menuSimmYES)
+ status = dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1);
+ else /* prec->simm == menuSimmRAW */
+ status = dbPutLink(&prec->siol, DBR_ULONG, &prec->rval, 1);
prec->pact = FALSE;
} else { /* !prec->pact && delay >= 0. */
epicsCallback *pvt = prec->simpvt;
@@ -446,7 +450,6 @@ static long writeValue(boRecord *prec)
prec->pact = TRUE;
}
break;
- }
default:
recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM);
diff --git a/modules/database/src/std/rec/boRecord.dbd.pod b/modules/database/src/std/rec/boRecord.dbd.pod
index bfbc8e820..618c53d82 100644
--- a/modules/database/src/std/rec/boRecord.dbd.pod
+++ b/modules/database/src/std/rec/boRecord.dbd.pod
@@ -15,29 +15,6 @@ values into other records via database or channel access links. This record
can implement both latched and momentary binary outputs depending on how
the HIGH field is configured.
-=head2 Parameter Fields
-
-The binary output's fields fall into the following categories:
-
-=over 1
-
-=item *
-scan parameters
-
-=item *
-convert and write parameters
-
-=item *
-operator display parameters
-
-=item *
-alarm parameters
-
-=item *
-run-time parameters
-
-=back
-
=recordtype bo
=cut
@@ -47,13 +24,10 @@ recordtype(bo) {
=head3 Scan Parameters
The binary output record has the standard fields for specifying under what
-circumstances the record will be processed. The fields are listed in
+circumstances the record will be processed.
+These fields are described in L.
-L. In addition, L explains how these
-fields are used. Note that I/O event scanning is only supported for those card
-types that interrupt.
-
-=fields SCAN
+=fields SCAN, PHAS, EVNT, PRIO, PINI
=head3 Desired Output Parameters
@@ -65,14 +39,14 @@ output mode select (OMSL) field, which can have two possible values:
C or C. If C is specified, the value
in the VAL field can be set externally via dbPuts at run-time. If
C is specified, the VAL field's value is obtained from the
-address specified in the desired output location (DOL) field which can be a
+address specified in the Desired Output Link (DOL) field which can be a
database link or a channel access link, but not a constant. To achieve
continuous control, a database link to a control algorithm record should be
entered in the DOL field.
-L presents more information on database addresses
-and links. L explaines the effect of database
-linkage on scanning.
+See L
+for information on hardware addresses and links.
=fields DOL, OMSL
@@ -130,15 +104,17 @@ The OUT field specifies where the binary output record writes its output.
It must specify the address of an I/O card if the record sends its output
to hardware, and the DTYP field must contain the corresponding device
support module. Be aware that the address format differs according to the
-I/O bus used. See L for information on the format of
-hardware addresses.
+I/O bus used. See L
+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 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.
+Otherwise, if the record is configured to use the soft device support modules,
+then it can be either a database link, a channel access link, or a constant. Be
+aware that nothing will be written when OUT is a constant. See L
+for information on the format of the database and channel access addresses.
+Also, see L in this chapter for more on output
+to other records.
=head3 Operator Display Parameters
@@ -206,8 +182,9 @@ The WPDT field is a private field for honoring seconds to hold HIGH.
The following fields are used to operate the record in simulation mode.
-If SIMM (fetched through SIML) is YES, the record is put in SIMS
-severity and the value is written through SIOL.
+If SIMM (fetched through SIML, if populated) is YES, the record is put
+in SIMS severity and the unconverted value is written through SIOL.
+If SIMM is RAW, the value is converted and RVAL is written.
SSCN sets a different SCAN mechanism to use in simulation mode.
SDLY sets a delay (in sec) that is used for asynchronous simulation
processing.
@@ -242,7 +219,7 @@ for more information on simulation mode and its fields.
menu(menuOmsl)
}
field(DOL,DBF_INLINK) {
- prompt("Desired Output Loc")
+ prompt("Desired Output Link")
promptgroup("40 - Input")
interest(1)
}
@@ -352,7 +329,7 @@ for more information on simulation mode and its fields.
prompt("Simulation Mode")
special(SPC_MOD)
interest(1)
- menu(menuYesNo)
+ menu(menuSimm)
}
field(SIMS,DBF_MENU) {
prompt("Simulation Mode Severity")
diff --git a/modules/database/src/std/rec/calcRecord.c b/modules/database/src/std/rec/calcRecord.c
index 8bb9435fa..4467c7080 100644
--- a/modules/database/src/std/rec/calcRecord.c
+++ b/modules/database/src/std/rec/calcRecord.c
@@ -37,7 +37,7 @@
#undef GEN_SIZE_OFFSET
#include "epicsExport.h"
-/* Hysterisis for alarm filtering: 1-1/e */
+/* Hysteresis for alarm filtering: 1-1/e */
#define THRESHOLD 0.6321
/* Create RSET - Record Support Entry Table */
diff --git a/modules/database/src/std/rec/calcRecord.dbd.pod b/modules/database/src/std/rec/calcRecord.dbd.pod
index b1d4242b4..053ce85c5 100644
--- a/modules/database/src/std/rec/calcRecord.dbd.pod
+++ b/modules/database/src/std/rec/calcRecord.dbd.pod
@@ -28,8 +28,9 @@ recordtype(calc) {
The Calc record has the standard fields for specifying under what
circumstances the record will be processed.
-These fields are listed in L.
+These fields are described in L.
+=fields SCAN, PHAS, EVNT, PRIO, PINI
=head3 Read Parameters
@@ -40,8 +41,9 @@ channel access link. If they are constants, they will be initialized with
the value they are configured with and can be changed via C. They
cannot be hardware addresses.
-See L for information on how to specify database
-links.
+See L
+for information on how to specify database links.
=fields INPA, INPB, INPC, INPD, INPE, INPF, INPG, INPH, INPI, INPJ, INPK, INPL
@@ -155,6 +157,9 @@ CEIL: Ceiling (unary)
=item *
FLOOR: Floor (unary)
+=item *
+FMOD: Floating point modulo (binary) Added in UNRELEASED
+
=item *
LOG: Log base 10 (unary)
@@ -453,9 +458,12 @@ The following alarm parameters which are configured by the user, define the
limit alarms for the VAL field and the severity corresponding to those
conditions.
-The HYST field defines an alarm deadband for each limit. See L
-for a complete explanation of alarms of these fields. L
-lists other fields related to alarms that are common to all record types.
+The HYST field defines an alarm deadband for each limit.
+
+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.
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST
diff --git a/modules/database/src/std/rec/calcoutRecord.c b/modules/database/src/std/rec/calcoutRecord.c
index eadcc5862..319994596 100644
--- a/modules/database/src/std/rec/calcoutRecord.c
+++ b/modules/database/src/std/rec/calcoutRecord.c
@@ -245,7 +245,7 @@ static long process(struct dbCommon *pcommon)
if ( !pact ) {
/* Update the timestamp before writing output values so it
- * will be uptodate if any downstream records fetch it via TSEL */
+ * will be up to date if any downstream records fetch it via TSEL */
recGblGetTimeStamp(prec);
}
/* check for output link execution */
diff --git a/modules/database/src/std/rec/calcoutRecord.dbd.pod b/modules/database/src/std/rec/calcoutRecord.dbd.pod
index 9302ba410..b62f57520 100644
--- a/modules/database/src/std/rec/calcoutRecord.dbd.pod
+++ b/modules/database/src/std/rec/calcoutRecord.dbd.pod
@@ -183,6 +183,9 @@ CEIL: Ceiling (unary)
=item *
FLOOR: Floor (unary)
+=item *
+FMOD: Floating point modulo (binary) Added in UNRELEASED
+
=item *
LOG: Log base 10 (unary)
@@ -535,13 +538,13 @@ the user and which describes the values being operated upon. The string is
retrieved whenever the routine C is called. The EGU string is
solely for an operator's sake and does not have to be used.
-The HOPR and LOPR fields on;y refer to the limits if the VAL, HIHI, HIGH,
+The HOPR and LOPR fields only refer to the limits of the VAL, HIHI, HIGH,
LOW, and LOLO fields. PREC controls the precision of the VAL field.
=head4 Menu calcoutINAV
The INAV-INLV fields indicate the status of the link to the PVs specified
-in the INPA-INPL fields, respectfully. These field can have four possible
+in the INPA-INPL fields respectively. These fields can have four possible
values:
=menu calcoutINAV
@@ -568,7 +571,7 @@ The OUTV field indicates the status of the OUT link. If has the same
possible values as the INAV-INLV fields.
The CLCV and OLCV fields indicate the validity of the expression in the
-CALC and OCAL fields respectfully. If the expression in invalid, the field
+CALC and OCAL fields respectively. If the expression in invalid, the field
is set to one.
The DLYA field is set to one during the delay specified in ODLY.
@@ -590,10 +593,12 @@ The following alarm parameters, which are configured by the user, define the
limit alarms for the VAL field and the severity corresponding to those
conditions.
-The HYST field defines an alarm deadband for each limit. See
-L for a complete explanation of alarms and these
-fields. C lists other fields related to alarms that are
-common to all record types.
+The HYST field defines an alarm deadband for each limit.
+
+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.
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST
@@ -1218,7 +1223,7 @@ 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 it met, either
+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.
diff --git a/modules/database/src/std/rec/compressRecord.dbd.pod b/modules/database/src/std/rec/compressRecord.dbd.pod
index d421fff5a..87ef67c00 100644
--- a/modules/database/src/std/rec/compressRecord.dbd.pod
+++ b/modules/database/src/std/rec/compressRecord.dbd.pod
@@ -67,11 +67,12 @@ The record-specific fields are described below, grouped by functionality.
=head3 Scanning Parameters
The compression record has the standard fields for specifying under what
-circumstances the record will be processed. These fields are listed in
+circumstances the record will be processed. Since the compression record
+supports no direct interfaces to hardware, its SCAN field cannot be set to C<<<
+I/O Intr >>>.
+These fields are described in L.
-L. In addition, L
-explains how these fields are used. Since the compression record supports no
-direct interfaces to hardware, its SCAN field cannot specify C<<< I/O Intr >>>.
+=fields SCAN, PHAS, EVNT, PRIO, PINI
=head3 Algorithms and Related Parameters
@@ -88,9 +89,11 @@ The following fields determine what channel to read and how to compress the data
As stated above, the ALG field specifies which algorithm to be performed on the data.
-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 for information on specifying links.
+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
+for information on specifying links.
IHIL and ILIL can be set to provide an initial value filter on the input array.
@@ -209,18 +212,26 @@ described in L.
These parameters are used by the run-time code for processing the data
compression algorithm. They are not configurable by the user, though some are
-accessible at run-time. They can represent the current state of the waveform or
+accessible at run-time. They can represent the current state of the algorithm or
of the record whose field is referenced by the INP field.
=fields NUSE, OUSE, BPTR, SPTR, WPTR, CVB, INPN, INX
NUSE and OUSE hold the current and previous number of elements stored in VAL.
-BPTR is a pointer that refers to the buffer referenced by VAL.
+BPTR points to the buffer referenced by VAL.
SPTR points to an array that is used for array averages.
-WPTR is used by the dbGetlinks routines.
+WPTR points to the buffer containing data referenced by INP.
+
+CVB stores the current compressed value for C<<< N to 1 >>> algorithms when INP
+references a scalar.
+
+INPN is updated when the record processes; if INP references an array and the
+size changes, the WPTR buffer is reallocated.
+
+INX counts the number of readings collected.
=head2 Record Support
@@ -479,7 +490,7 @@ Scan forward link if necessary, set PACT FALSE, and return.
interest(3)
}
field(INX,DBF_ULONG) {
- prompt("Compressed Array Inx")
+ prompt("Current number of readings")
special(SPC_NOMOD)
interest(3)
}
diff --git a/modules/database/src/std/rec/dfanoutRecord.dbd.pod b/modules/database/src/std/rec/dfanoutRecord.dbd.pod
index 9aa632b55..82a3ecf7d 100644
--- a/modules/database/src/std/rec/dfanoutRecord.dbd.pod
+++ b/modules/database/src/std/rec/dfanoutRecord.dbd.pod
@@ -43,7 +43,7 @@ originates, i.e., the data which is to be fowarded to the records in its
output links. The output mode select (OMSL) field determines whether the
output originates from another record or from run-time database access.
When set to C, the desired output is retrieved from the link
-specified in the desired output (DOL) field, which can specify either a
+specified in the Desired Output Link (DOL) field, which can specify either a
database or a channel access link, and placed into the VAL field. When set
to C, the desired output can be written to the VAL field via
dbPuts at run-time.
@@ -59,8 +59,9 @@ undergoes no conversions before it is sent out to the output links.
=head3 Write Parameters
The OUTA-OUTH fields specify where VAL is to be sent. Each field that is to
-forward data must specify an address to another record. See
-L for information on specifying links.
+forward data must specify an address to another record. See L
+for information on specifying links.
The SELL, SELM, and SELN fields specify which output links are to be
used.
@@ -71,42 +72,57 @@ SELM is a menu, with three choices:
=menu dfanoutSELM
-If SELM=="All", then all output links are used, and the values of
+If SELM is C, then all output links are used, and the values of
SELL and SELN are ignored.
-If SELM=="Specified", then the value of SELN is used to specify a single
+If SELM is C, then the value of SELN is used to specify a single
link which will be used. If SELN==0, then no link will be used; if SELN==1,
then OUTA will be used, and so on.
-SELN can either have its value set directly, or have its values retrieved
-from another EPICS PV. If SELL is a valid PV link, then SELN will be set to
-the values of the linked PV.
+SELN can either have its value set directly, or have it retrieved from
+another EPICS PV. If SELL is a valid PV link, then SELN will be read from
+the linked PV.
-If SELM=="Mask", then SELN will be treated as a bit mask. If bit one of
-SELN is set, then OUTA will be used, if bit two is set, OUTB will be used.
-Thus if SELN==5, OUTC and OUTA will be used.
+If SELM is C, then SELN will be treated as a bit mask. If bit zero
+(the LSB) of SELN is set, then OUTA will be written to; if bit one is set,
+OUTB will be written to, and so on. Thus when SELN==5, both OUTC and OUTA
+will be written to.
-=fields OUTA, OUTB, OUTC, OUTD, OUTE, OUTF, OUTG, OUTH
+=fields SELL, SELM, SELN, OUTA, OUTB, OUTC, OUTD, OUTE, OUTF, OUTG, OUTH
=head3 Operator Display Parameters
-These parameters are used to present meaningful data to the operator. They
-display the value and other parameters of the data fanout record either
-textually or graphically.
+These parameters are used to present meaningful data to the operator.
+They do not affect the functioning of the record at all.
-The EGU field can contain a string of up to 16 characters describing the
-value on the VAL field.
+=over
-The HOPR and LOPR fields determine the upper and lower display limits for
-graphic displays and the upper and lower control limits for control
-displays. They apply to the VAL, HIHI, HIGH, LOW, and LOLO fields. The
-record support routines C and C
-retrieve HOPR and LOPR.
+=item *
+NAME is the record's name, and can be useful when the PV name that a client
+knows is an alias for the record.
+
+=item *
+DESC is a string that is usually used to briefly describe the record.
+
+=item *
+EGU is a string of up to 16 characters naming the engineering units that the VAL
+field represents.
+
+=item *
+The HOPR and LOPR fields set the upper and lower display limits for the VAL,
+HIHI, HIGH, LOW, and LOLO fields.
+
+=item *
+The PREC field determines the floating point precision (i.e. the number of
+digits to show after the decimal point) with which to display VAL and the other
+DOUBLE fields.
+
+=back
See L for more on the record name (NAME) and description (DESC) fields.
+Parameters> for more about the record name (NAME) and description (DESC) fields.
-=fields EGU, HOPR, LOPR, NAME, DESC
+=fields NAME, DESC, EGU, HOPR, LOPR, PREC
=head3 Alarm Parameters
@@ -119,9 +135,10 @@ in the corresponding field (HHSV, LLSV, HSV, LSV) and can be either
NO_ALARM, MINOR, or MAJOR. In the hysteresis field (HYST) can be entered a
number which serves as the deadband on the limit alarms.
-See L for a complete explanation of alarms and these
-fields. L lists other fields related to alarms that are
-common to all record types.
+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.
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST
@@ -211,7 +228,7 @@ hysteresis factors for monitor callbacks.
interest(1)
}
field(DOL,DBF_INLINK) {
- prompt("Desired Output Loc")
+ prompt("Desired Output Link")
promptgroup("40 - Input")
interest(1)
}
diff --git a/modules/database/src/std/rec/eventRecord.dbd.pod b/modules/database/src/std/rec/eventRecord.dbd.pod
index 811c387da..964c3973c 100644
--- a/modules/database/src/std/rec/eventRecord.dbd.pod
+++ b/modules/database/src/std/rec/eventRecord.dbd.pod
@@ -37,10 +37,10 @@ recordtype(event) {
=head3 Scan Parameters
The event record has the standard fields for specifying under what circumstances
-it will be processed. If the SCAN field specifies C, then device
-support will provide an interrupt handler, posting an event number when an I/O
-interrupt occurs.
-These fields are listed in L.
+it will be processed.
+These fields are described in L.
+
+=fields SCAN, PHAS, EVNT, PRIO, PINI
=head3 Event Number Parameters
@@ -73,8 +73,9 @@ 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 for information on the format of
-hardware addresses and specifying links.
+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.
diff --git a/modules/database/src/std/rec/histogramRecord.c b/modules/database/src/std/rec/histogramRecord.c
index 44b278f39..9f35870ed 100644
--- a/modules/database/src/std/rec/histogramRecord.c
+++ b/modules/database/src/std/rec/histogramRecord.c
@@ -160,7 +160,7 @@ static long init_record(struct dbCommon *pcommon, int pass)
prec->bptr = calloc(prec->nelm, sizeof(epicsUInt32));
}
- /* calulate width of array element */
+ /* calculate width of array element */
prec->wdth = (prec->ulim - prec->llim) / prec->nelm;
return 0;
}
diff --git a/modules/database/src/std/rec/int64inRecord.c b/modules/database/src/std/rec/int64inRecord.c
index 2cd804d07..d4ea757c3 100644
--- a/modules/database/src/std/rec/int64inRecord.c
+++ b/modules/database/src/std/rec/int64inRecord.c
@@ -40,7 +40,7 @@
#undef GEN_SIZE_OFFSET
#include "epicsExport.h"
-/* Hysterisis for alarm filtering: 1-1/e */
+/* Hysteresis for alarm filtering: 1-1/e */
#define THRESHOLD 0.6321
/* Create RSET - Record Support Entry Table*/
#define report NULL
diff --git a/modules/database/src/std/rec/int64outRecord.c b/modules/database/src/std/rec/int64outRecord.c
index bf306e8e0..d5747f0c2 100644
--- a/modules/database/src/std/rec/int64outRecord.c
+++ b/modules/database/src/std/rec/int64outRecord.c
@@ -146,7 +146,7 @@ static long process(dbCommon *pcommon)
if (!status) convert(prec,value);
/* Update the timestamp before writing output values so it
- * will be uptodate if any downstream records fetch it via TSEL */
+ * will be up to date if any downstream records fetch it via TSEL */
recGblGetTimeStampSimm(prec, prec->simm, NULL);
}
diff --git a/modules/database/src/std/rec/int64outRecord.dbd.pod b/modules/database/src/std/rec/int64outRecord.dbd.pod
index e096ee5d5..09fd38d6d 100644
--- a/modules/database/src/std/rec/int64outRecord.dbd.pod
+++ b/modules/database/src/std/rec/int64outRecord.dbd.pod
@@ -154,7 +154,7 @@ monitoring deadband functionality.
interest(1)
}
field(DOL,DBF_INLINK) {
- prompt("Desired Output Loc")
+ prompt("Desired Output Link")
promptgroup("40 - Input")
interest(1)
}
diff --git a/modules/database/src/std/rec/longinRecord.c b/modules/database/src/std/rec/longinRecord.c
index a6b58eef4..2bd8a8350 100644
--- a/modules/database/src/std/rec/longinRecord.c
+++ b/modules/database/src/std/rec/longinRecord.c
@@ -41,7 +41,7 @@
#undef GEN_SIZE_OFFSET
#include "epicsExport.h"
-/* Hysterisis for alarm filtering: 1-1/e */
+/* Hysteresis for alarm filtering: 1-1/e */
#define THRESHOLD 0.6321
/* Create RSET - Record Support Entry Table*/
#define report NULL
diff --git a/modules/database/src/std/rec/longoutRecord.c b/modules/database/src/std/rec/longoutRecord.c
index f0e2252bd..eed2d0c76 100644
--- a/modules/database/src/std/rec/longoutRecord.c
+++ b/modules/database/src/std/rec/longoutRecord.c
@@ -81,10 +81,14 @@ rset longoutRSET={
};
epicsExportAddress(rset,longoutRSET);
+#define DONT_EXEC_OUTPUT 0
+#define EXEC_OUTPUT 1
+
static void checkAlarms(longoutRecord *prec);
static void monitor(longoutRecord *prec);
static long writeValue(longoutRecord *prec);
static void convert(longoutRecord *prec, epicsInt32 value);
+static long conditional_write(longoutRecord *prec);
static long init_record(struct dbCommon *pcommon, int pass)
{
@@ -119,6 +123,9 @@ static long init_record(struct dbCommon *pcommon, int pass)
prec->mlst = prec->val;
prec->alst = prec->val;
prec->lalm = prec->val;
+ prec->pval = prec->val;
+ prec->outpvt = EXEC_OUTPUT;
+
return 0;
}
@@ -148,7 +155,7 @@ static long process(struct dbCommon *pcommon)
if (!status) convert(prec,value);
/* Update the timestamp before writing output values so it
- * will be uptodate if any downstream records fetch it via TSEL */
+ * will be up to date if any downstream records fetch it via TSEL */
recGblGetTimeStampSimm(prec, prec->simm, NULL);
}
@@ -210,6 +217,14 @@ static long special(DBADDR *paddr, int after)
recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm);
return(0);
}
+
+ /* Detect an output link re-direction (change) */
+ if (dbGetFieldIndex(paddr) == longoutRecordOUT) {
+ if ((after) && (prec->ooch == menuYesNoYES))
+ prec->outpvt = EXEC_OUTPUT;
+ return(0);
+ }
+
default:
recGblDbaddrError(S_db_badChoice, paddr, "longout: special");
return(S_db_badChoice);
@@ -381,7 +396,6 @@ static void monitor(longoutRecord *prec)
static long writeValue(longoutRecord *prec)
{
- longoutdset *pdset = (longoutdset *) prec->dset;
long status = 0;
if (!prec->pact) {
@@ -391,7 +405,7 @@ static long writeValue(longoutRecord *prec)
switch (prec->simm) {
case menuYesNoNO:
- status = pdset->write_longout(prec);
+ status = conditional_write(prec);
break;
case menuYesNoYES: {
@@ -421,10 +435,61 @@ static long writeValue(longoutRecord *prec)
static void convert(longoutRecord *prec, epicsInt32 value)
{
- /* check drive limits */
+ /* check drive limits */
if(prec->drvh > prec->drvl) {
if (value > prec->drvh) value = prec->drvh;
else if (value < prec->drvl) value = prec->drvl;
}
prec->val = value;
}
+
+/* Evaluate OOPT field to perform the write operation */
+static long conditional_write(longoutRecord *prec)
+{
+ struct longoutdset *pdset = (struct longoutdset *) prec->dset;
+ long status = 0;
+ int doDevSupWrite = 0;
+
+ switch (prec->oopt)
+ {
+ case longoutOOPT_On_Change:
+ /* Forces a write op if a change in the OUT field is detected OR is first process */
+ if (prec->outpvt == EXEC_OUTPUT) {
+ doDevSupWrite = 1;
+ } else {
+ /* Only write if value is different from the previous one */
+ doDevSupWrite = (prec->val != prec->pval);
+ }
+ break;
+
+ case longoutOOPT_Every_Time:
+ doDevSupWrite = 1;
+ break;
+
+ case longoutOOPT_When_Zero:
+ doDevSupWrite = (prec->val == 0);
+ break;
+
+ case longoutOOPT_When_Non_zero:
+ doDevSupWrite = (prec->val != 0);
+ break;
+
+ case longoutOOPT_Transition_To_Zero:
+ doDevSupWrite = ((prec->val == 0)&&(prec->pval != 0));
+ break;
+
+ case longoutOOPT_Transition_To_Non_zero:
+ doDevSupWrite = ((prec->val != 0)&&(prec->pval == 0));
+ break;
+
+ default:
+ break;
+ }
+
+ if (doDevSupWrite)
+ status = pdset->write_longout(prec);
+
+ prec->pval = prec->val;
+ prec->outpvt = DONT_EXEC_OUTPUT; /* reset status */
+ return status;
+}
diff --git a/modules/database/src/std/rec/longoutRecord.dbd.pod b/modules/database/src/std/rec/longoutRecord.dbd.pod
index 60037a62f..912573749 100644
--- a/modules/database/src/std/rec/longoutRecord.dbd.pod
+++ b/modules/database/src/std/rec/longoutRecord.dbd.pod
@@ -20,6 +20,15 @@ limits.
=cut
+menu(longoutOOPT) {
+ choice(longoutOOPT_Every_Time,"Every Time")
+ choice(longoutOOPT_On_Change,"On Change")
+ choice(longoutOOPT_When_Zero,"When Zero")
+ choice(longoutOOPT_When_Non_zero,"When Non-zero")
+ choice(longoutOOPT_Transition_To_Zero,"Transition To Zero")
+ choice(longoutOOPT_Transition_To_Non_zero,"Transition To Non-zero")
+}
+
recordtype(longout) {
=head2 Parameter Fields
@@ -38,10 +47,10 @@ The record must specify where the desired output originates, i.e., the 32 bit
integer value it is to write. The output mode select (OMSL) field determines
whether the output originates from another record or from database access. When
set to C<<< closed_loop >>>, the desired output is retrieved from the link
-specified in the desired output (DOL) field (which can specify either a database
-or channel access link) and placed into the VAL field. When set to C<<<
-supervisory >>>, the desired output can be written into the VAL field via dpPuts
-at run-time.
+specified in the Desired Output Link (DOL) field (which can specify either a
+database or channel access link) and placed into the VAL field. When set to
+C<<< supervisory >>>, the desired output can be written into the VAL field via
+dpPuts at run-time.
A third type of value for the DOL field is a constant in which case, when the
record is initialized, the VAL field will be initialized with this constant
@@ -68,10 +77,54 @@ For soft records, the OUT output link can be a constant, a database link, or a
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 for information on the format of hardware addresses
-and database links.
+See L
+for information on the format of hardware addresses and database links.
-=fields OUT, DTYP
+=fields OUT, DTYP, OOPT, OOCH
+
+=head4 Menu longoutOOPT
+
+The OOPT field was added in EPICS UNRELEASED.
+
+It determines the condition that causes the output link to be
+written to. It's a menu field that has six choices:
+
+=menu longoutOOPT
+
+=over
+
+=item *
+C -- write output every time record is processed. (DEFAULT)
+
+=item *
+C -- write output every time VAL changes.
+
+=item *
+C -- when record is processed, write output if VAL is zero.
+
+=item *
+C -- when record is processed, write output if VAL is
+non-zero.
+
+=item *
+C