From dc123a0a37db110bf3d6adcdc5720e06874c21ae Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 27 Aug 2019 16:51:00 +0200 Subject: [PATCH 01/59] don't send errlog on all logClients --- src/libCom/log/iocLog.c | 14 ++++++++++++++ src/libCom/log/logClient.c | 4 ---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/libCom/log/iocLog.c b/src/libCom/log/iocLog.c index e62da2050..8cb1349a1 100644 --- a/src/libCom/log/iocLog.c +++ b/src/libCom/log/iocLog.c @@ -18,8 +18,10 @@ #define epicsExportSharedSymbols #include "envDefs.h" +#include "errlog.h" #include "logClient.h" #include "iocLog.h" +#include "epicsExit.h" int iocLogDisable = 0; @@ -74,6 +76,14 @@ void epicsShareAPI epicsShareAPI iocLogFlush (void) } } +/* + * logClientDestroy() + */ +static void iocLogClientDestroy (logClientId id) +{ + errlogRemoveListeners (logClientSendMessage, id); +} + /* * iocLogClientInit() */ @@ -89,6 +99,10 @@ static logClientId iocLogClientInit (void) return NULL; } id = logClientCreate (addr, port); + if (id != NULL) { + errlogAddListener (logClientSendMessage, id); + epicsAtExit (iocLogClientDestroy, id); + } return id; } diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 99ee671d9..b076d50cb 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -154,8 +154,6 @@ static void logClientDestroy (logClientId id) return; } - errlogRemoveListeners ( logClientSendMessage, (void *) pClient ); - logClientClose ( pClient ); epicsMutexDestroy ( pClient->mutex ); @@ -549,8 +547,6 @@ logClientId epicsShareAPI logClientCreate ( pClient->name, LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT ); } - errlogAddListener ( logClientSendMessage, (void *) pClient ); - return (void *) pClient; } From 74a403090b31b2b68283ea08cfe54c113b6a62f9 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 27 Aug 2019 17:34:01 +0200 Subject: [PATCH 02/59] speed up logRestart thread termination at exit --- src/libCom/log/logClient.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index b076d50cb..96382a2eb 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -44,6 +44,7 @@ typedef struct { SOCKET sock; epicsThreadId restartThreadId; epicsEventId stateChangeNotify; + epicsEventId shutdownNotify; unsigned connectCount; unsigned nextMsgIndex; unsigned connected; @@ -113,6 +114,7 @@ static void logClientDestroy (logClientId id) epicsMutexMustLock ( pClient->mutex ); pClient->shutdown = 1u; epicsMutexUnlock ( pClient->mutex ); + epicsEventSignal ( pClient->shutdownNotify ); /* unblock log client thread blocking in send() or connect() */ interruptInfo = @@ -157,8 +159,8 @@ static void logClientDestroy (logClientId id) logClientClose ( pClient ); epicsMutexDestroy ( pClient->mutex ); - epicsEventDestroy ( pClient->stateChangeNotify ); + epicsEventDestroy ( pClient->shutdownNotify ); free ( pClient ); } @@ -461,8 +463,8 @@ static void logClientRestart ( logClientId id ) else { logClientConnect ( pClient ); } - - epicsThreadSleep ( LOG_RESTART_DELAY ); + + epicsEventWaitWithTimeout ( pClient->shutdownNotify, LOG_RESTART_DELAY); epicsMutexMustLock ( pClient->mutex ); } @@ -505,14 +507,22 @@ logClientId epicsShareAPI logClientCreate ( pClient->shutdownConfirm = 0; epicsAtExit (logClientDestroy, (void*) pClient); - + pClient->stateChangeNotify = epicsEventCreate (epicsEventEmpty); if ( ! pClient->stateChangeNotify ) { epicsMutexDestroy ( pClient->mutex ); free ( pClient ); return NULL; } - + + pClient->shutdownNotify = epicsEventCreate (epicsEventEmpty); + if ( ! pClient->shutdownNotify ) { + epicsMutexDestroy ( pClient->mutex ); + epicsEventDestroy ( pClient->stateChangeNotify ); + free ( pClient ); + return NULL; + } + pClient->restartThreadId = epicsThreadCreate ( "logRestart", epicsThreadPriorityLow, epicsThreadGetStackSize(epicsThreadStackSmall), @@ -520,6 +530,7 @@ logClientId epicsShareAPI logClientCreate ( if ( pClient->restartThreadId == NULL ) { epicsMutexDestroy ( pClient->mutex ); epicsEventDestroy ( pClient->stateChangeNotify ); + epicsEventDestroy ( pClient->shutdownNotify ); free (pClient); fprintf(stderr, "log client: unable to start log client connection watch dog thread\n"); return NULL; From cf2658be5329f90f997be8ea8548d3b5a7188c60 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 28 Aug 2019 09:29:57 +0200 Subject: [PATCH 03/59] do not discard unsent messages when log server has closed connection, instead try to send them after reconnect --- src/libCom/log/logClient.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 96382a2eb..75984404c 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -85,8 +85,6 @@ static void logClientClose ( logClient *pClient ) pClient->sock = INVALID_SOCKET; } - pClient->nextMsgIndex = 0u; - memset ( pClient->msgBuf, '\0', sizeof ( pClient->msgBuf ) ); pClient->connected = 0u; /* From 06f1a8ec23cba2f47568879fd1c0ae8665935c1b Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 28 Aug 2019 11:41:12 +0200 Subject: [PATCH 04/59] elimitate duplicate code in logClient --- src/libCom/log/logClient.c | 62 +++++++++----------------------------- 1 file changed, 14 insertions(+), 48 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 75984404c..90fde98b5 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -174,57 +174,23 @@ static void sendMessageChunk(logClient * pClient, const char * message) { unsigned msgBufBytesLeft = sizeof ( pClient->msgBuf ) - pClient->nextMsgIndex; - if ( strSize > msgBufBytesLeft ) { - int status; - - if ( ! pClient->connected ) { - break; - } - - if ( msgBufBytesLeft > 0u ) { - memcpy ( & pClient->msgBuf[pClient->nextMsgIndex], - message, msgBufBytesLeft ); - pClient->nextMsgIndex += msgBufBytesLeft; - strSize -= msgBufBytesLeft; - message += msgBufBytesLeft; - } - - status = send ( pClient->sock, pClient->msgBuf, - pClient->nextMsgIndex, 0 ); - if ( status > 0 ) { - unsigned nSent = (unsigned) status; - if ( nSent < pClient->nextMsgIndex ) { - unsigned newNextMsgIndex = pClient->nextMsgIndex - nSent; - memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], - newNextMsgIndex ); - pClient->nextMsgIndex = newNextMsgIndex; - } - else { - pClient->nextMsgIndex = 0u; - } - } - else { - if ( ! pClient->shutdown ) { - char sockErrBuf[64]; - if ( status ) { - epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - } - else { - strcpy ( sockErrBuf, "server initiated disconnect" ); - } - fprintf ( stderr, "log client: lost contact with log server at \"%s\" because \"%s\"\n", - pClient->name, sockErrBuf ); - } - logClientClose ( pClient ); - break; - } + if ( msgBufBytesLeft < strSize && pClient->nextMsgIndex != 0u && pClient->connected) + { + /* buffer is full, thus flush it */ + logClientFlush ( pClient ); + msgBufBytesLeft = sizeof ( pClient->msgBuf ) - pClient->nextMsgIndex; } - else { - memcpy ( & pClient->msgBuf[pClient->nextMsgIndex], - message, strSize ); - pClient->nextMsgIndex += strSize; + if ( msgBufBytesLeft == 0u ) { + fprintf ( stderr, "log client: messages to \"%s\" are lost\n", + pClient->name ); break; } + if ( msgBufBytesLeft > strSize) msgBufBytesLeft = strSize; + memcpy ( & pClient->msgBuf[pClient->nextMsgIndex], + message, msgBufBytesLeft ); + pClient->nextMsgIndex += msgBufBytesLeft; + strSize -= msgBufBytesLeft; + message += msgBufBytesLeft; } } From e000ea491360ae681a42e9f479c65c6ef406f4f1 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 28 Aug 2019 15:15:19 +0200 Subject: [PATCH 05/59] avoid needless memmove calls --- src/libCom/log/logClient.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 90fde98b5..203f29dd3 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -219,6 +219,8 @@ void epicsShareAPI logClientSend ( logClientId id, const char * message ) void epicsShareAPI logClientFlush ( logClientId id ) { + unsigned nSent = 0u; + logClient * pClient = ( logClient * ) id; if ( ! pClient ) { @@ -227,20 +229,11 @@ void epicsShareAPI logClientFlush ( logClientId id ) epicsMutexMustLock ( pClient->mutex ); - while ( pClient->nextMsgIndex && pClient->connected ) { - int status = send ( pClient->sock, pClient->msgBuf, - pClient->nextMsgIndex, 0 ); + while ( nSent < pClient->nextMsgIndex && pClient->connected ) { + int status = send ( pClient->sock, pClient->msgBuf + nSent, + pClient->nextMsgIndex - nSent, 0 ); if ( status > 0 ) { - unsigned nSent = (unsigned) status; - if ( nSent < pClient->nextMsgIndex ) { - unsigned newNextMsgIndex = pClient->nextMsgIndex - nSent; - memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], - newNextMsgIndex ); - pClient->nextMsgIndex = newNextMsgIndex; - } - else { - pClient->nextMsgIndex = 0u; - } + nSent += (unsigned) status; } else { if ( ! pClient->shutdown ) { @@ -258,6 +251,11 @@ void epicsShareAPI logClientFlush ( logClientId id ) break; } } + pClient->nextMsgIndex -= nSent; + if ( nSent > 0 && pClient->nextMsgIndex > 0 ) { + memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], + pClient->nextMsgIndex ); + } epicsMutexUnlock ( pClient->mutex ); } From 1b88e834d6b4468758b02cacf76ea48287306e1c Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 28 Aug 2019 15:23:00 +0200 Subject: [PATCH 06/59] send pending log messages directly after connecting --- src/libCom/log/logClient.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 203f29dd3..743910fb8 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -223,7 +223,7 @@ void epicsShareAPI logClientFlush ( logClientId id ) logClient * pClient = ( logClient * ) id; - if ( ! pClient ) { + if ( ! pClient || ! pClient->connected ) { return; } @@ -419,12 +419,8 @@ static void logClientRestart ( logClientId id ) epicsMutexUnlock ( pClient->mutex ); - if ( isConn ) { - logClientFlush ( pClient ); - } - else { - logClientConnect ( pClient ); - } + if ( ! isConn ) logClientConnect ( pClient ); + logClientFlush ( pClient ); epicsEventWaitWithTimeout ( pClient->shutdownNotify, LOG_RESTART_DELAY); From ad861a06177d6900b6ede951af0ee6bd5796d400 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 28 Aug 2019 15:29:23 +0200 Subject: [PATCH 07/59] no need to delay startup only because log server is currently not available --- src/libCom/log/logClient.c | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 743910fb8..72a2e1364 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -54,7 +54,6 @@ typedef struct { } logClient; static const double LOG_RESTART_DELAY = 5.0; /* sec */ -static const double LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT = 5.0; /* sec */ static const double LOG_SERVER_SHUTDOWN_TIMEOUT = 30.0; /* sec */ /* @@ -438,9 +437,7 @@ static void logClientRestart ( logClientId id ) logClientId epicsShareAPI logClientCreate ( struct in_addr server_addr, unsigned short server_port) { - epicsTimeStamp begin, current; logClient *pClient; - double diff; pClient = calloc (1, sizeof (*pClient)); if (pClient==NULL) { @@ -494,28 +491,6 @@ logClientId epicsShareAPI logClientCreate ( return NULL; } - /* - * attempt to synchronize with circuit connect - */ - epicsTimeGetCurrent ( & begin ); - epicsMutexMustLock ( pClient->mutex ); - do { - epicsMutexUnlock ( pClient->mutex ); - epicsEventWaitWithTimeout ( - pClient->stateChangeNotify, - LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT / 10.0 ); - epicsTimeGetCurrent ( & current ); - diff = epicsTimeDiffInSeconds ( & current, & begin ); - epicsMutexMustLock ( pClient->mutex ); - } - while ( ! pClient->connected && diff < LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT ); - epicsMutexUnlock ( pClient->mutex ); - - if ( ! pClient->connected ) { - fprintf (stderr, "log client create: timed out synchronizing with circuit connect to \"%s\" after %.1f seconds\n", - pClient->name, LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT ); - } - return (void *) pClient; } From af73e4cf6547cdd65c858d48ac0114e6235606c5 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 17 Sep 2019 11:59:02 +0200 Subject: [PATCH 08/59] removed unneeded include --- src/libCom/log/logClient.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 72a2e1364..723f5d4be 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -25,7 +25,6 @@ #include "dbDefs.h" #include "epicsEvent.h" #include "iocLog.h" -#include "errlog.h" #include "epicsMutex.h" #include "epicsThread.h" #include "epicsTime.h" From 9d9840ad1ee58777ba234d29a524f7921f398111 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 17 Sep 2019 14:39:03 +0200 Subject: [PATCH 09/59] improve logClientShow to show unsent bytes on level 2 (and fix level 1) --- src/libCom/log/logClient.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 723f5d4be..9b9e039b1 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -507,14 +507,21 @@ void epicsShareAPI logClientShow (logClientId id, unsigned level) printf ("log client: disconnected from log server at \"%s\"\n", pClient->name); } - if (level>1) { - printf ("log client: sock=%s, connect cycles = %u\n", + if (logClientPrefix) { + printf ("log client: prefix is \"%s\"\n", logClientPrefix); + } + + if (level>0) { + printf ("log client: sock %s, connect cycles = %u\n", pClient->sock==INVALID_SOCKET?"INVALID":"OK", pClient->connectCount); } - - if (logClientPrefix) { - printf ("log client: prefix is \"%s\"\n", logClientPrefix); + if (level>1) { + printf ("log client: %u bytes in buffer\n", pClient->nextMsgIndex); + if (pClient->nextMsgIndex) + printf("-------------------------\n" + "%.*s-------------------------\n", + (int)(pClient->nextMsgIndex), pClient->msgBuf); } } From feb1f9b0df689915d77ce6c8c7a00b0f19e111d7 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 17 Sep 2019 14:41:35 +0200 Subject: [PATCH 10/59] increase error message buffer size for long (Windows) error messges --- src/libCom/log/logClient.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 9b9e039b1..3defa6baf 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -235,7 +235,7 @@ void epicsShareAPI logClientFlush ( logClientId id ) } else { if ( ! pClient->shutdown ) { - char sockErrBuf[64]; + char sockErrBuf[128]; if ( status ) { epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); } @@ -274,7 +274,7 @@ static void logClientMakeSock (logClient *pClient) */ pClient->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, 0 ); if ( pClient->sock == INVALID_SOCKET ) { - char sockErrBuf[64]; + char sockErrBuf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf ( stderr, "log client: no socket error %s\n", @@ -326,7 +326,7 @@ static void logClientConnect (logClient *pClient) } else { if ( pClient->connFailStatus != errnoCpy && ! pClient->shutdown ) { - char sockErrBuf[64]; + char sockErrBuf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf (stderr, @@ -352,7 +352,7 @@ static void logClientConnect (logClient *pClient) optval = TRUE; status = setsockopt (pClient->sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(optval)); if (status<0) { - char sockErrBuf[64]; + char sockErrBuf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf (stderr, "log client: unable to enable keepalive option because \"%s\"\n", sockErrBuf); @@ -364,7 +364,7 @@ static void logClientConnect (logClient *pClient) */ status = shutdown (pClient->sock, SHUT_RD); if (status < 0) { - char sockErrBuf[64]; + char sockErrBuf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf (stderr, "%s:%d shutdown(%d,SHUT_RD) error was \"%s\"\n", @@ -385,7 +385,7 @@ static void logClientConnect (logClient *pClient) lingerval.l_linger = 60*5; status = setsockopt (pClient->sock, SOL_SOCKET, SO_LINGER, (char *) &lingerval, sizeof(lingerval)); if (status<0) { - char sockErrBuf[64]; + char sockErrBuf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf (stderr, "log client: unable to set linger options because \"%s\"\n", sockErrBuf); From 059c3852862ea9fa454c0b8be010b651cddbdd8f Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 17 Sep 2019 17:06:55 +0200 Subject: [PATCH 11/59] use dynamic debug flag for logClient --- src/ioc/misc/dbCore.dbd | 3 +++ src/libCom/log/logClient.c | 22 +++++++++++----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/ioc/misc/dbCore.dbd b/src/ioc/misc/dbCore.dbd index 9d5ae9474..898bb5954 100644 --- a/src/ioc/misc/dbCore.dbd +++ b/src/ioc/misc/dbCore.dbd @@ -25,3 +25,6 @@ variable(callbackParallelThreadsDefault,int) # Real-time operation variable(dbThreadRealtimeLock,int) + +# show logClient network activity +variable(logClientDebug,int) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 3defa6baf..19d79b617 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -21,7 +21,6 @@ #include #include -#define epicsExportSharedSymbols #include "dbDefs.h" #include "epicsEvent.h" #include "iocLog.h" @@ -32,9 +31,13 @@ #include "epicsAssert.h" #include "epicsExit.h" #include "epicsSignal.h" +#include "epicsExport.h" #include "logClient.h" +int logClientDebug = 0; +epicsExportAddress (int, logClientDebug); + typedef struct { char msgBuf[0x4000]; struct sockaddr_in addr; @@ -65,10 +68,10 @@ static char* logClientPrefix = NULL; */ static void logClientClose ( logClient *pClient ) { -# ifdef DEBUG + if (logClientDebug) { fprintf (stderr, "log client: lingering for connection close..."); fflush (stderr); -# endif + } /* * mutex on @@ -90,9 +93,8 @@ static void logClientClose ( logClient *pClient ) */ epicsMutexUnlock (pClient->mutex); -# ifdef DEBUG + if (logClientDebug) fprintf (stderr, "done\n"); -# endif } /* @@ -262,10 +264,10 @@ void epicsShareAPI logClientFlush ( logClientId id ) */ static void logClientMakeSock (logClient *pClient) { - -# ifdef DEBUG + if (logClientDebug) { fprintf (stderr, "log client: creating socket..."); -# endif + fflush (stderr); + } epicsMutexMustLock (pClient->mutex); @@ -283,10 +285,8 @@ static void logClientMakeSock (logClient *pClient) epicsMutexUnlock (pClient->mutex); -# ifdef DEBUG + if (logClientDebug) fprintf (stderr, "done\n"); -# endif - } /* From 765af2efead9c0758929e69cfe42d242a6948400 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 17 Sep 2019 17:34:16 +0200 Subject: [PATCH 12/59] ask logClient socket how many bytes are still in the send queue and don't discard them in case the connection turns out broken. --- src/libCom/log/logClient.c | 83 +++++++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 19d79b617..44eaf7dab 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -49,6 +49,7 @@ typedef struct { epicsEventId shutdownNotify; unsigned connectCount; unsigned nextMsgIndex; + unsigned backlog; unsigned connected; unsigned shutdown; unsigned shutdownConfirm; @@ -194,6 +195,39 @@ static void sendMessageChunk(logClient * pClient, const char * message) { } } +/* + * epicsSockCountUnsentBytes () + * Should go to osd socket support + */ +#if defined (_WIN32) && WINVER >= _WIN32_WINNT_WIN10 +#include +#endif + +static int epicsSockCountUnsentBytes(SOCKET sock) { +#if defined (_WIN32) && WINVER >= _WIN32_WINNT_WIN10 +/* Windows 10 Version 1703 / Server 2016 */ +/* https://docs.microsoft.com/en-us/windows/win32/api/mstcpip/ns-mstcpip-tcp_info_v0 */ + DWORD infoVersion = 0, bytesReturned; + TCP_INFO_v0 tcpInfo; + int status; + if ((status = WSAIoctl(sock, SIO_TCP_INFO, &infoVersion, sizeof(infoVersion), + &tcpInfo, sizeof(tcpInfo), &bytesReturned, NULL, NULL)) == 0) + return tcpInfo.BytesInFlight; +#elif defined (SO_NWRITE) +/* macOS / iOS */ +/* https://www.unix.com/man-page/osx/2/setsockopt/ */ + int unsent; + if (getsockopt(sock, SOL_SOCKET, SO_NWRITE, &unsent) == 0) + return unsent; +#elif defined (TIOCOUTQ) +/* Linux */ +/* https://linux.die.net/man/7/tcp */ + int unsent; + if (ioctl(sock, TIOCOUTQ, &unsent) == 0) + return unsent; +#endif + return 0; +} /* * logClientSend () @@ -219,7 +253,8 @@ void epicsShareAPI logClientSend ( logClientId id, const char * message ) void epicsShareAPI logClientFlush ( logClientId id ) { - unsigned nSent = 0u; + unsigned nSent; + int status = 0; logClient * pClient = ( logClient * ) id; @@ -229,32 +264,32 @@ void epicsShareAPI logClientFlush ( logClientId id ) epicsMutexMustLock ( pClient->mutex ); + nSent = pClient->backlog; while ( nSent < pClient->nextMsgIndex && pClient->connected ) { - int status = send ( pClient->sock, pClient->msgBuf + nSent, + status = send ( pClient->sock, pClient->msgBuf + nSent, pClient->nextMsgIndex - nSent, 0 ); - if ( status > 0 ) { - nSent += (unsigned) status; - } - else { - if ( ! pClient->shutdown ) { - char sockErrBuf[128]; - if ( status ) { - epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - } - else { - strcpy ( sockErrBuf, "server initiated disconnect" ); - } - fprintf ( stderr, "log client: lost contact with log server at \"%s\" because \"%s\"\n", - pClient->name, sockErrBuf ); - } - logClientClose ( pClient ); - break; - } + if ( status < 0 ) break; + nSent += status; } - pClient->nextMsgIndex -= nSent; - if ( nSent > 0 && pClient->nextMsgIndex > 0 ) { - memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], - pClient->nextMsgIndex ); + + if ( status < 0 ) { + if ( ! pClient->shutdown ) { + char sockErrBuf[128]; + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); + fprintf ( stderr, "log client: lost contact with log server at \"%s\" because \"%s\"\n", + pClient->name, sockErrBuf ); + } + pClient->backlog = 0; + logClientClose ( pClient ); + } + else if ( nSent > 0 && pClient->nextMsgIndex > 0 ) { + pClient->backlog = epicsSockCountUnsentBytes ( pClient->sock ); + nSent -= pClient->backlog; + if ( nSent > 0 ) { + memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], + pClient->nextMsgIndex ); + pClient->nextMsgIndex -= nSent; + } } epicsMutexUnlock ( pClient->mutex ); } From 9c18ce007a67800528b927eeaae480a9d50da330 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 17 Sep 2019 17:45:33 +0200 Subject: [PATCH 13/59] cannot print sockets with %d in Windows, they are not small ints but maybe pointers. --- src/libCom/log/logClient.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 44eaf7dab..762703417 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -402,8 +402,8 @@ static void logClientConnect (logClient *pClient) char sockErrBuf[128]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - fprintf (stderr, "%s:%d shutdown(%d,SHUT_RD) error was \"%s\"\n", - __FILE__, __LINE__, pClient->sock, sockErrBuf); + fprintf (stderr, "%s:%d shutdown(sock,SHUT_RD) error was \"%s\"\n", + __FILE__, __LINE__, sockErrBuf); /* not fatal (although it shouldn't happen) */ } From 15f28f11834f188c3708f2652f821f98139e7946 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Wed, 18 Sep 2019 09:58:28 +0200 Subject: [PATCH 14/59] sending 0 bytes helps to detect broken connections on some systems (but is undefined behavior on Linux, fails on vxWorks and is a documented no-op on Windows) --- src/libCom/log/logClient.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 762703417..6671d2785 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -272,6 +272,15 @@ void epicsShareAPI logClientFlush ( logClientId id ) nSent += status; } + if ( pClient->backlog > 0 && status >= 0 ) + { + /* On Linux send 0 bytes can detect EPIPE */ + /* NOOP on Windows, fails on vxWorks */ + errno = 0; + status = send ( pClient->sock, NULL, 0, 0 ); + if (!(errno == ECONNRESET || errno == EPIPE)) status = 0; + } + if ( status < 0 ) { if ( ! pClient->shutdown ) { char sockErrBuf[128]; From a16ce877e7b7991efc8c52787199b1a5e0bf178e Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 19 Sep 2019 08:54:19 +0200 Subject: [PATCH 15/59] fix wrong function name in comment --- src/libCom/log/iocLog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libCom/log/iocLog.c b/src/libCom/log/iocLog.c index 8cb1349a1..c0fa33fa6 100644 --- a/src/libCom/log/iocLog.c +++ b/src/libCom/log/iocLog.c @@ -77,7 +77,7 @@ void epicsShareAPI epicsShareAPI iocLogFlush (void) } /* - * logClientDestroy() + * iocLogClientDestroy() */ static void iocLogClientDestroy (logClientId id) { From 04e752c83aa863dbee5b3d4ccc2fcab62e465c00 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 19 Sep 2019 10:44:36 +0200 Subject: [PATCH 16/59] moved logClientSendMessage and made it static --- src/libCom/log/iocLog.c | 11 +++++++++++ src/libCom/log/logClient.c | 10 ---------- src/libCom/log/logClient.h | 1 - 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libCom/log/iocLog.c b/src/libCom/log/iocLog.c index c0fa33fa6..ba78041c8 100644 --- a/src/libCom/log/iocLog.c +++ b/src/libCom/log/iocLog.c @@ -76,6 +76,16 @@ void epicsShareAPI epicsShareAPI iocLogFlush (void) } } +/* + * logClientSendMessage () + */ +static void logClientSendMessage ( logClientId id, const char * message ) +{ + if ( !iocLogDisable ) { + logClientSend (id, message); + } +} + /* * iocLogClientDestroy() */ @@ -149,3 +159,4 @@ logClientId epicsShareAPI logClientInit (void) { return iocLogClientInit (); } + diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 6671d2785..cd832b60c 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -569,16 +569,6 @@ void epicsShareAPI logClientShow (logClientId id, unsigned level) } } -/* - * logClientSendMessage (); deprecated - */ -void logClientSendMessage ( logClientId id, const char * message ) -{ - if ( !iocLogDisable ) { - logClientSend (id, message); - } -} - /* * iocLogPrefix() */ diff --git a/src/libCom/log/logClient.h b/src/libCom/log/logClient.h index 1797bbb20..3b3f63add 100644 --- a/src/libCom/log/logClient.h +++ b/src/libCom/log/logClient.h @@ -38,7 +38,6 @@ epicsShareFunc void epicsShareAPI iocLogPrefix(const char* prefix); /* deprecated interface; retained for backward compatibility */ /* note: implementations are in iocLog.c, not logClient.c */ epicsShareFunc logClientId epicsShareAPI logClientInit (void); -epicsShareFunc void logClientSendMessage (logClientId id, const char *message); #ifdef __cplusplus } From a5c9db8c8eeb819144d35263263c043d487bd916 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 19 Sep 2019 10:48:14 +0200 Subject: [PATCH 17/59] epicsSockCountUnsentBytes renamed to epicsSocketCountUnsentBytes --- src/libCom/log/logClient.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index cd832b60c..28b933e02 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -196,14 +196,14 @@ static void sendMessageChunk(logClient * pClient, const char * message) { } /* - * epicsSockCountUnsentBytes () + * epicsSocketCountUnsentBytes () * Should go to osd socket support */ #if defined (_WIN32) && WINVER >= _WIN32_WINNT_WIN10 #include #endif -static int epicsSockCountUnsentBytes(SOCKET sock) { +static int epicsSocketCountUnsentBytes(SOCKET sock) { #if defined (_WIN32) && WINVER >= _WIN32_WINNT_WIN10 /* Windows 10 Version 1703 / Server 2016 */ /* https://docs.microsoft.com/en-us/windows/win32/api/mstcpip/ns-mstcpip-tcp_info_v0 */ @@ -292,7 +292,7 @@ void epicsShareAPI logClientFlush ( logClientId id ) logClientClose ( pClient ); } else if ( nSent > 0 && pClient->nextMsgIndex > 0 ) { - pClient->backlog = epicsSockCountUnsentBytes ( pClient->sock ); + pClient->backlog = epicsSocketCountUnsentBytes ( pClient->sock ); nSent -= pClient->backlog; if ( nSent > 0 ) { memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], From 62fb49f93b3e5782eb7ad9c6196499c8e377f30e Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 19 Sep 2019 11:42:04 +0200 Subject: [PATCH 18/59] bugfix: memmove'ed to much --- src/libCom/log/logClient.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 28b933e02..4e647bd83 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -294,10 +294,10 @@ void epicsShareAPI logClientFlush ( logClientId id ) else if ( nSent > 0 && pClient->nextMsgIndex > 0 ) { pClient->backlog = epicsSocketCountUnsentBytes ( pClient->sock ); nSent -= pClient->backlog; - if ( nSent > 0 ) { + pClient->nextMsgIndex -= nSent; + if ( nSent > 0 && pClient->nextMsgIndex > 0 ) { memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], pClient->nextMsgIndex ); - pClient->nextMsgIndex -= nSent; } } epicsMutexUnlock ( pClient->mutex ); From eb8992a750d9b9d9ff4dd4e66835d01972fc2999 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Thu, 19 Sep 2019 12:08:45 +0200 Subject: [PATCH 19/59] epicsSocketCountUnsentBytes returns -1 on failure --- src/libCom/log/logClient.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 4e647bd83..476634c5f 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -226,7 +226,7 @@ static int epicsSocketCountUnsentBytes(SOCKET sock) { if (ioctl(sock, TIOCOUTQ, &unsent) == 0) return unsent; #endif - return 0; + return -1; } /* @@ -292,8 +292,11 @@ void epicsShareAPI logClientFlush ( logClientId id ) logClientClose ( pClient ); } else if ( nSent > 0 && pClient->nextMsgIndex > 0 ) { - pClient->backlog = epicsSocketCountUnsentBytes ( pClient->sock ); - nSent -= pClient->backlog; + int backlog = epicsSocketCountUnsentBytes ( pClient->sock ); + if (backlog >= 0) { + pClient->backlog = backlog; + nSent -= backlog; + } pClient->nextMsgIndex -= nSent; if ( nSent > 0 && pClient->nextMsgIndex > 0 ) { memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], From 39e8ccdef4d7d569e77fdfb465a0c151604ba703 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 23 Sep 2019 10:54:17 +0200 Subject: [PATCH 20/59] fix bug from commit f85454. Apparently epicsExportSharedSymbols is needed even though epicsExport.h is included --- src/libCom/log/logClient.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 476634c5f..ee92c27c4 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -21,6 +21,7 @@ #include #include +#define epicsExportSharedSymbols #include "dbDefs.h" #include "epicsEvent.h" #include "iocLog.h" From 6f193242e07afe99c06d60e3da33654a3d57df3b Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Mon, 23 Sep 2019 11:10:32 +0200 Subject: [PATCH 21/59] renamed epicsSocketCountUnsentBytes to epicsSocketUnsentCount and moved it to osi/os/ --- src/libCom/log/logClient.c | 36 +------------------ src/libCom/osi/Makefile | 1 + src/libCom/osi/os/Darwin/osdSockUnsentCount.c | 17 +++++++++ src/libCom/osi/os/Linux/osdSockUnsentCount.c | 18 ++++++++++ src/libCom/osi/os/WIN32/osdSockUnsentCount.c | 25 +++++++++++++ .../osi/os/default/osdSockUnsentCount.c | 14 ++++++++ src/libCom/osi/os/iOS/osdSockUnsentCount.c | 17 +++++++++ src/libCom/osi/osiSock.h | 6 ++++ 8 files changed, 99 insertions(+), 35 deletions(-) create mode 100644 src/libCom/osi/os/Darwin/osdSockUnsentCount.c create mode 100644 src/libCom/osi/os/Linux/osdSockUnsentCount.c create mode 100644 src/libCom/osi/os/WIN32/osdSockUnsentCount.c create mode 100644 src/libCom/osi/os/default/osdSockUnsentCount.c create mode 100644 src/libCom/osi/os/iOS/osdSockUnsentCount.c diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index ee92c27c4..73664ff76 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -196,40 +196,6 @@ static void sendMessageChunk(logClient * pClient, const char * message) { } } -/* - * epicsSocketCountUnsentBytes () - * Should go to osd socket support - */ -#if defined (_WIN32) && WINVER >= _WIN32_WINNT_WIN10 -#include -#endif - -static int epicsSocketCountUnsentBytes(SOCKET sock) { -#if defined (_WIN32) && WINVER >= _WIN32_WINNT_WIN10 -/* Windows 10 Version 1703 / Server 2016 */ -/* https://docs.microsoft.com/en-us/windows/win32/api/mstcpip/ns-mstcpip-tcp_info_v0 */ - DWORD infoVersion = 0, bytesReturned; - TCP_INFO_v0 tcpInfo; - int status; - if ((status = WSAIoctl(sock, SIO_TCP_INFO, &infoVersion, sizeof(infoVersion), - &tcpInfo, sizeof(tcpInfo), &bytesReturned, NULL, NULL)) == 0) - return tcpInfo.BytesInFlight; -#elif defined (SO_NWRITE) -/* macOS / iOS */ -/* https://www.unix.com/man-page/osx/2/setsockopt/ */ - int unsent; - if (getsockopt(sock, SOL_SOCKET, SO_NWRITE, &unsent) == 0) - return unsent; -#elif defined (TIOCOUTQ) -/* Linux */ -/* https://linux.die.net/man/7/tcp */ - int unsent; - if (ioctl(sock, TIOCOUTQ, &unsent) == 0) - return unsent; -#endif - return -1; -} - /* * logClientSend () */ @@ -293,7 +259,7 @@ void epicsShareAPI logClientFlush ( logClientId id ) logClientClose ( pClient ); } else if ( nSent > 0 && pClient->nextMsgIndex > 0 ) { - int backlog = epicsSocketCountUnsentBytes ( pClient->sock ); + int backlog = epicsSocketUnsentCount ( pClient->sock ); if (backlog >= 0) { pClient->backlog = backlog; nSent -= backlog; diff --git a/src/libCom/osi/Makefile b/src/libCom/osi/Makefile index e05aec37d..00685d8bc 100644 --- a/src/libCom/osi/Makefile +++ b/src/libCom/osi/Makefile @@ -86,6 +86,7 @@ endif Com_SRCS += osdSock.c Com_SRCS += osdSockAddrReuse.cpp +Com_SRCS += osdSockUnsentCount.c Com_SRCS += osiSock.c Com_SRCS += systemCallIntMech.cpp Com_SRCS += epicsSocketConvertErrnoToString.cpp diff --git a/src/libCom/osi/os/Darwin/osdSockUnsentCount.c b/src/libCom/osi/os/Darwin/osdSockUnsentCount.c new file mode 100644 index 000000000..00ef550bd --- /dev/null +++ b/src/libCom/osi/os/Darwin/osdSockUnsentCount.c @@ -0,0 +1,17 @@ +/*************************************************************************\ +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "osiSock.h" + +/* + * epicsSocketUnsentCount () + * See https://www.unix.com/man-page/osx/2/setsockopt + */ +int epicsSocketUnsentCount(SOCKET sock) { + int unsent; + if (getsockopt(sock, SOL_SOCKET, SO_NWRITE, &unsent) == 0) + return unsent; + return -1; +} diff --git a/src/libCom/osi/os/Linux/osdSockUnsentCount.c b/src/libCom/osi/os/Linux/osdSockUnsentCount.c new file mode 100644 index 000000000..6f6cbf0fe --- /dev/null +++ b/src/libCom/osi/os/Linux/osdSockUnsentCount.c @@ -0,0 +1,18 @@ +/*************************************************************************\ +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include "osiSock.h" + +/* + * epicsSocketUnsentCount () + * See https://linux.die.net/man/7/tcp + */ +int epicsSocketUnsentCount(SOCKET sock) { + int unsent; + if (ioctl(sock, SIOCOUTQ, &unsent) == 0) + return unsent; + return -1; +} diff --git a/src/libCom/osi/os/WIN32/osdSockUnsentCount.c b/src/libCom/osi/os/WIN32/osdSockUnsentCount.c new file mode 100644 index 000000000..c2045bc79 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdSockUnsentCount.c @@ -0,0 +1,25 @@ +/*************************************************************************\ +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "osiSock.h" +#include + +/* + * epicsSocketUnsentCount () + * See https://docs.microsoft.com/en-us/windows/win32/api/mstcpip/ns-mstcpip-tcp_info_v0 + */ +int epicsSocketUnsentCount(SOCKET sock) { +#if defined (_WIN32) && WINVER >= _WIN32_WINNT_WIN10 +/* Windows 10 Version 1703 / Server 2016 */ + DWORD infoVersion = 0, bytesReturned; + TCP_INFO_v0 tcpInfo; + int status; + if ((status = WSAIoctl(sock, SIO_TCP_INFO, &infoVersion, sizeof(infoVersion), + &tcpInfo, sizeof(tcpInfo), &bytesReturned, NULL, NULL)) == 0) + return tcpInfo.BytesInFlight; +#endif + return -1; +} diff --git a/src/libCom/osi/os/default/osdSockUnsentCount.c b/src/libCom/osi/os/default/osdSockUnsentCount.c new file mode 100644 index 000000000..61094c710 --- /dev/null +++ b/src/libCom/osi/os/default/osdSockUnsentCount.c @@ -0,0 +1,14 @@ +/*************************************************************************\ +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "osiSock.h" + +/* + * epicsSocketUnsentCount () + */ +int epicsSocketUnsentCount(SOCKET sock) { + /* not implemented */ + return -1; +} diff --git a/src/libCom/osi/os/iOS/osdSockUnsentCount.c b/src/libCom/osi/os/iOS/osdSockUnsentCount.c new file mode 100644 index 000000000..00ef550bd --- /dev/null +++ b/src/libCom/osi/os/iOS/osdSockUnsentCount.c @@ -0,0 +1,17 @@ +/*************************************************************************\ +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "osiSock.h" + +/* + * epicsSocketUnsentCount () + * See https://www.unix.com/man-page/osx/2/setsockopt + */ +int epicsSocketUnsentCount(SOCKET sock) { + int unsent; + if (getsockopt(sock, SOL_SOCKET, SO_NWRITE, &unsent) == 0) + return unsent; + return -1; +} diff --git a/src/libCom/osi/osiSock.h b/src/libCom/osi/osiSock.h index 061619e89..e1c2de881 100644 --- a/src/libCom/osi/osiSock.h +++ b/src/libCom/osi/osiSock.h @@ -52,6 +52,12 @@ enum epicsSocketSystemCallInterruptMechanismQueryInfo { epicsShareFunc enum epicsSocketSystemCallInterruptMechanismQueryInfo epicsSocketSystemCallInterruptMechanismQuery (); +/* + * Some systems (e.g Linux and Windows 10) allow to check the amount + * of unsent data in the output queue. + * Returns -1 if the information is not available. + */ +epicsShareFunc int epicsSocketUnsentCount(SOCKET sock); /* * convert socket address to ASCII in this order From d2d8674cb9925ab72bef5d59fccf5cd4fbb28026 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Fri, 4 Oct 2019 14:32:07 +0200 Subject: [PATCH 22/59] use EPICS_PRIVATE_API macro and fix bug with darwin/ios --- src/libCom/log/logClient.c | 1 + src/libCom/osi/os/Darwin/osdSockUnsentCount.c | 4 +++- src/libCom/osi/os/Linux/osdSockUnsentCount.c | 1 + src/libCom/osi/os/WIN32/osdSockUnsentCount.c | 1 + src/libCom/osi/os/default/osdSockUnsentCount.c | 1 + src/libCom/osi/os/iOS/osdSockUnsentCount.c | 4 +++- src/libCom/osi/osiSock.h | 2 ++ 7 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c index 73664ff76..9a09ef7b7 100644 --- a/src/libCom/log/logClient.c +++ b/src/libCom/log/logClient.c @@ -21,6 +21,7 @@ #include #include +#define EPICS_PRIVATE_API #define epicsExportSharedSymbols #include "dbDefs.h" #include "epicsEvent.h" diff --git a/src/libCom/osi/os/Darwin/osdSockUnsentCount.c b/src/libCom/osi/os/Darwin/osdSockUnsentCount.c index 00ef550bd..20bd82b14 100644 --- a/src/libCom/osi/os/Darwin/osdSockUnsentCount.c +++ b/src/libCom/osi/os/Darwin/osdSockUnsentCount.c @@ -3,6 +3,7 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ +#define EPICS_PRIVATE_API #include "osiSock.h" /* @@ -11,7 +12,8 @@ */ int epicsSocketUnsentCount(SOCKET sock) { int unsent; - if (getsockopt(sock, SOL_SOCKET, SO_NWRITE, &unsent) == 0) + socklen_t len = sizeof(unsent); + if (getsockopt(sock, SOL_SOCKET, SO_NWRITE, &unsent, &len) == 0) return unsent; return -1; } diff --git a/src/libCom/osi/os/Linux/osdSockUnsentCount.c b/src/libCom/osi/os/Linux/osdSockUnsentCount.c index 6f6cbf0fe..3c0a8f915 100644 --- a/src/libCom/osi/os/Linux/osdSockUnsentCount.c +++ b/src/libCom/osi/os/Linux/osdSockUnsentCount.c @@ -4,6 +4,7 @@ \*************************************************************************/ #include +#define EPICS_PRIVATE_API #include "osiSock.h" /* diff --git a/src/libCom/osi/os/WIN32/osdSockUnsentCount.c b/src/libCom/osi/os/WIN32/osdSockUnsentCount.c index c2045bc79..fe68ead01 100644 --- a/src/libCom/osi/os/WIN32/osdSockUnsentCount.c +++ b/src/libCom/osi/os/WIN32/osdSockUnsentCount.c @@ -4,6 +4,7 @@ \*************************************************************************/ #define epicsExportSharedSymbols +#define EPICS_PRIVATE_API #include "osiSock.h" #include diff --git a/src/libCom/osi/os/default/osdSockUnsentCount.c b/src/libCom/osi/os/default/osdSockUnsentCount.c index 61094c710..ef01e9b24 100644 --- a/src/libCom/osi/os/default/osdSockUnsentCount.c +++ b/src/libCom/osi/os/default/osdSockUnsentCount.c @@ -3,6 +3,7 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ +#define EPICS_PRIVATE_API #include "osiSock.h" /* diff --git a/src/libCom/osi/os/iOS/osdSockUnsentCount.c b/src/libCom/osi/os/iOS/osdSockUnsentCount.c index 00ef550bd..20bd82b14 100644 --- a/src/libCom/osi/os/iOS/osdSockUnsentCount.c +++ b/src/libCom/osi/os/iOS/osdSockUnsentCount.c @@ -3,6 +3,7 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ +#define EPICS_PRIVATE_API #include "osiSock.h" /* @@ -11,7 +12,8 @@ */ int epicsSocketUnsentCount(SOCKET sock) { int unsent; - if (getsockopt(sock, SOL_SOCKET, SO_NWRITE, &unsent) == 0) + socklen_t len = sizeof(unsent); + if (getsockopt(sock, SOL_SOCKET, SO_NWRITE, &unsent, &len) == 0) return unsent; return -1; } diff --git a/src/libCom/osi/osiSock.h b/src/libCom/osi/osiSock.h index e1c2de881..6e3b053c5 100644 --- a/src/libCom/osi/osiSock.h +++ b/src/libCom/osi/osiSock.h @@ -52,12 +52,14 @@ enum epicsSocketSystemCallInterruptMechanismQueryInfo { epicsShareFunc enum epicsSocketSystemCallInterruptMechanismQueryInfo epicsSocketSystemCallInterruptMechanismQuery (); +#ifdef EPICS_PRIVATE_API /* * Some systems (e.g Linux and Windows 10) allow to check the amount * of unsent data in the output queue. * Returns -1 if the information is not available. */ epicsShareFunc int epicsSocketUnsentCount(SOCKET sock); +#endif /* * convert socket address to ASCII in this order From 81550ac4d3b532ca78824ae48059e7ef4e3d6f01 Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Thu, 14 Nov 2019 10:11:16 -0500 Subject: [PATCH 23/59] Fix segfault when calling dbLoadRecords after iocInit This fixes lp:1829919. --- src/ioc/db/dbAccess.c | 15 ++++++++++++--- src/ioc/dbStatic/dbLexRoutines.c | 10 ++++++++++ src/ioc/dbStatic/dbStaticIocRegister.c | 8 ++++++++ src/ioc/dbStatic/dbStaticPvt.h | 1 + 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 4ee2d677d..0b893eeb9 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -730,9 +730,18 @@ int dbLoadDatabase(const char *file, const char *path, const char *subs) int dbLoadRecords(const char* file, const char* subs) { int status = dbReadDatabase(&pdbbase, file, 0, subs); - - if (!status && dbLoadRecordsHook) - dbLoadRecordsHook(file, subs); + switch(status) + { + case 0: + if(dbLoadRecordsHook) + dbLoadRecordsHook(file, subs); + break; + case -2: + errlogPrintf("dbLoadRecords: failed to load %s - cannot load records after running iocBuild!\n", file); + break; + default: + errlogPrintf("dbLoadRecords: failed to load %s\n", file); + } return status; } diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c index 3df3c7f3a..9bc0f8722 100644 --- a/src/ioc/dbStatic/dbLexRoutines.c +++ b/src/ioc/dbStatic/dbLexRoutines.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "dbDefs.h" #include "dbmf.h" @@ -115,6 +116,12 @@ typedef struct tempListNode { static ELLLIST tempList = ELLLIST_INIT; static void *freeListPvt = NULL; static int duplicate = FALSE; +static bool dbLoadRecordsAllowed = true; + +void disableDbLoadRecords() +{ + dbLoadRecordsAllowed = false; +} static void yyerrorAbort(char *str) { @@ -215,6 +222,9 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, char *penv; char **macPairs; + if(!dbLoadRecordsAllowed) + return -2; + if(*ppdbbase == 0) *ppdbbase = dbAllocBase(); pdbbase = *ppdbbase; if(path && strlen(path)>0) { diff --git a/src/ioc/dbStatic/dbStaticIocRegister.c b/src/ioc/dbStatic/dbStaticIocRegister.c index 18d346c70..7156691ac 100644 --- a/src/ioc/dbStatic/dbStaticIocRegister.c +++ b/src/ioc/dbStatic/dbStaticIocRegister.c @@ -8,12 +8,19 @@ \*************************************************************************/ #include "iocsh.h" +#include "initHooks.h" #define epicsExportSharedSymbols #include "dbStaticIocRegister.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" +static void dbStaticIocRegisterInitHook(initHookState state) +{ + if(state == initHookAtIocBuild) + disableDbLoadRecords(); +} + /* common arguments */ static const iocshArg argPdbbase = { "pdbbase", iocshArgPdbbase}; @@ -153,6 +160,7 @@ static void dbReportDeviceConfigCallFunc(const iocshArgBuf *args) void dbStaticIocRegister(void) { + initHookRegister(dbStaticIocRegisterInitHook); iocshRegister(&dbDumpPathFuncDef, dbDumpPathCallFunc); iocshRegister(&dbDumpRecordFuncDef, dbDumpRecordCallFunc); iocshRegister(&dbDumpMenuFuncDef, dbDumpMenuCallFunc); diff --git a/src/ioc/dbStatic/dbStaticPvt.h b/src/ioc/dbStatic/dbStaticPvt.h index 842c0dc21..62e595e02 100644 --- a/src/ioc/dbStatic/dbStaticPvt.h +++ b/src/ioc/dbStatic/dbStaticPvt.h @@ -25,6 +25,7 @@ dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry); void dbFreeLinkContents(struct link *plink); void dbFreePath(DBBASE *pdbbase); int dbIsMacroOk(DBENTRY *pdbentry); +void disableDbLoadRecords(); /*The following routines have different versions for run-time no-run-time*/ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName); From 6767bcd31e2335e13fc1f3e548fe54b122d5527b Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Thu, 14 Nov 2019 13:57:45 -0500 Subject: [PATCH 24/59] Use accessor in iocInit This is simpler than using init hooks. --- src/ioc/dbStatic/dbLexRoutines.c | 10 ++-------- src/ioc/dbStatic/dbStaticIocRegister.c | 8 -------- src/ioc/dbStatic/dbStaticPvt.h | 1 - src/ioc/misc/iocInit.c | 9 ++++++--- src/ioc/misc/iocInit.h | 5 +++++ 5 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c index 9bc0f8722..b5f94674f 100644 --- a/src/ioc/dbStatic/dbLexRoutines.c +++ b/src/ioc/dbStatic/dbLexRoutines.c @@ -16,7 +16,6 @@ #include #include #include -#include #include "dbDefs.h" #include "dbmf.h" @@ -27,6 +26,7 @@ #include "freeList.h" #include "gpHash.h" #include "macLib.h" +#include "iocInit.h" #define epicsExportSharedSymbols #include "dbBase.h" @@ -116,12 +116,6 @@ typedef struct tempListNode { static ELLLIST tempList = ELLLIST_INIT; static void *freeListPvt = NULL; static int duplicate = FALSE; -static bool dbLoadRecordsAllowed = true; - -void disableDbLoadRecords() -{ - dbLoadRecordsAllowed = false; -} static void yyerrorAbort(char *str) { @@ -222,7 +216,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, char *penv; char **macPairs; - if(!dbLoadRecordsAllowed) + if(getIocState() != iocVirgin) return -2; if(*ppdbbase == 0) *ppdbbase = dbAllocBase(); diff --git a/src/ioc/dbStatic/dbStaticIocRegister.c b/src/ioc/dbStatic/dbStaticIocRegister.c index 7156691ac..18d346c70 100644 --- a/src/ioc/dbStatic/dbStaticIocRegister.c +++ b/src/ioc/dbStatic/dbStaticIocRegister.c @@ -8,19 +8,12 @@ \*************************************************************************/ #include "iocsh.h" -#include "initHooks.h" #define epicsExportSharedSymbols #include "dbStaticIocRegister.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" -static void dbStaticIocRegisterInitHook(initHookState state) -{ - if(state == initHookAtIocBuild) - disableDbLoadRecords(); -} - /* common arguments */ static const iocshArg argPdbbase = { "pdbbase", iocshArgPdbbase}; @@ -160,7 +153,6 @@ static void dbReportDeviceConfigCallFunc(const iocshArgBuf *args) void dbStaticIocRegister(void) { - initHookRegister(dbStaticIocRegisterInitHook); iocshRegister(&dbDumpPathFuncDef, dbDumpPathCallFunc); iocshRegister(&dbDumpRecordFuncDef, dbDumpRecordCallFunc); iocshRegister(&dbDumpMenuFuncDef, dbDumpMenuCallFunc); diff --git a/src/ioc/dbStatic/dbStaticPvt.h b/src/ioc/dbStatic/dbStaticPvt.h index 62e595e02..842c0dc21 100644 --- a/src/ioc/dbStatic/dbStaticPvt.h +++ b/src/ioc/dbStatic/dbStaticPvt.h @@ -25,7 +25,6 @@ dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry); void dbFreeLinkContents(struct link *plink); void dbFreePath(DBBASE *pdbbase); int dbIsMacroOk(DBENTRY *pdbentry); -void disableDbLoadRecords(); /*The following routines have different versions for run-time no-run-time*/ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName); diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index a45770d9e..93d8908a8 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -70,9 +70,7 @@ #include "registryRecordType.h" #include "rsrv.h" -static enum { - iocVirgin, iocBuilding, iocBuilt, iocRunning, iocPaused, iocStopped -} iocState = iocVirgin; +static enum iocStateEnum iocState = iocVirgin; static enum { buildRSRV, buildIsolated } iocBuildMode; @@ -91,6 +89,11 @@ static void exitDatabase(void *dummy); int dbThreadRealtimeLock = 1; epicsExportAddress(int, dbThreadRealtimeLock); +enum iocStateEnum getIocState(void) +{ + return iocState; +} + /* * Initialize EPICS on the IOC. */ diff --git a/src/ioc/misc/iocInit.h b/src/ioc/misc/iocInit.h index 24ae45e06..3e711d6c5 100644 --- a/src/ioc/misc/iocInit.h +++ b/src/ioc/misc/iocInit.h @@ -13,10 +13,15 @@ #include "shareLib.h" +enum iocStateEnum { + iocVirgin, iocBuilding, iocBuilt, iocRunning, iocPaused, iocStopped +}; + #ifdef __cplusplus extern "C" { #endif +epicsShareFunc enum iocStateEnum getIocState(void); epicsShareFunc int iocInit(void); epicsShareFunc int iocBuild(void); epicsShareFunc int iocBuildIsolated(void); From a50b850ebdb4ef7ebb2906e090513bc618085a18 Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Fri, 15 Nov 2019 09:33:15 -0500 Subject: [PATCH 25/59] Fix mingw cross-build --- src/ioc/dbStatic/dbLexRoutines.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c index b5f94674f..2be7b0d0c 100644 --- a/src/ioc/dbStatic/dbLexRoutines.c +++ b/src/ioc/dbStatic/dbLexRoutines.c @@ -26,7 +26,6 @@ #include "freeList.h" #include "gpHash.h" #include "macLib.h" -#include "iocInit.h" #define epicsExportSharedSymbols #include "dbBase.h" @@ -36,6 +35,7 @@ #include "epicsExport.h" #include "link.h" #include "special.h" +#include "iocInit.h" From cbe6173417d323f61be3fa163aa101ec0b02c386 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 8 Jan 2020 17:19:25 -0600 Subject: [PATCH 26/59] Updates to the subArrayRecord reference page --- src/std/rec/subArrayRecord.dbd.pod | 55 +++++++++++++++--------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/src/std/rec/subArrayRecord.dbd.pod b/src/std/rec/subArrayRecord.dbd.pod index c47f2d003..086ca583d 100644 --- a/src/std/rec/subArrayRecord.dbd.pod +++ b/src/std/rec/subArrayRecord.dbd.pod @@ -49,7 +49,7 @@ L
for information on specifying links. In addition, the DTYP field must specify a device support module. Currently, the -only device support module is C<<< Soft Channel >>>. +only device support module is C. =fields INP, DTYP @@ -59,13 +59,13 @@ These parameters determine the number of array elements (the array length) and the data type of those elements. The Field Type of Value (FTVL) field determines the data type of the array. -The user specifies the maximum number of elements allowed in the subarray in the -MALM field. Generally, the number should be equal to the number of elements of -the Waveform array (found in the Waveform's NELM field). The MALM field is used -to allocate memory. The subArray's Number of Elements (NELM) field is where the -user specifies the actual number of elements that the subArray will contain. It -should of course be no greater than MALM; if it is, the record processing -routine sets it equal to MALM. +The user specifies the maximum number of elements that can be read into the +subarray in the MALM field. This number should normally be equal to the number +of elements of the Waveform array (found in the Waveform's NELM field). The MALM +field is used to allocate memory. The subArray's Number of Elements (NELM) field +is where the user specifies the actual number of elements that the subArray will +extract. It should of course be no greater than MALM; if it is, the record +processing routine sets it equal to MALM. The INDX field determines the offset of the subArray record's array in relation to the Waveform's. For instance, if INDX is 2, then the subArray will read NELM @@ -83,15 +83,15 @@ display the value and other parameters of the subarray record either textually or graphically. EGU is a string of up to 16 characters describing the engineering units (if any) -of the values which the subArray holds. It is retrieved by the C<<< get_units ->>> record support routine. +of the values which the subArray holds. It is retrieved by the C +record support routine. The HOPR and LOPR fields set the upper and lower display limits for the -sub-array elements. Both the C<<< get_graphic_double >>> and C<<< -get_control_double >>> record support routines retrieve these fields. +sub-array elements. Both the C and C +record support routines retrieve these fields. The PREC field determines the floating point precision with which to display -VAL. It is used whenever the C<<< get_precision >>> record support routine is +VAL. It is used whenever the C record support routine is called. See L @@ -110,9 +110,9 @@ record types. These fields are not configurable by the user. They are used for the record's internal processing or to represent the current state of the record. -The NORD field holds a counter of the number of elements read into the array. It -can be less than NELM even after the array is full if NELM exceeds the number of -existing elements in the referenced array, i.e., the Waveform's array. +The NORD field holds the number of elements that were actually read into the +array. It will be less than NELM whenever the sum of the NELM and INDX fields +exceeds the number of existing elements found in the source array. BPTR contains a pointer to the record's array. @@ -150,14 +150,14 @@ See L. long (*cvt_dbaddr)(struct dbAddr *paddr) -This is called by dbNameToAddr. It makes the dbAddr structure refer to the +This is called by C. It makes the dbAddr structure refer to the actual buffer holding the result. =head4 get_array_info long (*get_array_info)(struct dbAddr *paddr, long *no_elements, long *offset) -Retrieves NELM. +Retrieves NORD. =head4 put_array_info @@ -171,14 +171,14 @@ Sets NORD. For the elements in the array, this routine routines HOPR and LOPR. For the INDX field, this routine returns MALM - 1 and 0. For NELM, it returns MALM and 1. For -other fields, it calls C<<< recGblGetGraphicDouble() >>>. +other fields, it calls C. =head4 get_control_double long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p) -For array elements, this routine retrieves HOPR and LOPR. Otherwise, C<<< -recGblGetControlDouble() >>> is called. +For array elements, this routine retrieves HOPR and LOPR. Otherwise, +C is called. =head4 get_units @@ -212,13 +212,13 @@ INDX is greater than or equal to MALM it is set to MALM-1. =item 3. -Call device support read routine. This routine is expected to place the desired -sub-array at the beginning of the buffer and set NORD to the number of elements -of the sub-array that were read. +Call the device support's C routine. This routine is expected to +place the desired sub-array at the beginning of the buffer and set NORD to the +number of elements of the sub-array that were read. =item 4. -If PACT has been changed to TRUE, the device support read routine has started +If PACT has been changed to TRUE, the device support read operation has started but has not completed writing the new value. In this case, the processing routine merely returns, leaving PACT TRUE. Otherwise, process sets PACT TRUE at this time. This asynchronous processing logic is not currently used but has been @@ -305,12 +305,11 @@ sub-array were acquired. =head3 Device Support For Soft Records -Only the device support module C<<< Soft Channel >>> is currently provided. The -INP link type must be either DB_LINK or CA_LINK. +Only the device support module C is currently provided. =head4 Soft Channel -INP is expected to point to a waveform record. +INP is expected to point to an array field of a waveform record or similar. =cut From 54cd7e7ba12726564795beadb15217ad2d9b8b67 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 8 Jan 2020 17:21:04 -0600 Subject: [PATCH 27/59] MinGW: Replace -Wno-format with -D__USE_MINGW_ANSI_STDIO --- configure/os/CONFIG.Common.win32-x86-mingw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure/os/CONFIG.Common.win32-x86-mingw b/configure/os/CONFIG.Common.win32-x86-mingw index cb3f53978..7c03781b0 100644 --- a/configure/os/CONFIG.Common.win32-x86-mingw +++ b/configure/os/CONFIG.Common.win32-x86-mingw @@ -30,7 +30,7 @@ ARCH_DEP_LDFLAGS += -m32 # Compiler does not define __unix __unix__ unix # Override for -DUNIX from CONFIG.Common.UnixCommon -OP_SYS_CPPFLAGS = -D_MINGW -Wno-format +OP_SYS_CPPFLAGS = -D_MINGW -D__USE_MINGW_ANSI_STDIO EXE = .exe RES = .coff From dbd6f7e8079bb158b9b30eaec6a60d0327752d6a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 23 Jan 2020 14:26:33 -0600 Subject: [PATCH 28/59] Adding tests for epicsThreadClass API Two tests are disabled which hang the parent in the epicsThread destuctor --- src/libCom/test/Makefile | 5 + src/libCom/test/epicsThreadClassTest.cpp | 210 +++++++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 src/libCom/test/epicsThreadClassTest.cpp diff --git a/src/libCom/test/Makefile b/src/libCom/test/Makefile index c80339317..58fd4556d 100755 --- a/src/libCom/test/Makefile +++ b/src/libCom/test/Makefile @@ -107,6 +107,11 @@ epicsThreadTest_SRCS += epicsThreadTest.cpp testHarness_SRCS += epicsThreadTest.cpp TESTS += epicsThreadTest +TESTPROD_HOST += epicsThreadClassTest +epicsThreadClassTest_SRCS += epicsThreadClassTest.cpp +testHarness_SRCS += epicsThreadClassTest.cpp +TESTS += epicsThreadClassTest + TESTPROD_HOST += epicsThreadOnceTest epicsThreadOnceTest_SRCS += epicsThreadOnceTest.c testHarness_SRCS += epicsThreadOnceTest.c diff --git a/src/libCom/test/epicsThreadClassTest.cpp b/src/libCom/test/epicsThreadClassTest.cpp new file mode 100644 index 000000000..1dba5ee26 --- /dev/null +++ b/src/libCom/test/epicsThreadClassTest.cpp @@ -0,0 +1,210 @@ +/*************************************************************************\ +* Copyright (c) 2020 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* epicsThreadClassTest.cpp */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsAssert.h" +#include "epicsThread.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +/* Key to the char's that define the test case actions: + * + * Upper case letters are for parent thread actions + * B - Parent calls thread->start() and waits for child to start + * D - Parent deletes thread. This waits for child to return if it hasn't yet + * E - Parent calls thread->exitWait(), this may wait for child to return + * S - Parent sleeps for SLEEP_TIME seconds + * T - Parent sends sync trigger to child (w) + * W - Parent waits for sync trigger from child (t) + * X - Parent calls thread->exitWait(0) + * + * Lower case letters are for child thread actions + * d - Child deletes thread + * e - Child calls thread->exitWait() + * r - Child returns + * s - Child sleeps for SLEEP_TIME seconds + * t - Child sends sync trigger to parent (W) + * w - Child waits for sync trigger from child (T) + * + * Note that it is possible to write test cases that can hang, + * segfault, or that trigger errors from thread APIs. + */ + +// The test cases + +const char * const cases[] = { + // These cases don't start the thread: + "D", // Parent deletes thread + "ED", // Parent does exitWait(), deletes thread + + // In these cases the parent deletes the thread + "BrSD", // Child returns; parent deletes thread + "BsDr", // Parent deletes thread; child returns + "BrSED", // Child returns; parent does exitWait(), deletes thread + "BsErD", // Parent does exitWait(); child returns; parent deletes thread + "BsXDr", // Parent does exitWait(0); parent deletes thread; child returns + "BwXTDsr", // Parent does exitWait(0); parent deletes thread; child returns + // These are currently broken +// "BetWSrD", // Child does exitWait(); sync; child returns; parent deletes thread +// "BetWsDr", // Child does exitWait(); sync; parent deletes thread; child returns + + // In these cases the child deletes the thread + "BdrS", // Child deletes thread, returns + "BedrS", // Child does exitWait(), deletes thread, returns + "BwXTSdr", // Parent does exitWait(0); sync; child deletes thread, returns + + NULL // Terminator +}; + +// How long to sleep for while the other thread works +#define SLEEP_TIME 1.0 + +class threadCase: public epicsThreadRunable { +public: + threadCase(const char * const tcase); + virtual ~threadCase(); + virtual void run(); + epicsThread *pthread; + epicsEvent startEvt; + epicsEvent childEvt; + epicsEvent parentEvt; +private: + const char * const name; +}; + +threadCase::threadCase(const char * const tcase) : + pthread(new epicsThread(*this, tcase, + epicsThreadGetStackSize(epicsThreadStackSmall))), + name(tcase) +{ + testDiag("Constructing test case '%s'", name); +} + +threadCase::~threadCase() +{ + testDiag("Destroying test case '%s'", name); +} + +void threadCase::run() +{ + testDiag("Child running for '%s'", name); + startEvt.signal(); + + for (const char * pdo = name; + const char tdo = *pdo; + pdo++) + { + switch (tdo) + { + case 'd': + testDiag("'%c': Child deleting epicsThread", tdo); + delete pthread; + pthread = NULL; + break; + + case 'e': + testDiag("'%c': Child calling exitWait()", tdo); + assert(pthread); + pthread->exitWait(); + break; + + case 's': + testDiag("'%c': Child sleeping", tdo); + epicsThreadSleep(SLEEP_TIME); + break; + + case 't': + testDiag("'%c': Child sending trigger", tdo); + parentEvt.signal(); + break; + + case 'w': + testDiag("'%c': Child awaiting trigger", tdo); + childEvt.wait(); + break; + + case 'r': + testDiag("'%c': Child returning", tdo); + return; + } + } + testFail("Test case '%s' is missing 'r'", name); +} + +MAIN(epicsThreadClassTest) +{ + const int ntests = NELEMENTS(cases); + testPlan(ntests - 1); // The last element is the NULL terminator + + for (const char * const * pcase = cases; + const char * const tcase = *pcase; + pcase++) + { + testDiag("======= Test case '%s' =======", tcase); + threadCase thrCase(tcase); + + for (const char * pdo = tcase; + const char tdo = *pdo; + pdo++) + { + switch (tdo) + { + case 'B': + testDiag("'%c': Parent starting child", tdo); + assert(thrCase.pthread); + thrCase.pthread->start(); + thrCase.startEvt.wait(); + break; + + case 'D': + testDiag("'%c': Parent deleting epicsThread", tdo); + assert(thrCase.pthread); + delete thrCase.pthread; + thrCase.pthread = NULL; + break; + + case 'E': + testDiag("'%c': Parent calling exitWait()", tdo); + assert(thrCase.pthread); + thrCase.pthread->exitWait(); + break; + + case 'X': + testDiag("'%c': Parent calling exitWait(0)", tdo); + assert(thrCase.pthread); + thrCase.pthread->exitWait(0); + break; + + case 'S': + testDiag("'%c': Parent sleeping", tdo); + epicsThreadSleep(SLEEP_TIME); + break; + + case 'T': + testDiag("'%c': Parent sending trigger", tdo); + thrCase.childEvt.signal(); + break; + + case 'W': + testDiag("'%c': Parent awaiting trigger", tdo); + thrCase.parentEvt.wait(); + break; + } + } + + testPass("Test case '%s' passed", tcase); + } + + return testDone(); +} From 0db8f8ca1ba16cadf9b0ef3c11c5f455bf9a37ed Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 12 Feb 2020 07:45:04 -0600 Subject: [PATCH 29/59] Rename histogramRecord.dbd to .dbd.pod --- src/std/rec/{histogramRecord.dbd => histogramRecord.dbd.pod} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/std/rec/{histogramRecord.dbd => histogramRecord.dbd.pod} (100%) diff --git a/src/std/rec/histogramRecord.dbd b/src/std/rec/histogramRecord.dbd.pod similarity index 100% rename from src/std/rec/histogramRecord.dbd rename to src/std/rec/histogramRecord.dbd.pod From 7a612f952457338b709a7c0d43cbb7acb49c1c57 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 12 Feb 2020 09:25:54 -0600 Subject: [PATCH 30/59] Update to stringout POD from Rolf Keitel Fix spelling of OMSL. Document the stdio device support. --- src/std/rec/stringoutRecord.dbd.pod | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/std/rec/stringoutRecord.dbd.pod b/src/std/rec/stringoutRecord.dbd.pod index 6034641bc..773f0a7bf 100644 --- a/src/std/rec/stringoutRecord.dbd.pod +++ b/src/std/rec/stringoutRecord.dbd.pod @@ -48,7 +48,7 @@ explains how these fields are used. The string output record must specify from where it gets its desired output string. The first field that determines where the desired output originates is -the output mode select (OSML) field, which can have two possible value: C<<< +the output mode select (OMSL) field, which can have two possible value: C<<< closed_loop >>> or C<<< supervisory >>>. If C<<< supervisory >>> is specified, DOL is ignored, the current value of VAL is written, and the VAL can be changed externally via dbPuts at run-time. If C<<< closed_loop >>> is specified, the VAL @@ -80,7 +80,7 @@ for information on specifying links. menu(menuOmsl) } -=head3 Write Parameters +=head3 Output Specification The output link specified in the OUT field specifies where the string output record is to write its string. The link can be a database or channel access @@ -360,6 +360,10 @@ C. write_so calls recGblPutLinkValue to write the current value of VAL. See L. +Device support for DTYP C is provided for writing values to the stdout, stderr, or errlog streams. +C addressing C<@stdout>, C<@stderr> or C<@errlog> is used on the OUT link field to select the desired +stream. + =cut } From a5bae49dabda6981ee1c6b7a24998efa78de0947 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 13 Feb 2020 04:58:06 -0600 Subject: [PATCH 31/59] Rename lsi, lso and printf *Record.dbd to .dbd.pod --- src/std/rec/{lsiRecord.dbd => lsiRecord.dbd.pod} | 0 src/std/rec/{lsoRecord.dbd => lsoRecord.dbd.pod} | 0 src/std/rec/{printfRecord.dbd => printfRecord.dbd.pod} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename src/std/rec/{lsiRecord.dbd => lsiRecord.dbd.pod} (100%) rename src/std/rec/{lsoRecord.dbd => lsoRecord.dbd.pod} (100%) rename src/std/rec/{printfRecord.dbd => printfRecord.dbd.pod} (100%) diff --git a/src/std/rec/lsiRecord.dbd b/src/std/rec/lsiRecord.dbd.pod similarity index 100% rename from src/std/rec/lsiRecord.dbd rename to src/std/rec/lsiRecord.dbd.pod diff --git a/src/std/rec/lsoRecord.dbd b/src/std/rec/lsoRecord.dbd.pod similarity index 100% rename from src/std/rec/lsoRecord.dbd rename to src/std/rec/lsoRecord.dbd.pod diff --git a/src/std/rec/printfRecord.dbd b/src/std/rec/printfRecord.dbd.pod similarity index 100% rename from src/std/rec/printfRecord.dbd rename to src/std/rec/printfRecord.dbd.pod From bfd289e85f4931999b78ee2faaba5a104daa13ac Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 13 Feb 2020 05:11:36 -0600 Subject: [PATCH 32/59] Add converted histogram POD --- src/std/rec/histogramRecord.dbd.pod | 275 +++++++++++++++++++++++++++- 1 file changed, 274 insertions(+), 1 deletion(-) diff --git a/src/std/rec/histogramRecord.dbd.pod b/src/std/rec/histogramRecord.dbd.pod index 075400fc6..a459e08d9 100644 --- a/src/std/rec/histogramRecord.dbd.pod +++ b/src/std/rec/histogramRecord.dbd.pod @@ -6,14 +6,141 @@ # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* + +=title Histogram Record (histogram) + +The histogram record is used to store frequency counts of a signal into an array +of arbitrary length. The user can configure the range of the signal value that +the array will store. Anything outside this range will be ignored. + +=head2 Parameter Fields + +The record-specific fields are described below. + +=recordtype histogram + +=cut + menu(histogramCMD) { choice(histogramCMD_Read,"Read") choice(histogramCMD_Clear,"Clear") choice(histogramCMD_Start,"Start") choice(histogramCMD_Stop,"Stop") } + recordtype(histogram) { - include "dbCommon.dbd" + +=head3 Read Parameters + +The SVL is the input link where the record reads its value. It can be a +constant, a database link, or a channel access link. If SVL is a database or +channel access link, then SGNL is read from SVL. If SVL is a constant, then SGNL +is initialized with the constant value but can be changed via dbPuts. The C device support module can be specified in the DTYP field. + +The ULIM and LLIM fields determine the usable range of signal values. Any value +of SGNL below LLIM or above ULIM is outside the range and will not be stored in +the array. In the NELM field the user must specify the array size, e.g., the +number of array elements. Each element in the NELM field holds the counts for an +interval of the range of signal counts, the range specified by ULIM and LLIM. +These intervals are determined by dividing the range by NELM: + + (ULIM - LLIM) / NELM. + +=fields SVL, SGNL, DTYP, NELM, ULIM, LLIM + +=head3 Operator Display Parameters + +These parameters are used to present meaningful data to the operator. These +fields are used to display the value and other parameters of the histogram +either textually or graphically. See L for +more on the record name (NAME) and description (DESC) fields. + +=fields NAME, DESC + +=head3 Alarm Parameters + +The Histogram record has the alarm parameters common to all record types. +L lists other fields related to a alarms that are common to all +record types. + +=head3 Monitor Parameters + +The MDEL field implements the monitor count deadband. Only when MCNT is greater +than the value given to MDEL are monitors triggered, MCNT being the number of +counts since the last time the record was processed. If MDEL is -1, everytime +the record is processed, a monitor is triggered regardless. + +If SDEL is greater than 0, it causes a callback routine to be called. The number +specified in SDEL is the callback routines interval. The callback routine is +called every SDEL seconds. The callback routine posts an event if MCNT is +greater than 0. + +=fields MDEL, SDEL + +=head3 Run-time and Simulation Mode Parameters + +These parameters are used by the run-time code for processing the histogram. +They are not configurable by the user prior to run-time. They represent the +current state of the record. Many of them are used to process the histogram more +efficiently. + +The BPTR field contains a pointer to the unsigned long array of frequency +values. The VAL field references this array as well. However, the BPTR field is +not accessible at run-time. + +The MCNT field keeps counts the number of signal counts since the last monitor +was invoked. + +The collections controls field (CMD) is a menu field with five choices: + +=menu histogramCMD + +When CMD is C, the record retrieves its values and adds them to the signal +array. This command will first clear the signal counts which have already been +read when it is first invoked. + +The C command erases the signal counts, setting the elements in the array +back to zero. Afterwards, the CMD field is set back to C. + +The C command simply causes the record to read signal values into the +array. Unlike C, it doesn't clear the array first. + +The C command disables the reading of signal values into the array. + +The C command waits until the C or C command has been issued +to start counting. + +The CSTA or collections status field implements the CMD field choices by +enabling or disabling the reading of values into the histogram array. While +FALSE, no signals are added to the array. While TRUE, signals are read and added +to the array. The field is initialized to TRUE. The C command is the only +command that sets CSTA to FALSE. On the other hand, the C command is the +only command that sets it to TRUE. Thus, C must be invoked after each +C command in order to enable counting; invoking C will not enable +signal counting after C has been invoked. + +A typical use of these fields would be to initialize the CMD field to C +(it is initialized to this command by default), to use the C command to +disable counting when necessary, after which the C command can be invoked +to re-start the signal count. + +The WDTH field is a private field that holds the signal width of the array +elements. For instance, if the LLIM was configured to be 4.0 and ULIM was +configured to be 12.0 and the NELM was set to 4, then the WDTH for each array +would be 2. Thus, it is (ULIM - LLIM) / NELM. + +=fields BPTR, VAL, MCNT, CMD, CSTA, WDTH + +The following fields are used to operate the histogram record in simulation +mode. See L for more information on the +simulation mode fields. + +=fields SIOL, SVAL, SIML, SIMM, SIMS + +=cut + + include "dbCommon.dbd" field(VAL,DBF_NOACCESS) { prompt("Value") asl(ASL0) @@ -141,6 +268,152 @@ recordtype(histogram) { interest(1) prop(YES) } + +=head2 Record Support + +=head3 Record Support Routines + +=head4 init_record + +Using NELM, space for the unsigned long array is allocated and the width WDTH of +the array is calculated. + +This routine initializes SIMM with the value of SIML if SIML type is CONSTANT +link or creates a channel access link if SIML type is PV_LINK. SVAL is likewise +initialized if SIOL is CONSTANT or PV_LINK. + +This routine next checks to see that device support and a device support read +routine are available. If device support includes C, it is +called. + +=head4 process + +See next section. + +=head4 special + +Special is invoked whenever the fields CMD, SGNL, ULIM, or LLIM are changed. + +If SGNL is changed, add_count is called. + +If ULIM or LLIM are changed, WDTH is recalculated and clear_histogram is called. + +If CMD is less or equal to 1, clear_histogram is called and CMD is reset to 0. +If CMD is 2, CSTA is set to TRUE and CMD is reset to 0. If CMD is 3, CSTA is set +to FALSE and CMD is reset to 0. + +clear_histogram zeros out the histogram array. add_count increments the +frequency in the histogram array. + +=head4 cvt_dbaddr + +This is called by dbNameToAddr. It makes the dbAddr structure refer to the +actual buffer holding the array. + +=head4 get_array_info + +Obtains values from the array referenced by VAL. + +=head4 put_array_info + +Writes values into the array referenced by VAL. + +=head3 Record Processing + +Routine process implements the following algorithm: + +=over + +=item 1. + +Check to see that the appropriate device support module exists. If it doesn't, +an error message is issued and processing is terminated with the PACT field set +to TRUE. This ensures that processes will no longer be called for this record. +Thus error storms will not occur. + +=item 2. + +readValue is called. See L for more information + +=item 3. + +If PACT has been changed to TRUE, the device support read routine has started +but has not completed writing the new value. In this case, the processing +routine merely returns, leaving PACT TRUE. + +=item 4. + +Add count to histogram array. + +=item 5. + +Check to see if monitors should be invoked. Alarm monitors are invoked if the +alarm status or severity has changed. Archive and value change monitors are +invoked if MDEL conditions are met. NSEV and NSTA are reset to 0. + +=item 6. + +Scan forward link if necessary, set PACT and INIT to FALSE, and return. + +=back + +=head2 Device Support + +=head3 Fields Of Interest To Device Support + +The device support routines are primarily interested in the following fields: + +=fields PACT, DPVT, UDF, NSEV, NSTA, SVL, SGNL + +=head3 Device Support Routines + +Device support consists of the following routines: + +=head4 long report(int level) + +This optional routine is called by the IOC command C and is passed the +report level that was requested by the user. +It should print a report on the state of the device support to stdout. +The C parameter may be used to output increasingly more detailed +information at higher levels, or to select different types of information with +different levels. +Level zero should print no more than a small summary. + +=head4 long init(int after) + +This optional routine is called twice at IOC initialization time. +The first call happens before any of the C calls are made, with +the integer parameter C set to 0. +The second call happens after all of the C calls have been made, +with C set to 1. + +=head4 init_record + + init_record(precord) + +This routine is called by the record support C routine. It makes +sure that SGNL is a CONSTANT, PV_LINK, DB_LINK, or CA_LINK. It also retrieves a +value for SVL from SGNL. If SGNL is none of the above, an error is generated. + +=head4 read_histogram + + read_histogram(*precord) + +This routine is called by the record support routines. It retrieves a value for +SVL from SGNL. + +=head3 Device Support For Soft Records + +Only the device support module C is currently provided, though +other device support modules may be provided at the user's site. + +=head4 Soft Channel + +The C device support routine retrieves a value from SGNL. SGNL +must be CONSTANT, PV_LINK, DB_LINK, or CA_LINK. + +=cut + } variable(histogramSDELprecision, int) From eb8ca227044a04e025f183b9d22262a532d9d48c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 13 Feb 2020 08:59:17 -0600 Subject: [PATCH 33/59] Add new POD documentation, from Rolf Keitel Documents the lsi, lso and printf record types. --- src/std/rec/lsiRecord.dbd.pod | 119 +++++++++++++++++ src/std/rec/lsoRecord.dbd.pod | 162 ++++++++++++++++++++++- src/std/rec/printfRecord.dbd.pod | 216 ++++++++++++++++++++++++++++++- 3 files changed, 495 insertions(+), 2 deletions(-) diff --git a/src/std/rec/lsiRecord.dbd.pod b/src/std/rec/lsiRecord.dbd.pod index c50d905d8..bb8b5d712 100644 --- a/src/std/rec/lsiRecord.dbd.pod +++ b/src/std/rec/lsiRecord.dbd.pod @@ -5,7 +5,44 @@ # in file LICENSE that is included with this distribution. #************************************************************************* + +=title Long String Input Record (lsi) + +The long string input record is used to retrieve an arbitrary ASCII string with +a maximum length of 65535 characters. + +=head2 Parameter Fields + +The record-specific fields are described below, grouped by functionality. + +=recordtype lsi + +=cut + recordtype(lsi) { + +=head3 Scan Parameters + +The long string input record has the standard fields for specifying under what +circumstances it will be processed. These fields are listed in L. +In addition, L explains how these fields are used. + +=head3 Input Specification + +The INP field determines where the long string input record obtains its string +from. It can be a database or channel access link, or a constant. If constant, +the VAL field is initialized with the constant and can be changed via dbPuts. +Otherwise, the string is read from the specified location each time the record +is processed and placed in the VAL field. The maximum number of characters in +VAL is given by SIZV, and cannot be larger than 65535. In addition, the +appropriate device support module must be entered into the DTYP field. + +See L
for information on specifying links. + +=fields VAL, OVAL, SIZV, INP, DTYP + +=cut + include "dbCommon.dbd" %#include "devSup.h" % @@ -25,12 +62,18 @@ recordtype(lsi) { pp(TRUE) special(SPC_DBADDR) extra("char *val") + #=type STRING[SIZV] + #=read Yes + #=write Yes } field(OVAL,DBF_NOACCESS) { prompt("Old Value") special(SPC_DBADDR) interest(3) extra("char *oval") + #=type STRING[SIZV] + #=read Yes + #=write No } field(SIZV,DBF_USHORT) { prompt("Size of buffers") @@ -52,6 +95,18 @@ recordtype(lsi) { promptgroup("40 - Input") interest(1) } + +=head3 Monitor Parameters + +These parameters are used to specify when the monitor post should be sent by the +C routine. There are two possible choices: + +APST is used for archiver monitors and MPST for all other type of monitors. + +=fields MPST, APST + +=cut + field(MPST,DBF_MENU) { prompt("Post Value Monitors") promptgroup("80 - Display") @@ -64,6 +119,37 @@ recordtype(lsi) { interest(1) menu(menuPost) } + +=head3 Operator Display Parameters + +See L for more on the record name (NAME) and +description (DESC) fields. + +=fields NAME, DESC + +=head3 Alarm Parameters + +The long string input record has the alarm parameters common to all record +types. L lists other fields related to a alarms that are common to +all record types. + +=head3 Run-time and Simulation Mode Parameters + +The old value field (OVAL) of the long string input record is used to implement +value change monitors for VAL. If VAL is not equal to OVAL, then monitors are +triggered. LEN contains the length of the string in VAL, OLEN contains the +length of the string in OVAL. + +=fields OVAL, LEN, OLEN + + +The following fields are used to operate the string input in the simulation +mode. See L for more information on simulation mode fields. + +=fields SIOL, SIML, SIMM, SIMS + +=cut + field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") @@ -86,3 +172,36 @@ recordtype(lsi) { interest(2) } } + + + +=head2 Device Support Interface + +The record requires device support to provide an entry table (dset) which +defines the following members: + + typedef struct { + long number; + long (*report)(int level); + long (*init)(int after); + long (*init_record)(lsiRecord *prec); + long (*get_ioint_info)(int cmd, lsiRecord *prec, IOSCANPVT *piosl); + long (*read_string)(lsiRecord *prec); + } lsidset; + +The module must set C to at least 5, and provide a pointer to its +C routine; the other function pointers may be C if their +associated functionality is not required for this support layer. +Most device supports also provide an C routine to configure the +record instance and connect it to the hardware or driver support layer. + +=head2 Device Support for Soft Records + +A device support module for DTYP C is provided for retrieving +values from other records or other software components. + +Device support for DTYP C is provided for retrieving strings from +environment variables. C addressing C<< @ >> is +used on the C link field to select the desired environment variable. + +=cut diff --git a/src/std/rec/lsoRecord.dbd.pod b/src/std/rec/lsoRecord.dbd.pod index 69203f2d0..63aa4c515 100644 --- a/src/std/rec/lsoRecord.dbd.pod +++ b/src/std/rec/lsoRecord.dbd.pod @@ -5,8 +5,110 @@ # in file LICENSE that is included with this distribution. #************************************************************************* + +=title Long String Output Record (lso) + +The long string output record is used to write an arbitrary ASCII string with a +maximum length of 65535 characters. + +=head2 Parameter Fields + +The record-specific fields are described below, grouped by functionality. + +=recordtype lso + +=cut + +include "menuIvoa.dbd" + recordtype(lso) { - include "dbCommon.dbd" + +=head3 Scan Parameters + +The long string output record has the standard fields for specifying under what +circumstances it will be processed. These fields are listed in L. +In addition, L explains how these fields are used. + +=head3 Desired Output Parameters + +The long string output record must specify from where it gets its desired output +string. The first field that determines where the desired output originates is +the output mode select (OMSL) field, which can have two possible value: +C or C. If C is specified, DOL is +ignored, the current value of VAL is written, and VAL can be changed 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 field (DOL) +which can be either a database link or a channel access link. + +The maximum number of characters in VAL is given by SIZV, and cannot be larger +than 65535. + +DOL can also be a constant in addition to a link, in which case VAL is +initialized to the constant value. Your string constant, however, may be +interpreted as a CA link name. If you want to initialize your string output +record, it is therefore best to use the VAL field. Note that if DOL is a +constant, OMSL cannot be C. + +See L
for information on specifying links. + +=fields VAL, SIZV, DOL, OMSL + +=head3 Output Specification + +The output link specified in the OUT field specifies where the long string +output record is to write its string. The link can be a database or channel +access link. If the OUT field is a constant, no output will be written. + +See L
for information on specifying links. + +In addition, the appropriate device support module must be entered into the DTYP +field. + + +=fields OUT, DTYP + +=head3 Monitor Parameters + +These parameters are used to specify when the monitor post should be sent by the +C routine. There are two possible choices: + +APST is used for archiver monitors and MPST for all other type of monitors. + +=fields MPST, APST + + +=head3 Operator Display Parameters + +See L for more on the record name (NAME) and +description (DESC) fields. + +=fields NAME, DESC + +=head3 Alarm Parameters + +The long string input record has the alarm parameters common to all record +types. L lists other fields related to a alarms that are common to +all record types. + +The IVOA field specifies an action to take when the INVALID alarm is triggered. +There are three possible actions: + +=head4 Menu menuIvoa + +=menu menuIvoa + +When C<<< Set output to IVOV >>>, the value contained in the IVOV field is +written to the output link during an alarm condition. See +L +for more information on the IVOA and IVOV fields. +L +lists other fields related to a alarms that are common to all record types. + +=fields IVOA, IVOV + +=cut + + include "dbCommon.dbd" %#include "devSup.h" % %/* Declare Device Support Entry Table */ @@ -25,12 +127,18 @@ recordtype(lso) { pp(TRUE) special(SPC_DBADDR) extra("char *val") + #=type STRING[SIZV] + #=read Yes + #=write Yes } field(OVAL,DBF_NOACCESS) { prompt("Previous Value") special(SPC_DBADDR) interest(3) extra("char *oval") + #=type STRING[SIZV] + #=read Yes + #=write No } field(SIZV,DBF_USHORT) { prompt("Size of buffers") @@ -88,6 +196,24 @@ recordtype(lso) { interest(1) menu(menuPost) } + + +=head3 Run-time and Simulation Mode Parameters + +The old value field (OVAL) of the long string input record is used to implement +value change monitors for VAL. If VAL is not equal to OVAL, then monitors are +triggered. LEN contains the length of the string in VAL, OLEN contains the +length of the string in OVAL. + +=fields OVAL, LEN, OLEN + +The following fields are used to operate the string input in the simulation +mode. See L for more information on simulation mode fields. + +=fields SIOL, SIML, SIMM, SIMS + +=cut + field(SIML,DBF_INLINK) { prompt("Sim Mode link") promptgroup("90 - Simulate") @@ -110,3 +236,37 @@ recordtype(lso) { interest(1) } } + + + +=head2 Device Support Interface + +The record requires device support to provide an entry table (dset) which +defines the following members: + + typedef struct { + long number; + long (*report)(int level); + long (*init)(int after); + long (*init_record)(lsoRecord *prec); + long (*get_ioint_info)(int cmd, lsoRecord *prec, IOSCANPVT *piosl); + long (*write_string)(lsoRecord *prec); + } lsodset; + +The module must set C to at least 5, and provide a pointer to its +C routine; the other function pointers may be C if their +associated functionality is not required for this support layer. +Most device supports also provide an C routine to configure the +record instance and connect it to the hardware or driver support layer. + + +=head2 Device Support for Soft Records + +Device support for DTYP C is provided for writing values to other +records or other software components. + +Device support for DTYP C is provided for writing values to the stdout, +stderr, or errlog streams. C addressing C<@stdout>, C<@stderr> or +C<@errlog> is used on the OUT link field to select the desired stream. + +=cut diff --git a/src/std/rec/printfRecord.dbd.pod b/src/std/rec/printfRecord.dbd.pod index 4fd63ef3c..3f4c859c1 100644 --- a/src/std/rec/printfRecord.dbd.pod +++ b/src/std/rec/printfRecord.dbd.pod @@ -2,10 +2,174 @@ # Copyright (c) 2012 UChicago Argonne LLC, as Operator of Argonne # National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. +# in file LICENSE that is included with this distribution. #************************************************************************* + +=title Printf Record (printf) + +The printf record is used to generate and write a string using a format +specification and parameters, analogous to the C C function. + +=head2 Parameter Fields + +The record-specific fields are described below, grouped by functionality. + +=recordtype printf + +=cut + recordtype(printf) { + +=head3 Scan Parameters + +The printf record has the standard fields for specifying under what +circumstances it will be processed. These fields are listed in L. +In addition, L explains how these fields are used. + +=head3 String Generation Parameters + +The printf record must specify the desired output string with embedded format +specifiers in the FMT field. Plain characters are copied directly to the output +string. A pair of percent characters 'C<%%>' are converted into a single percent +character in the output string. A single precent character 'C<%>' introduces a +format specifier and is followed by zero or more of the standard C +format flags and modifiers: + +=over 4 + +=item * + +Plus ('C<+>') + +=item * + +Minus ('C<->') + +=item * + +Space ('C< >') + +=item * + +Hash ('C<#>') + +=item * + +Minimum Field Width (decimal digits or 'C<*>') + +=item * + +Precision ('C<.>' followed by decimal digits or 'C<*>') + +=item * + +Length Modifier 'C' E Reads link as DBR_CHAR or DBR_UCHAR + +=item * + +Length Modifier 'C' E Reads link as DBR_SHORT or DBR_USHORT for +integer conversions, DBR_FLOAT for floating-point conversions. + +=item * + +Length Modifier 'C' E Reads link as DBR_LONG or DBR_ULONG for integer +conversions, array of DBR_CHAR for string conversion. + +=back + + +The following character specifies the conversion to perform, see your operating +system's C documentation for more details. These conversions +ultimately call the C routine for the actual string conversion +process, so are subject to the behaviour of that routine. + +=over 4 + +=item * + +'C' E Convert to a character. Only single byte characters are +permitted. + +=item * + +'C' or 'C' E Convert to a decimal integer. + +=item * + +'C' E Convert to an unsigned octal integer. + +=item * + +'C' E Convert to an unsigned decimal integer. + +=item * + +'C' E Convert to an unsigned hexadecimal integer, using C. + +=item * + +'C' E Convert to an unsigned hexadecimal integer, using C. + +=item * + +'C' or 'C' E Convert to floating-point in exponent style, reading +the link as DBR_DOUBLE or DBR_FLOAT. + +=item * + +'C' or 'C' E Convert to floating-point in fixed-point style, +reading the link as DBR_DOUBLE or DBR_FLOAT. + +=item * + +'C' or 'C' E Convert to floating-point in general style, reading +the link as DBR_DOUBLE or DBR_FLOAT. + +=item * + +'C' E Insert string, reading the link as DBR_STRING or array of +DBR_CHAR. + +=back + +The fields INP0 ... INP9 are input links that provide the parameter values to be +formatted into the output. The format specifiers in the FMT string determine +which type of the data is requested through the appropriate input link. As with +C a C<*> character may be used in the format to specify width and/or +precision instead of numeric literals, in which case additional input links are +used to provide the necessary integer parameter or parameters. See L
for information on specifying links. + +The formatted string is written to the VAL field. The maximum number of +characters in VAL is given by SIZV, and cannot be larger than 65535. The LEN +field contains the length of the formatted string in the VAL field. + +=fields FMT, INP0, INP1, INP2, INP3, INP4, INP5, INP6, INP7, INP8, INP9, VAL, SIZV, LEN + + +=head3 Output Specification + +The output link specified in the OUT field specifies where the printf record is +to write the contents of its VAL field. The link can be a database or channel +access link. If the OUT field is a constant, no output will be written. + +See L
for information on specifying links. + +In addition, the appropriate device support module must be entered into the DTYP +field. + +=fields OUT, DTYP + +=head3 Operator Display Parameters + +See L for more on the record name (NAME) and +description (DESC) fields. + +=fields NAME, DESC + +=cut + include "dbCommon.dbd" %#include "devSup.h" % @@ -25,6 +189,9 @@ recordtype(printf) { pp(TRUE) special(SPC_DBADDR) extra("char *val") + #=type STRING[SIZV] + #=read Yes + #=write Yes } field(SIZV,DBF_USHORT) { prompt("Size of VAL buffer") @@ -48,6 +215,21 @@ recordtype(printf) { pp(TRUE) size(81) } + + +=head3 Alarm Parameters + +The printf record has the alarm parameters common to all record types. +L lists other fields related to a alarms that are common to all +record types. + +The IVLS field specifies a string which is sent to the OUT link if if input +link data are invalid. + +=fields IVLS + +=cut + field(IVLS,DBF_STRING) { prompt("Invalid Link String") promptgroup("30 - Action") @@ -107,3 +289,35 @@ recordtype(printf) { %/* Number of INPx fields defined */ %#define PRINTF_NLINKS 10 } + + +=head2 Device Support Interface + +The record requires device support to provide an entry table (dset) which +defines the following members: + + typedef struct { + long number; + long (*report)(int level); + long (*init)(int after); + long (*init_record)(printfRecord *prec); + long (*get_ioint_info)(int cmd, printfRecord *prec, IOSCANPVT *piosl); + long (*write_string)(printfRecord *prec); + } printfdset; + +The module must set C to at least 5, and provide a pointer to its +C routine; the other function pointers may be C if their +associated functionality is not required for this support layer. +Most device supports also provide an C routine to configure the +record instance and connect it to the hardware or driver support layer. + +=head2 Device Support for Soft Records + +A soft device support module Soft Channel is provided for writing values to +other records or other software components. + +Device support for DTYP C is provided for writing values to the stdout, +stderr, or errlog streams. C addressing C<@stdout>, C<@stderr> or +C<@errlog> is used on the OUT link field to select the desired stream. + +=cut From 1d9e9ff4f70027b72b4e74e1be5758948bd61493 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 13 Feb 2020 09:06:48 -0600 Subject: [PATCH 34/59] Add new POD output files to RecordReference index --- documentation/RecordReference.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/documentation/RecordReference.md b/documentation/RecordReference.md index 941d6d97b..469c39626 100644 --- a/documentation/RecordReference.md +++ b/documentation/RecordReference.md @@ -15,13 +15,17 @@ The following documentation for the record types and menus include with Base was * [Data Fanout Record (dfanout)](dfanoutRecord.html) * [Event Record (event)](eventRecord.html) * [Fanout Record (fanout)](fanoutRecord.html) +* [Histogram Record (histogram)](histogramRecord.html) * [Long Input Record (longin)](longinRecord.html) * [Long Output Record (longout)](longoutRecord.html) +* [Long String Input Record (lsi)](lsiRecord.html) +* [Long String Output Record (lso)](lsoRecord.html) * [Multi-Bit Binary Input Direct Record (mbbiDirect)](mbbiDirectRecord.html) * [Multi-Bit Binary Input Record (mbbi)](mbbiRecord.html) * [Multi-Bit Binary Output Direct Record (mbboDirect)](mbboDirectRecord.html) * [Multi-Bit Binary Output Record (mbbo)](mbboRecord.html) * [Permissive Record (permissive)](permissiveRecord.html) +* [Printf Record (prinf)](printfRecord.html) * [Select Record (sel)](selRecord.html) * [Sequence Record (seq)](seqRecord.html) * [State Record (state)](stateRecord.html) From 6867f973465329656dc333a307af6f42e46f7adc Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Fri, 14 Feb 2020 01:16:09 +0000 Subject: [PATCH 35/59] Fix bit operations failures on VS2019 32bit Working with Dirk Zimoch @dirk.zimoch, fixed various issues with bit operations on VS2019 32bit. These seem to relate to handling bit 31 of a 32 bit number. As EPICS << is an arithmetic bit shift, following Java we have added <<< and >>> operators for logical shifts Though it is on a different architecture, this looks like a similar issue to LP: #1838792 --- src/libCom/calc/calcPerform.c | 61 ++++++++++++++++++++----------- src/libCom/calc/postfix.c | 12 ++++-- src/libCom/calc/postfixPvt.h | 6 ++- src/libCom/test/epicsCalcTest.cpp | 37 +++++++++++++++++-- 4 files changed, 84 insertions(+), 32 deletions(-) diff --git a/src/libCom/calc/calcPerform.c b/src/libCom/calc/calcPerform.c index c0f4aebb8..1d675cffc 100644 --- a/src/libCom/calc/calcPerform.c +++ b/src/libCom/calc/calcPerform.c @@ -33,7 +33,7 @@ static int cond_search(const char **ppinst, int match); #endif /* Turn off global optimization for 64-bit MSVC builds */ -#if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW) +#if 0 && defined(_WIN32) && defined(_M_X64) && !defined(_MINGW) # pragma optimize("g", off) #endif @@ -48,7 +48,6 @@ epicsShareFunc long double *ptop; /* stack pointer */ double top; /* value from top of stack */ epicsInt32 itop; /* integer from top of stack */ - epicsUInt32 utop; /* unsigned integer from top of stack */ int op; int nargs; @@ -287,30 +286,37 @@ epicsShareFunc long *ptop = ! *ptop; break; - /* For bitwise operations on values with bit 31 set, double values - * must first be cast to unsigned to correctly set that bit; the - * double value must be negative in that case. The result must be - * cast to a signed integer before converting to the double result. + /* Be VERY careful converting double to int in case bit 31 is set! + * Out-of-range errors give very different results on different sytems. + * Convert negative doubles to signed and positive doubles to unsigned + * first to avoid overflows if bit 32 is set. + * The result is always signed, values with bit 31 set are negative + * to avoid problems when writing the value to signed integer fields + * like longout.VAL or ao.RVAL. However unsigned fields may give + * problems on some architectures. (Fewer than giving problems with + * signed integer. Maybe the conversion functions should handle + * overflows better.) */ + #define d2i(x) ((x)<0?(epicsInt32)(x):(epicsInt32)(epicsUInt32)(x)) + #define d2ui(x) ((x)<0?(epicsUInt32)(epicsInt32)(x):(epicsUInt32)(x)) case BIT_OR: - utop = *ptop--; - *ptop = (epicsInt32) ((epicsUInt32) *ptop | utop); + top = *ptop--; + *ptop = (double)(d2i(*ptop) | d2i(top)); break; case BIT_AND: - utop = *ptop--; - *ptop = (epicsInt32) ((epicsUInt32) *ptop & utop); + top = *ptop--; + *ptop = (double)(d2i(*ptop) & d2i(top)); break; case BIT_EXCL_OR: - utop = *ptop--; - *ptop = (epicsInt32) ((epicsUInt32) *ptop ^ utop); + top = *ptop--; + *ptop = (double)(d2i(*ptop) ^ d2i(top)); break; case BIT_NOT: - utop = *ptop; - *ptop = (epicsInt32) ~utop; + *ptop = (double)~d2i(*ptop); break; /* The shift operators use signed integers, so a right-shift will @@ -318,14 +324,24 @@ epicsShareFunc long * double-casting through unsigned here is important, see above. */ - case RIGHT_SHIFT: - utop = *ptop--; - *ptop = ((epicsInt32) (epicsUInt32) *ptop) >> (utop & 31); + case RIGHT_SHIFT_ARITH: + top = *ptop--; + *ptop = (double)(d2i(*ptop) >> (d2i(top) & 31)); break; - case LEFT_SHIFT: - utop = *ptop--; - *ptop = ((epicsInt32) (epicsUInt32) *ptop) << (utop & 31); + case LEFT_SHIFT_ARITH: + top = *ptop--; + *ptop = (double)(d2i(*ptop) << (d2i(top) & 31)); + break; + + case RIGHT_SHIFT_LOGIC: + top = *ptop--; + *ptop = (double)(d2ui(*ptop) >> (d2ui(top) & 31u)); + break; + + case LEFT_SHIFT_LOGIC: + top = *ptop--; + *ptop = (double)(d2ui(*ptop) << (d2ui(top) & 31u)); break; case NOT_EQ: @@ -382,11 +398,12 @@ epicsShareFunc long *presult = *ptop; return 0; } -#if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW) +#if 0 && defined(_WIN32) && defined(_M_X64) && !defined(_MINGW) # pragma optimize("", on) #endif - + + epicsShareFunc long calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores) { diff --git a/src/libCom/calc/postfix.c b/src/libCom/calc/postfix.c index 463ceea82..cf54eb8ec 100644 --- a/src/libCom/calc/postfix.c +++ b/src/libCom/calc/postfix.c @@ -148,13 +148,15 @@ static const ELEMENT operators[] = { {":=", 0, 0, -1, STORE_OPERATOR, STORE_A}, {";", 0, 0, 0, EXPR_TERMINATOR,NOT_GENERATED}, {"<", 3, 3, -1, BINARY_OPERATOR,LESS_THAN}, -{"<<", 2, 2, -1, BINARY_OPERATOR,LEFT_SHIFT}, +{"<<", 2, 2, -1, BINARY_OPERATOR,LEFT_SHIFT_ARITH}, +{"<<<", 2, 2, -1, BINARY_OPERATOR,LEFT_SHIFT_LOGIC}, {"<=", 3, 3, -1, BINARY_OPERATOR,LESS_OR_EQ}, {"=", 3, 3, -1, BINARY_OPERATOR,EQUAL}, {"==", 3, 3, -1, BINARY_OPERATOR,EQUAL}, {">", 3, 3, -1, BINARY_OPERATOR,GR_THAN}, {">=", 3, 3, -1, BINARY_OPERATOR,GR_OR_EQ}, -{">>", 2, 2, -1, BINARY_OPERATOR,RIGHT_SHIFT}, +{">>", 2, 2, -1, BINARY_OPERATOR,RIGHT_SHIFT_ARITH}, +{">>>", 2, 2, -1, BINARY_OPERATOR,RIGHT_SHIFT_LOGIC}, {"?", 0, 0, -1, CONDITIONAL, COND_IF}, {"AND", 2, 2, -1, BINARY_OPERATOR,BIT_AND}, {"OR", 1, 1, -1, BINARY_OPERATOR,BIT_OR}, @@ -579,8 +581,10 @@ epicsShareFunc void "BIT_AND", "BIT_EXCL_OR", "BIT_NOT", - "RIGHT_SHIFT", - "LEFT_SHIFT", + "RIGHT_SHIFT_ARITH", + "RIGHT_SHIFT_LOGIC", + "LEFT_SHIFT_ARITH", + "LEFT_SHIFT_LOGIC", /* Relationals */ "NOT_EQ", "LESS_THAN", diff --git a/src/libCom/calc/postfixPvt.h b/src/libCom/calc/postfixPvt.h index 53efb32e0..5b2ba0a7b 100644 --- a/src/libCom/calc/postfixPvt.h +++ b/src/libCom/calc/postfixPvt.h @@ -84,8 +84,10 @@ typedef enum { BIT_AND, BIT_EXCL_OR, BIT_NOT, - RIGHT_SHIFT, - LEFT_SHIFT, + RIGHT_SHIFT_ARITH, + RIGHT_SHIFT_LOGIC, + LEFT_SHIFT_ARITH, + LEFT_SHIFT_LOGIC, /* Relationals */ NOT_EQ, LESS_THAN, diff --git a/src/libCom/test/epicsCalcTest.cpp b/src/libCom/test/epicsCalcTest.cpp index 2492c95ba..a0131cd31 100644 --- a/src/libCom/test/epicsCalcTest.cpp +++ b/src/libCom/test/epicsCalcTest.cpp @@ -104,7 +104,7 @@ void testUInt32Calc(const char *expr, epicsUInt32 expected) { testDiag("calcPerform: error evaluating '%s'", expr); } - uresult = (epicsUInt32) result; + uresult = (result < 0.0 ? (epicsUInt32)(epicsInt32)result : (epicsUInt32)result); pass = (uresult == expected); if (!testOk(pass, "%s", expr)) { testDiag("Expected result is 0x%x (%u), actually got 0x%x (%u)", @@ -297,7 +297,7 @@ MAIN(epicsCalcTest) const double a=1.0, b=2.0, c=3.0, d=4.0, e=5.0, f=6.0, g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0; - testPlan(613); + testPlan(643); /* LITERAL_OPERAND elements */ testExpr(0); @@ -688,7 +688,9 @@ MAIN(epicsCalcTest) testExpr(NaN < NaN); testExpr(1 << 2); - testExpr(1 << 3 << 2) + testCalc("1 <<< 2", 1u << 2u); + testExpr(1 << 3 << 2); + testCalc("1 <<< 3 <<< 2", 1u << 3u << 2u); testExpr(0 <= 1); testExpr(0 <= 0); @@ -776,7 +778,9 @@ MAIN(epicsCalcTest) testExpr(NaN >= NaN); testExpr(8 >> 1); + testCalc("8 >>> 1", 8u >> 1u); testExpr(64 >> 2 >> 1); + testCalc("64 >>> 2 >>> 1", 64u >> 2u >> 1u); testExpr(7 AND 4); @@ -873,13 +877,19 @@ MAIN(epicsCalcTest) testExpr(2 | 4 / 2); // 1 5 testCalc("1 | 2 ** 3", 1 | (int) pow(2., 3.));// 1 6 testExpr(3 << 2 & 10); // 2 2 + testCalc("3 <<< 2 & 10", 3u << 2u & 10u); // 2 2 testCalc("18 & 6 << 2", (18 & 6) << 2); // 2 2 + testCalc("18 & 6 <<< 2", (18u & 6u) << 2u); // 2 2 testExpr(36 >> 2 & 10); // 2 2 + testCalc("36 >>> 2 & 10", 36u >> 2u & 10u); // 2 2 testCalc("18 & 20 >> 2", (18 & 20) >> 2); // 2 2 + testCalc("18 & 20 >>> 2", (18u & 20u) >> 2u); // 2 2 testExpr(3 & 4 == 4); // 2 3 testExpr(3 AND 4 == 4); // 2 3 testCalc("1 << 2 != 4", 1 << (2 != 4)); // 2 3 + testCalc("1 <<< 2 != 4", 1u << (2u != 4u)); // 2 3 testCalc("16 >> 2 != 4", 16 >> (2 != 4)); // 2 3 + testCalc("16 >>> 2 != 4", 16u >> (2u != 4u)); // 2 3 testExpr(3 AND -2); // 2 8 testExpr(0 < 1 ? 2 : 3); // 3 0 testExpr(1 <= 0 ? 2 : 3); // 3 0 @@ -951,7 +961,13 @@ MAIN(epicsCalcTest) testUInt32Calc("~0xaaaaaaaa", 0x55555555u); testUInt32Calc("~~0xaaaaaaaa", 0xaaaaaaaau); testUInt32Calc("0xaaaaaaaa >> 8", 0xffaaaaaau); + testUInt32Calc("0x55555555 >> 8", 0x00555555u); + testUInt32Calc("0xaaaaaaaa >>> 8", 0x00aaaaaau); + testUInt32Calc("0x55555555 >>> 8", 0x00555555u); testUInt32Calc("0xaaaaaaaa << 8", 0xaaaaaa00u); + testUInt32Calc("0x55555555 << 8", 0x55555500u); + testUInt32Calc("0xaaaaaaaa <<< 8", 0xaaaaaa00u); + testUInt32Calc("0x55555555 <<< 8", 0x55555500u); // using integer literals assigned to variables testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a AND b", 0xaaaa0000u); testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a OR b", 0xffffaaaau); @@ -959,7 +975,13 @@ MAIN(epicsCalcTest) testUInt32Calc("a:=0xaaaaaaaa; ~a", 0x55555555u); testUInt32Calc("a:=0xaaaaaaaa; ~~a", 0xaaaaaaaau); testUInt32Calc("a:=0xaaaaaaaa; a >> 8", 0xffaaaaaau); + testUInt32Calc("a:=0xaaaaaaaa; a >>> 8", 0x00aaaaaau); testUInt32Calc("a:=0xaaaaaaaa; a << 8", 0xaaaaaa00u); + testUInt32Calc("a:=0xaaaaaaaa; a <<< 8", 0xaaaaaa00u); + testUInt32Calc("a:=0x55555555; a >> 8", 0x00555555u); + testUInt32Calc("a:=0x55555555; a >>> 8", 0x00555555u); + testUInt32Calc("a:=0x55555555; a << 8", 0x55555500u); + testUInt32Calc("a:=0x55555555; a <<< 8", 0x55555500u); // Test proper conversion of double values (+ 0.1 enforces double literal) // when used as inputs to the bitwise operations. @@ -979,14 +1001,21 @@ MAIN(epicsCalcTest) testUInt32Calc("~ -1431655766.1", 0x55555555u); testUInt32Calc("~ 2863311530.1", 0x55555555u); testUInt32Calc("-1431655766.1 >> 0", 0xaaaaaaaau); + testUInt32Calc("-1431655766.1 >>> 0", 0xaaaaaaaau); testUInt32Calc("2863311530.1 >> 0", 0xaaaaaaaau); + testUInt32Calc("2863311530.1 >>> 0", 0xaaaaaaaau); testUInt32Calc("-1431655766.1 >> 0.1", 0xaaaaaaaau); + testUInt32Calc("-1431655766.1 >>> 0.1", 0xaaaaaaaau); testUInt32Calc("2863311530.1 >> 0.1", 0xaaaaaaaau); + testUInt32Calc("2863311530.1 >>> 0.1", 0xaaaaaaaau); testUInt32Calc("-1431655766.1 << 0", 0xaaaaaaaau); + testUInt32Calc("-1431655766.1 <<< 0", 0xaaaaaaaau); testUInt32Calc("2863311530.1 << 0", 0xaaaaaaaau); + testUInt32Calc("2863311530.1 <<< 0", 0xaaaaaaaau); testUInt32Calc("-1431655766.1 << 0.1", 0xaaaaaaaau); + testUInt32Calc("-1431655766.1 <<< 0.1", 0xaaaaaaaau); testUInt32Calc("2863311530.1 << 0.1", 0xaaaaaaaau); + testUInt32Calc("2863311530.1 <<< 0.1", 0xaaaaaaaau); return testDone(); } - From 983937a52ff9ffe04b3013b51dd240fe8da3f0c5 Mon Sep 17 00:00:00 2001 From: Gabriel Fedel Date: Tue, 11 Feb 2020 12:45:07 +0100 Subject: [PATCH 36/59] Fix event record device support with constant INP This fix apply to event record device with constant INP. Now when the event record is proccessed the associated records with the same SCAN setup get triggered correctly, it is not more necessary to set VAL on event record. Fixes lp: #1829770 --- src/std/rec/eventRecord.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/std/rec/eventRecord.c b/src/std/rec/eventRecord.c index a135a3a37..2ba4859a5 100644 --- a/src/std/rec/eventRecord.c +++ b/src/std/rec/eventRecord.c @@ -106,10 +106,11 @@ static long init_record(eventRecord *prec, int pass) recGblInitConstantLink(&prec->siol,DBF_STRING,&prec->sval); } - prec->epvt = eventNameToHandle(prec->val); - if( (pdset=(struct eventdset *)(prec->dset)) && (pdset->init_record) ) status=(*pdset->init_record)(prec); + + prec->epvt = eventNameToHandle(prec->val); + return(status); } From 4844fbbd8271e513d2baf7cd3726a10619c3fa9c Mon Sep 17 00:00:00 2001 From: Bryan Robert Tester Date: Wed, 12 Feb 2020 15:11:38 +0000 Subject: [PATCH 37/59] moved listen into rsrv_grab_tcp to allow retry if failed Fixes race condition with multiple IOCs starting simultaneously. --- src/ioc/rsrv/caservertask.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/ioc/rsrv/caservertask.c b/src/ioc/rsrv/caservertask.c index 80310a422..24fa5859f 100644 --- a/src/ioc/rsrv/caservertask.c +++ b/src/ioc/rsrv/caservertask.c @@ -66,17 +66,6 @@ static void req_server (void *pParm) IOC_sock = conf->tcp; - /* listen and accept new connections */ - if ( listen ( IOC_sock, 20 ) < 0 ) { - char sockErrBuf[64]; - epicsSocketConvertErrnoToString ( - sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ( "CAS: Listen error: %s\n", - sockErrBuf ); - epicsSocketDestroy (IOC_sock); - epicsThreadSuspendSelf (); - } - epicsEventSignal(castcp_startStopEvent); while (TRUE) { @@ -194,7 +183,7 @@ SOCKET* rsrv_grab_tcp(unsigned short *port) epicsSocketEnableAddressReuseDuringTimeWaitState ( tcpsock ); - if(bind(tcpsock, &scratch.sa, sizeof(scratch))==0) { + if(bind(tcpsock, &scratch.sa, sizeof(scratch))==0 && listen(tcpsock, 20)==0) { if(scratch.ia.sin_port==0) { /* use first socket to pick a random port */ osiSocklen_t alen = sizeof(ifaceAddr); From 803593560dc160ef5c971ef04908574e6dc88c37 Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Mon, 17 Feb 2020 12:22:20 +0000 Subject: [PATCH 38/59] Remove redundant left logical shift --- src/libCom/calc/calcPerform.c | 24 ++++++------------------ src/libCom/calc/postfix.c | 4 +--- src/libCom/calc/postfixPvt.h | 3 +-- src/libCom/test/epicsCalcTest.cpp | 15 +-------------- 4 files changed, 9 insertions(+), 37 deletions(-) diff --git a/src/libCom/calc/calcPerform.c b/src/libCom/calc/calcPerform.c index 1d675cffc..4a073c774 100644 --- a/src/libCom/calc/calcPerform.c +++ b/src/libCom/calc/calcPerform.c @@ -32,11 +32,6 @@ static int cond_search(const char **ppinst, int match); #define PI 3.14159265358979323 #endif -/* Turn off global optimization for 64-bit MSVC builds */ -#if 0 && defined(_WIN32) && defined(_M_X64) && !defined(_MINGW) -# pragma optimize("g", off) -#endif - /* calcPerform * * Evalutate the postfix expression @@ -319,9 +314,12 @@ epicsShareFunc long *ptop = (double)~d2i(*ptop); break; - /* The shift operators use signed integers, so a right-shift will - * extend the sign bit into the left-hand end of the value. The - * double-casting through unsigned here is important, see above. + /* In C the shift operators decide on an arithmetic or logical shift + * based on whether the integer is signed or unsigned. + * With signed integers, a right-shift is arithmetic and will + * extend the sign bit into the left-hand end of the value. When used + * with unsigned values a logical shift is performed. The + * double-casting through signed/unsigned here is important, see above. */ case RIGHT_SHIFT_ARITH: @@ -339,11 +337,6 @@ epicsShareFunc long *ptop = (double)(d2ui(*ptop) >> (d2ui(top) & 31u)); break; - case LEFT_SHIFT_LOGIC: - top = *ptop--; - *ptop = (double)(d2ui(*ptop) << (d2ui(top) & 31u)); - break; - case NOT_EQ: top = *ptop--; *ptop = *ptop != top; @@ -398,11 +391,6 @@ epicsShareFunc long *presult = *ptop; return 0; } -#if 0 && defined(_WIN32) && defined(_M_X64) && !defined(_MINGW) -# pragma optimize("", on) -#endif - - epicsShareFunc long calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores) diff --git a/src/libCom/calc/postfix.c b/src/libCom/calc/postfix.c index cf54eb8ec..54d629b40 100644 --- a/src/libCom/calc/postfix.c +++ b/src/libCom/calc/postfix.c @@ -149,7 +149,6 @@ static const ELEMENT operators[] = { {";", 0, 0, 0, EXPR_TERMINATOR,NOT_GENERATED}, {"<", 3, 3, -1, BINARY_OPERATOR,LESS_THAN}, {"<<", 2, 2, -1, BINARY_OPERATOR,LEFT_SHIFT_ARITH}, -{"<<<", 2, 2, -1, BINARY_OPERATOR,LEFT_SHIFT_LOGIC}, {"<=", 3, 3, -1, BINARY_OPERATOR,LESS_OR_EQ}, {"=", 3, 3, -1, BINARY_OPERATOR,EQUAL}, {"==", 3, 3, -1, BINARY_OPERATOR,EQUAL}, @@ -582,9 +581,8 @@ epicsShareFunc void "BIT_EXCL_OR", "BIT_NOT", "RIGHT_SHIFT_ARITH", - "RIGHT_SHIFT_LOGIC", "LEFT_SHIFT_ARITH", - "LEFT_SHIFT_LOGIC", + "RIGHT_SHIFT_LOGIC", /* Relationals */ "NOT_EQ", "LESS_THAN", diff --git a/src/libCom/calc/postfixPvt.h b/src/libCom/calc/postfixPvt.h index 5b2ba0a7b..739cdbb78 100644 --- a/src/libCom/calc/postfixPvt.h +++ b/src/libCom/calc/postfixPvt.h @@ -85,9 +85,8 @@ typedef enum { BIT_EXCL_OR, BIT_NOT, RIGHT_SHIFT_ARITH, - RIGHT_SHIFT_LOGIC, LEFT_SHIFT_ARITH, - LEFT_SHIFT_LOGIC, + RIGHT_SHIFT_LOGIC, /* Relationals */ NOT_EQ, LESS_THAN, diff --git a/src/libCom/test/epicsCalcTest.cpp b/src/libCom/test/epicsCalcTest.cpp index a0131cd31..5f7d68826 100644 --- a/src/libCom/test/epicsCalcTest.cpp +++ b/src/libCom/test/epicsCalcTest.cpp @@ -297,7 +297,7 @@ MAIN(epicsCalcTest) const double a=1.0, b=2.0, c=3.0, d=4.0, e=5.0, f=6.0, g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0; - testPlan(643); + testPlan(630); /* LITERAL_OPERAND elements */ testExpr(0); @@ -688,9 +688,7 @@ MAIN(epicsCalcTest) testExpr(NaN < NaN); testExpr(1 << 2); - testCalc("1 <<< 2", 1u << 2u); testExpr(1 << 3 << 2); - testCalc("1 <<< 3 <<< 2", 1u << 3u << 2u); testExpr(0 <= 1); testExpr(0 <= 0); @@ -877,9 +875,7 @@ MAIN(epicsCalcTest) testExpr(2 | 4 / 2); // 1 5 testCalc("1 | 2 ** 3", 1 | (int) pow(2., 3.));// 1 6 testExpr(3 << 2 & 10); // 2 2 - testCalc("3 <<< 2 & 10", 3u << 2u & 10u); // 2 2 testCalc("18 & 6 << 2", (18 & 6) << 2); // 2 2 - testCalc("18 & 6 <<< 2", (18u & 6u) << 2u); // 2 2 testExpr(36 >> 2 & 10); // 2 2 testCalc("36 >>> 2 & 10", 36u >> 2u & 10u); // 2 2 testCalc("18 & 20 >> 2", (18 & 20) >> 2); // 2 2 @@ -887,7 +883,6 @@ MAIN(epicsCalcTest) testExpr(3 & 4 == 4); // 2 3 testExpr(3 AND 4 == 4); // 2 3 testCalc("1 << 2 != 4", 1 << (2 != 4)); // 2 3 - testCalc("1 <<< 2 != 4", 1u << (2u != 4u)); // 2 3 testCalc("16 >> 2 != 4", 16 >> (2 != 4)); // 2 3 testCalc("16 >>> 2 != 4", 16u >> (2u != 4u)); // 2 3 testExpr(3 AND -2); // 2 8 @@ -966,8 +961,6 @@ MAIN(epicsCalcTest) testUInt32Calc("0x55555555 >>> 8", 0x00555555u); testUInt32Calc("0xaaaaaaaa << 8", 0xaaaaaa00u); testUInt32Calc("0x55555555 << 8", 0x55555500u); - testUInt32Calc("0xaaaaaaaa <<< 8", 0xaaaaaa00u); - testUInt32Calc("0x55555555 <<< 8", 0x55555500u); // using integer literals assigned to variables testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a AND b", 0xaaaa0000u); testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a OR b", 0xffffaaaau); @@ -977,11 +970,9 @@ MAIN(epicsCalcTest) testUInt32Calc("a:=0xaaaaaaaa; a >> 8", 0xffaaaaaau); testUInt32Calc("a:=0xaaaaaaaa; a >>> 8", 0x00aaaaaau); testUInt32Calc("a:=0xaaaaaaaa; a << 8", 0xaaaaaa00u); - testUInt32Calc("a:=0xaaaaaaaa; a <<< 8", 0xaaaaaa00u); testUInt32Calc("a:=0x55555555; a >> 8", 0x00555555u); testUInt32Calc("a:=0x55555555; a >>> 8", 0x00555555u); testUInt32Calc("a:=0x55555555; a << 8", 0x55555500u); - testUInt32Calc("a:=0x55555555; a <<< 8", 0x55555500u); // Test proper conversion of double values (+ 0.1 enforces double literal) // when used as inputs to the bitwise operations. @@ -1009,13 +1000,9 @@ MAIN(epicsCalcTest) testUInt32Calc("2863311530.1 >> 0.1", 0xaaaaaaaau); testUInt32Calc("2863311530.1 >>> 0.1", 0xaaaaaaaau); testUInt32Calc("-1431655766.1 << 0", 0xaaaaaaaau); - testUInt32Calc("-1431655766.1 <<< 0", 0xaaaaaaaau); testUInt32Calc("2863311530.1 << 0", 0xaaaaaaaau); - testUInt32Calc("2863311530.1 <<< 0", 0xaaaaaaaau); testUInt32Calc("-1431655766.1 << 0.1", 0xaaaaaaaau); - testUInt32Calc("-1431655766.1 <<< 0.1", 0xaaaaaaaau); testUInt32Calc("2863311530.1 << 0.1", 0xaaaaaaaau); - testUInt32Calc("2863311530.1 <<< 0.1", 0xaaaaaaaau); return testDone(); } From 8250339e0df18d7922dce4eb70ee44ed7a6d2bd7 Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Mon, 17 Feb 2020 12:46:11 +0000 Subject: [PATCH 39/59] Update record pod documentation --- src/std/rec/calcRecord.dbd.pod | 7 +++++-- src/std/rec/calcoutRecord.dbd.pod | 6 ++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/std/rec/calcRecord.dbd.pod b/src/std/rec/calcRecord.dbd.pod index 243c81eee..83b8edd76 100644 --- a/src/std/rec/calcRecord.dbd.pod +++ b/src/std/rec/calcRecord.dbd.pod @@ -318,10 +318,13 @@ XOR : Bitwise Exclusive Or C<~> : One's Complement =item * -C<<< << >>> : Left shift +C<<< << >>> : Arithmetic Left Shift =item * -C<<< >> >>> : Right shift +C<<< >> >>> : Arithmetic Right Shift + +=item * +C<<<< >>> >>>> : Logical Right Shift =back diff --git a/src/std/rec/calcoutRecord.dbd.pod b/src/std/rec/calcoutRecord.dbd.pod index a59eaa348..9a1393d73 100644 --- a/src/std/rec/calcoutRecord.dbd.pod +++ b/src/std/rec/calcoutRecord.dbd.pod @@ -350,11 +350,13 @@ XOR : Bitwise Exclusive Or C<~> : One's Complement =item * -C<<< << >>> : Left shift +C<<< << >>> : Arithmetic Left Shift =item * -C<<< >> >>> : Right shift +C<<< >> >>> : Arithmetic Right Shift +=item * +C<<<< >>> >>>> : Logical Right Shift =back =head3 Assignment Operator From f2b4c412d3aa14bdf4c86fc3cd61d9cf482b4a99 Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Mon, 17 Feb 2020 12:49:45 +0000 Subject: [PATCH 40/59] Fix doc typo --- src/std/rec/calcoutRecord.dbd.pod | 1 + 1 file changed, 1 insertion(+) diff --git a/src/std/rec/calcoutRecord.dbd.pod b/src/std/rec/calcoutRecord.dbd.pod index 9a1393d73..a9b2a4e59 100644 --- a/src/std/rec/calcoutRecord.dbd.pod +++ b/src/std/rec/calcoutRecord.dbd.pod @@ -357,6 +357,7 @@ C<<< >> >>> : Arithmetic Right Shift =item * C<<<< >>> >>>> : Logical Right Shift + =back =head3 Assignment Operator From a0667a122bd2e4ac9fd01f3c554a43b040e8d9e0 Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Mon, 17 Feb 2020 13:29:38 +0000 Subject: [PATCH 41/59] Excluded x64 tests now need to be excluded on x86 too --- src/libCom/test/epicsCalcTest.cpp | 4 ++-- src/libCom/test/epicsMathTest.c | 18 +++++++++--------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libCom/test/epicsCalcTest.cpp b/src/libCom/test/epicsCalcTest.cpp index 5f7d68826..ee2108ffb 100644 --- a/src/libCom/test/epicsCalcTest.cpp +++ b/src/libCom/test/epicsCalcTest.cpp @@ -612,14 +612,14 @@ MAIN(epicsCalcTest) testExpr(0.0 + NaN); testExpr(Inf + 0.0); testExpr(Inf + Inf); -#if defined(_WIN64) && defined(_MSC_VER) +#if defined(_WIN32) && defined(_MSC_VER) testCalc("Inf + -Inf", NaN); #else testExpr(Inf + -Inf); #endif testExpr(Inf + NaN); testExpr(-Inf + 0.0); -#if defined(_WIN64) && defined(_MSC_VER) +#if defined(_WIN32) && defined(_MSC_VER) testCalc("-Inf + Inf", NaN); #else testExpr(-Inf + Inf); diff --git a/src/libCom/test/epicsMathTest.c b/src/libCom/test/epicsMathTest.c index 8ea763cf0..ffb2426fd 100644 --- a/src/libCom/test/epicsMathTest.c +++ b/src/libCom/test/epicsMathTest.c @@ -32,23 +32,23 @@ MAIN(epicsMathTest) testOk1(epicsINF > 0.0); testOk1(epicsINF - epicsINF != 0.0); -#if defined(_WIN64) && defined(_MSC_VER) - testTodoBegin("Known failure on windows-x64"); +#if defined(_WIN32) && defined(_MSC_VER) + testTodoBegin("Known failure on windows-x64 and win32-x86"); #endif testOk1(epicsINF + -epicsINF != 0.0); testOk1(-epicsINF + epicsINF != 0.0); -#if defined(_WIN64) && defined(_MSC_VER) +#if defined(_WIN32) && defined(_MSC_VER) testTodoEnd(); #endif testOk1(isnan(epicsINF - epicsINF)); -#if defined(_WIN64) && defined(_MSC_VER) - testTodoBegin("Known failure on windows-x64"); +#if defined(_WIN32) && defined(_MSC_VER) + testTodoBegin("Known failure on windows-x64 and win32-x86"); #endif testOk1(isnan(epicsINF + -epicsINF)); testOk1(isnan(-epicsINF + epicsINF)); -#if defined(_WIN64) && defined(_MSC_VER) +#if defined(_WIN32) && defined(_MSC_VER) testTodoEnd(); #endif @@ -62,12 +62,12 @@ MAIN(epicsMathTest) testOk1(!(epicsNAN > epicsNAN)); testOk1(isnan(epicsNAN - epicsNAN)); -#if defined(_WIN64) && defined(_MSC_VER) - testTodoBegin("Known failure on windows-x64"); +#if defined(_WIN32) && defined(_MSC_VER) + testTodoBegin("Known failure on windows-x64 and win32-x86"); #endif testOk1(isnan(epicsNAN + -epicsNAN)); testOk1(isnan(-epicsNAN + epicsNAN)); -#if defined(_WIN64) && defined(_MSC_VER) +#if defined(_WIN32) && defined(_MSC_VER) testTodoEnd(); #endif From 3944b32e048b4362808ffa847cd2feb7d3525e06 Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Mon, 17 Feb 2020 13:30:15 +0000 Subject: [PATCH 42/59] Add back in optimisation disable --- src/libCom/calc/calcPerform.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libCom/calc/calcPerform.c b/src/libCom/calc/calcPerform.c index 4a073c774..40e173b61 100644 --- a/src/libCom/calc/calcPerform.c +++ b/src/libCom/calc/calcPerform.c @@ -25,6 +25,7 @@ #include "postfix.h" #include "postfixPvt.h" + static double calcRandom(void); static int cond_search(const char **ppinst, int match); @@ -32,6 +33,11 @@ static int cond_search(const char **ppinst, int match); #define PI 3.14159265358979323 #endif +/* Turn off global optimization for 64-bit MSVC builds */ +#if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW) +# pragma optimize("g", off) +#endif + /* calcPerform * * Evalutate the postfix expression @@ -392,6 +398,10 @@ epicsShareFunc long return 0; } +#if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW) +# pragma optimize("", on) +#endif + epicsShareFunc long calcArgUsage(const char *pinst, unsigned long *pinputs, unsigned long *pstores) { From e48cdb48aca1fcd7cbf5dae518572b1f323e4be4 Mon Sep 17 00:00:00 2001 From: Dirk Zimoch Date: Tue, 18 Feb 2020 17:45:12 +0100 Subject: [PATCH 43/59] dbGet should not crash when source is an empty array --- src/ioc/db/dbAccess.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 4ee2d677d..a8ff3f137 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -898,6 +898,11 @@ long dbGet(DBADDR *paddr, short dbrType, } else { DBADDR localAddr = *paddr; /* Structure copy */ + if (pfl->no_elements < 1) { + status = S_db_badField; + goto done; + } + localAddr.field_type = pfl->field_type; localAddr.field_size = pfl->field_size; localAddr.no_elements = pfl->no_elements; From d82d3d367975e4c686f265e9fba5549b3f0af946 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 18 Feb 2020 18:05:46 -0600 Subject: [PATCH 44/59] Combine the iocVirgin and iocStopped states into iocVoid --- src/ioc/dbStatic/dbLexRoutines.c | 2 +- src/ioc/misc/iocInit.c | 8 ++++---- src/ioc/misc/iocInit.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ioc/dbStatic/dbLexRoutines.c b/src/ioc/dbStatic/dbLexRoutines.c index 2be7b0d0c..f412711d5 100644 --- a/src/ioc/dbStatic/dbLexRoutines.c +++ b/src/ioc/dbStatic/dbLexRoutines.c @@ -216,7 +216,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp, char *penv; char **macPairs; - if(getIocState() != iocVirgin) + if(getIocState() != iocVoid) return -2; if(*ppdbbase == 0) *ppdbbase = dbAllocBase(); diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 93d8908a8..1e13463a4 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -70,7 +70,7 @@ #include "registryRecordType.h" #include "rsrv.h" -static enum iocStateEnum iocState = iocVirgin; +static enum iocStateEnum iocState = iocVoid; static enum { buildRSRV, buildIsolated } iocBuildMode; @@ -104,7 +104,7 @@ int iocInit(void) static int iocBuild_1(void) { - if (iocState != iocVirgin && iocState != iocStopped) { + if (iocState != iocVoid) { errlogPrintf("iocBuild: IOC can only be initialized from uninitialized or stopped state\n"); return -1; } @@ -704,7 +704,7 @@ static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord, int iocShutdown(void) { - if (iocState == iocVirgin || iocState == iocStopped) return 0; + if (iocState == iocVoid) return 0; iterateRecords(doCloseLinks, NULL); if (iocBuildMode==buildIsolated) { /* stop and "join" threads */ @@ -723,7 +723,7 @@ int iocShutdown(void) dbProcessNotifyExit(); iocshFree(); } - iocState = iocStopped; + iocState = iocVoid; iocBuildMode = buildRSRV; return 0; } diff --git a/src/ioc/misc/iocInit.h b/src/ioc/misc/iocInit.h index 3e711d6c5..7e73b620a 100644 --- a/src/ioc/misc/iocInit.h +++ b/src/ioc/misc/iocInit.h @@ -14,7 +14,7 @@ #include "shareLib.h" enum iocStateEnum { - iocVirgin, iocBuilding, iocBuilt, iocRunning, iocPaused, iocStopped + iocVoid, iocBuilding, iocBuilt, iocRunning, iocPaused }; #ifdef __cplusplus From 41f1b0ffb51b611f4290dfcc9454b8b72dedbc81 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 19 Feb 2020 15:09:13 -0600 Subject: [PATCH 45/59] Fix histogram record allocation bug Found by Peter Heesterman: Potential use of NULL pcallback pointer. Nothing looks at the return value from wdogInit(), so don't bother. --- src/std/rec/histogramRecord.c | 39 +++++++++++++++-------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/std/rec/histogramRecord.c b/src/std/rec/histogramRecord.c index 17730991b..c37b05df9 100644 --- a/src/std/rec/histogramRecord.c +++ b/src/std/rec/histogramRecord.c @@ -133,33 +133,28 @@ static void wdogCallback(epicsCallback *arg) return; } -static long wdogInit(histogramRecord *prec) + +static void wdogInit(histogramRecord *prec) { - myCallback *pcallback; - - if (!prec->wdog && prec->sdel > 0) { - /* initialize a callback object */ - pcallback = calloc(1, sizeof(myCallback)); - pcallback->prec = prec; - if (!pcallback) - return -1; - - callbackSetCallback(wdogCallback, &pcallback->callback); - callbackSetUser(pcallback, &pcallback->callback); - callbackSetPriority(priorityLow, &pcallback->callback); - prec->wdog = pcallback; - } - - if (!prec->wdog) - return -1; - pcallback = prec->wdog; - if (!pcallback) - return -1; if (prec->sdel > 0) { + myCallback *pcallback = prec->wdog; + + if (!pcallback) { + /* initialize a callback object */ + pcallback = calloc(1, sizeof(myCallback)); + if (!pcallback) + return; + + pcallback->prec = prec; + callbackSetCallback(wdogCallback, &pcallback->callback); + callbackSetUser(pcallback, &pcallback->callback); + callbackSetPriority(priorityLow, &pcallback->callback); + prec->wdog = pcallback; + } + /* start new timer on monitor */ callbackRequestDelayed(&pcallback->callback, prec->sdel); } - return 0; } static long init_record(histogramRecord *prec, int pass) From e6914f3b8089a417c9efc31940906854be182e6b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 24 Oct 2019 19:15:31 -0700 Subject: [PATCH 46/59] osdSockUnsentCount.c check for existance of SIO_TCP_INFO --- src/libCom/osi/os/WIN32/osdSockUnsentCount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libCom/osi/os/WIN32/osdSockUnsentCount.c b/src/libCom/osi/os/WIN32/osdSockUnsentCount.c index fe68ead01..3f4ab3eee 100644 --- a/src/libCom/osi/os/WIN32/osdSockUnsentCount.c +++ b/src/libCom/osi/os/WIN32/osdSockUnsentCount.c @@ -13,7 +13,7 @@ * See https://docs.microsoft.com/en-us/windows/win32/api/mstcpip/ns-mstcpip-tcp_info_v0 */ int epicsSocketUnsentCount(SOCKET sock) { -#if defined (_WIN32) && WINVER >= _WIN32_WINNT_WIN10 +#ifdef SIO_TCP_INFO /* Windows 10 Version 1703 / Server 2016 */ DWORD infoVersion = 0, bytesReturned; TCP_INFO_v0 tcpInfo; From f9820577c1908ce7442ef4cae2b963f9a5ba78e5 Mon Sep 17 00:00:00 2001 From: Martin Konrad Date: Wed, 4 Mar 2020 10:56:25 -0500 Subject: [PATCH 47/59] Replace usleep call by nanosleep Also improve behavior in case signals are delivered to the sleeping thread. This fixes a potential security weakness reported by codacy (interaction of usleep with SIGALRM and other timer functions such as sleep(), alarm(), setitimer(), and nanosleep() is unspecified). --- src/libCom/osi/os/posix/epicsAtomicOSD.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libCom/osi/os/posix/epicsAtomicOSD.cpp b/src/libCom/osi/os/posix/epicsAtomicOSD.cpp index 1cc227fcd..a449a69c0 100644 --- a/src/libCom/osi/os/posix/epicsAtomicOSD.cpp +++ b/src/libCom/osi/os/posix/epicsAtomicOSD.cpp @@ -48,8 +48,10 @@ void epicsAtomicLock ( EpicsAtomicLockKey * ) status = pthread_mutex_lock ( & mutex ); if ( status == 0 ) return; assert ( status == EINTR ); - static const useconds_t retryDelayUSec = 100000; - usleep ( retryDelayUSec ); + struct timespec retryDelay = { 0, 100000000 }; + struct timespec remainingDelay; + while (nanosleep(&retryDelay, &remainingDelay) == -1 && errno == EINTR) + retryDelay = remainingDelay; countDown--; assert ( countDown ); } From a9034bb5860fb46bb094ca2cd02ba3f4135b43b9 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 7 Mar 2020 00:40:13 -0600 Subject: [PATCH 48/59] Fix clock_gettime issue on newer MinGW builds Fixes lp: #1853168 --- src/libCom/osi/osiClockTime.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/libCom/osi/osiClockTime.c b/src/libCom/osi/osiClockTime.c index 01958b20e..d9dff13a2 100644 --- a/src/libCom/osi/osiClockTime.c +++ b/src/libCom/osi/osiClockTime.c @@ -41,7 +41,7 @@ static struct { static epicsThreadOnceId onceId = EPICS_THREAD_ONCE_INIT; -#ifdef CLOCK_REALTIME +#if defined(CLOCK_REALTIME) && !defined(_WIN32) /* This code is not used on systems without Posix CLOCK_REALTIME, * but the only way to detect that is from the OS headers, so the * Makefile can't exclude compiling this file on those systems. @@ -229,7 +229,11 @@ static int ClockTimeGetCurrent(epicsTimeStamp *pDest) return 0; } -#endif /* CLOCK_REALTIME */ +/* Used in Report function below: */ +#define UNINIT_ERROR "initialized" +#else +#define UNINIT_ERROR "available" +#endif /* CLOCK_REALTIME && !WIN32 */ /* Allow the following report routine to be compiled anyway * to avoid getting a build warning from ranlib. @@ -242,13 +246,7 @@ int ClockTime_Report(int level) char timebuf[32]; if (onceId == EPICS_THREAD_ONCE_INIT) { - printf("OS Clock driver not %s.\n", -#ifdef CLOCK_REALTIME - "initialized" -#else - "available" -#endif /* CLOCK_REALTIME */ - ); + puts("OS Clock driver not " UNINIT_ERROR); } else if (ClockTimePvt.synchronize == CLOCKTIME_SYNC) { int synchronized, syncFromPriority; From 062c75a078e164f89a2d0d0851f3f222492a6f92 Mon Sep 17 00:00:00 2001 From: Rold Keitel Date: Thu, 26 Mar 2020 16:35:58 -0500 Subject: [PATCH 49/59] Prepare for POD documentation of the aa[io]Records --- src/std/rec/{aaiRecord.dbd => aaiRecord.dbd.pod} | 0 src/std/rec/{aaoRecord.dbd => aaoRecord.dbd.pod} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/std/rec/{aaiRecord.dbd => aaiRecord.dbd.pod} (100%) rename src/std/rec/{aaoRecord.dbd => aaoRecord.dbd.pod} (100%) diff --git a/src/std/rec/aaiRecord.dbd b/src/std/rec/aaiRecord.dbd.pod similarity index 100% rename from src/std/rec/aaiRecord.dbd rename to src/std/rec/aaiRecord.dbd.pod diff --git a/src/std/rec/aaoRecord.dbd b/src/std/rec/aaoRecord.dbd.pod similarity index 100% rename from src/std/rec/aaoRecord.dbd rename to src/std/rec/aaoRecord.dbd.pod From cb3fb18f40b59fead64df9732a9e6a7b19cfafd2 Mon Sep 17 00:00:00 2001 From: Rold Keitel Date: Thu, 26 Mar 2020 17:41:12 -0500 Subject: [PATCH 50/59] POD docs: add aai & aai, update others --- src/std/rec/aaiRecord.dbd.pod | 362 ++++++++++++++++++++++++++++ src/std/rec/aaoRecord.dbd.pod | 360 +++++++++++++++++++++++++++ src/std/rec/stringinRecord.dbd.pod | 55 +++-- src/std/rec/stringoutRecord.dbd.pod | 54 ++++- src/std/rec/waveformRecord.dbd.pod | 45 +--- 5 files changed, 817 insertions(+), 59 deletions(-) diff --git a/src/std/rec/aaiRecord.dbd.pod b/src/std/rec/aaiRecord.dbd.pod index 06ab7f7b8..deb846c75 100644 --- a/src/std/rec/aaiRecord.dbd.pod +++ b/src/std/rec/aaiRecord.dbd.pod @@ -6,11 +6,287 @@ # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* + +=title Array Analog Input (aai) + +The array analog input record type is used to read array data. The array data can +contain any of the supported data types. The record is in many ways similar to the +waveform record. It allows, however, the device support to allocate the array +storage. + +=recordtype aai + +=cut + +include "menuFtype.dbd" + menu(aaiPOST) { choice(aaiPOST_Always,"Always") choice(aaiPOST_OnChange,"On Change") } + recordtype(aai) { + +=head2 Parameter Fields + +The record-specific fields are described below, grouped by functionality. + +=head3 Scan Parameters + +The array analog input record has the standard fields for specifying under what +circumstances the record will be processed. These fields are listed in L. In addition, L explains how these fields are +used. Note that I/O event scanning is only supported for those card types that +interrupt. + +=head3 Read Parameters + +These fields are configurable by the user to specify how and from where the record +reads its data. The INP field determines from where the array analog input gets +its input. 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 INP field be a +channel access link, a database link, or a constant. Otherwise, the INP field must +be a hardware address. See L
for information on the format +of hardware addresses and database links. + +=head4 Fields related to waveform reading + +The DTYP field must contain the name of the appropriate device support module. +The values retrieved from the input link are placed in an array referenced by +VAL. (If the INP link is a constant, elements can be placed in the array via +dbPuts.) NELM specifies the number of elements that the array will hold, while +FTVL specifies the data type of the elements. + +=fields DTYP, INP, NELM, FTVL + +=head4 Possible data types for FTVL + +=menu menuFtype + +=head3 Operator Display Parameters + +These parameters are used to present meaningful data to the operator. They +display the value and other parameters of the waveform either textually or +graphically. + +=head4 Fields related to I + +EGU is a string of up to 16 characters describing the units that the array data +measures. It is retrieved by the C<<< get_units() >>> record support routine. + +The HOPR and LOPR fields set the upper and lower display limits for array +elements referenced by the VAL field. Both the C<<< get_graphic_double() >>> and +C<<< get_control_double() >>> record support routines retrieve these fields. + +The PREC field determines the floating point precision with which to display the +array values. It is used whenever the C<<< get_precision() >>> record support +routine is called. + +See L for more on the record name (NAME) and +description (DESC) fields. + +=fields EGU, HOPR, LOPR, PREC, NAME, DESC + + +=head3 Alarm Parameters + +The array analog input record has the alarm parameters common to all record types. + +=head3 Monitor Parameters + +These parameters are used to determine when to send monitors placed on the VAL +field. The APST and MPST fields are a menu with choices "Always" and "On +Change". The default is "Always", thus monitors will normally be sent every time +the record processes. Selecting "On Change" causes a 32-bit hash of the VAL +field buffer to be calculated and compared with the previous hash value every +time the record processes; the monitor will only be sent if the hash is +different, indicating that the buffer has changed. Note that there is a small +chance that two different value buffers might result in the same hash value, so +for critical systems "Always" may be a better choice, even though it re-sends +duplicate data. + +=head4 Record fields related to I + +=fields APST, MPST, HASH + +=head4 Menu choices for C and C fields + +=menu aaiPOST + +=head3 Run-time Parameters + +These parameters are used by the run-time code for processing the array analog +input 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 input 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 read +into the array. + +=fields VAL, BPTR, NORD + +The following fields are used to operate the array analog input record in the +simulation mode. See L for more information on the simulation +mode fields. + +=fields SIOL, SIML, SIMM, SIMS + +=begin html + +
+
+
+ +=end html + +=head2 Record Support + +=head3 Record Support Routines + +=head4 init_record + + static long init_record(aaiRecord *prec, int pass) + +If device support includes C, it is called. + +Checks if device support allocated array space. If not, space for the array is +allocated using NELM and FTVL. The array address is stored in the record. + +This routine initializes SIMM with the value of SIML if SIML type is CONSTANT +link or creates a channel access link if SIML type is PV_LINK. VAL is likewise +initialized if SIOL is CONSTANT or PV_LINK. + +This routine next checks to see that device support is available and a device +support read routine is defined. If either does not exist, an error message is +issued and processing is terminated + +=head4 process + + static long process(aaiRecord *prec) + +See L section below. + +=head4 cvt_dbaddr + + static long cvt_dbaddr(DBADDR *paddr) + +This is called by dbNameToAddr. It makes the dbAddr structure refer to the +actual buffer holding the result. + +=head4 get_array_info + + static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) + +Obtains values from the array referenced by VAL. + +=head4 put_array_info + + static long put_array_info(DBADDR *paddr, long nNew) + +Writes values into the array referenced by VAL. + +=head4 get_units + + static long get_units(DBADDR *paddr, char *units) + +Retrieves EGU. + +=head4 get_prec + + static long get_precision(DBADDR *paddr, long *precision) + +Retrieves PREC if field is VAL field. Otherwise, calls C<<< recGblGetPrec() >>>. + +=head4 get_graphic_double + + static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) + +Sets the upper display and lower display limits for a field. If the field is VAL +the limits are set to HOPR and LOPR, else if the field has upper and lower +limits defined they will be used, else the upper and lower maximum values for +the field type will be used. + +Sets the following values: + + upper_disp_limit = HOPR + lower_disp_limit = LOPR + +=head4 get_control_double + + static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) + +Sets the upper control and the lower control limits for a field. If the field is +VAL the limits are set to HOPR and LOPR, else if the field has upper and lower +limits defined they will be used, else the upper and lower maximum values for +the field type will be used. + +Sets the following values + + upper_ctrl_limit = HOPR + lower_ctrl_limit = LOPR + +=head3 Record Processing + +Routine process implements the following algorithm: + +=over + +=item 1. + +Check to see that the appropriate device support module exists. If it doesn't, +an error message is issued and processing is terminated with the PACT field +still set to TRUE. This ensures that processes will no longer be called for this +record. Thus error storms will not occur. + +=item 2. + +Call device support read routine C. + +=item 3. + +If PACT has been changed to TRUE, the device support read routine has started +but has not completed writing the new value. In this case, the processing +routine merely returns, leaving PACT TRUE. + +=item 4. + +Check to see if monitors should be invoked. + +=over + +=item * + +Alarm monitors are invoked if the alarm status or severity has changed. + +=item * + +Archive and value change monitors are invoked if APST or MPST are Always or if +the result of the hash calculation is different. + +=item * + +NSEV and NSTA are reset to 0. + +=back + +=item 5. + +Scan forward link if necessary, set PACT FALSE, and return. + +=back + +=begin html + +
+
+
+ +=end html + +=cut + include "dbCommon.dbd" field(VAL,DBF_NOACCESS) { prompt("Value") @@ -114,3 +390,89 @@ recordtype(aai) { interest(3) } } + +=head2 Device Support + +=head3 Fields Of Interest To Device Support + +Each array analog input record record must have an associated set of device +support routines. The primary responsibility of the device support routines is to +obtain a new array value whenever C is called. The device support +routines are primarily interested in the following fields: + +=fields PACT, DPVT, NSEV, NSTA, INP, NELM, FTVL, BPTR, NORD + +=head3 Device Support Routines + +Device support consists of the following routines: + +=head4 report + + long report(int level) + +This optional routine is called by the IOC command C and is passed the +report level that was requested by the user. +It should print a report on the state of the device support to stdout. +The C parameter may be used to output increasingly more detailed +information at higher levels, or to select different types of information with +different levels. +Level zero should print no more than a small summary. + +=head4 init + + long init(int after) + +This optional routine is called twice at IOC initialization time. +The first call happens before any of the C calls are made, with +the integer parameter C set to 0. +The second call happens after all of the C calls have been made, +with C set to 1. + +=head4 init_record + + long init_record(dbCommon *precord) + +This routine is optional. If provided, it is called by the record support +C routine. + +=head4 get_ioint_info + + long get_ioint_info(int cmd, dbCommon *precord, IOSCANPVT *ppvt) + +This routine is called by the ioEventScan system each time the record is added +or deleted from an I/O event scan list. cmd has the value (0,1) if the +record is being (added to, deleted from) an I/O event list. It must be +provided for any device type that can use the ioEvent scanner. + +=head4 read_aai + + long read_aai(dbCommon *precord) + +This routine must provide a new input value. It returns the following values: + +=over + +=item * + +0: Success. + +=item * + +Other: Error. + +=back + +=head3 Device Support For Soft Records + +The C<<< Soft Channel >>> device support module is provided to read values from +other records and store them in arrays. If INP is a constant link, then read_aai +does nothing. In this case, the record can be used to hold arrays written via +dbPuts. If INP is a database or channel access link, the new array value is read +from the link. NORD is set. + +This module places a value directly in VAL and NORD is set to the number of items +in the array. + +If the INP link type is constant, then NORD is set to zero. + +=cut diff --git a/src/std/rec/aaoRecord.dbd.pod b/src/std/rec/aaoRecord.dbd.pod index 57d842f4f..ed2477c98 100644 --- a/src/std/rec/aaoRecord.dbd.pod +++ b/src/std/rec/aaoRecord.dbd.pod @@ -6,11 +6,287 @@ # EPICS BASE is distributed subject to a Software License Agreement found # in file LICENSE that is included with this distribution. #************************************************************************* + +=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. + +=recordtype aao + +=cut + +include "menuFtype.dbd" + menu(aaoPOST) { choice(aaoPOST_Always,"Always") choice(aaoPOST_OnChange,"On Change") } + recordtype(aao) { + +=head2 Parameter Fields + +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. + +=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. + +=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. + +=fields DTYP, OUT, NELM, FTVL + +=head4 Possible data types for FTVL + +=menu menuFtype + +=head3 Operator Display Parameters + +These parameters are used to present meaningful data to the operator. They +display the value and other parameters of the waveform either textually or +graphically. + +=head4 Fields related to I + +EGU is a string of up to 16 characters describing the units that the array data +measures. It is retrieved by the C<<< get_units >>> record support routine. + +The HOPR and LOPR fields set the upper and lower display limits for array +elements referenced by the VAL field. Both the C<<< get_graphic_double >>> and +C<<< get_control_double >>> record support routines retrieve these fields. + +The PREC field determines the floating point precision with which to display the +array values. It is used whenever the C<<< get_precision >>> record support +routine is called. + +See L for more on the record name (NAME) and +description (DESC) fields. + +=fields EGU, HOPR, LOPR, PREC, NAME, DESC + + +=head3 Alarm Parameters + +The array analog output record has the alarm parameters common to all record +types. + +=head3 Monitor Parameters + +These parameters are used to determine when to send monitors placed on the VAL +field. The APST and MPST fields are a menu with choices "Always" and "On +Change". The default is "Always", thus monitors will normally be sent every time +the record processes. Selecting "On Change" causes a 32-bit hash of the VAL +field buffer to be calculated and compared with the previous hash value every +time the record processes; the monitor will only be sent if the hash is +different, indicating that the buffer has changed. Note that there is a small +chance that two different value buffers might result in the same hash value, so +for critical systems "Always" may be a better choice, even though it re-sends +duplicate data. + +=head4 Record fields related to I + +=fields APST, MPST, HASH + +=head4 Menu choices for C and C fields + +=menu aaoPOST + +=head3 Run-time Parameters + +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. + +The NORD field holds a counter of the number of elements that have been written to +the output, + +=fields VAL, BPTR, NORD + +The following fields are used to operate the array analog output record in the +simulation mode. See L for more information on the simulation +mode fields. + +=fields SIOL, SIML, SIMM, SIMS + +=begin html + +
+
+
+ +=end html + +=head2 Record Support + +=head3 Record Support Routines + +=head4 init_record + + static long init_record(aaoRecord *prec, int pass) + +If device support includes C, it is called. + +Checks if device support allocated array space. If not, space for the array is +allocated using NELM and FTVL. The array address is stored in the record. + +This routine initializes SIMM with the value of SIML if SIML type is CONSTANT +link or creates a channel access link if SIML type is PV_LINK. VAL is likewise +initialized if SIOL is CONSTANT or PV_LINK. + +This routine next checks to see that device support is available and a device +support write routine is defined. If either does not exist, an error message is +issued and processing is terminated + +=head4 process + + static long process(aaoRecord *prec) + +See L section below. + +=head4 cvt_dbaddr + + static long cvt_dbaddr(DBADDR *paddr) + +This is called by dbNameToAddr. It makes the dbAddr structure refer to the +actual buffer holding the result. + +=head4 get_array_info + + static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) + +Obtains values from the array referenced by VAL. + +=head4 put_array_info + + static long put_array_info(DBADDR *paddr, long nNew) + +Writes values into the array referenced by VAL. + +=head4 get_units + + static long get_units(DBADDR *paddr, char *units) + +Retrieves EGU. + +=head4 get_prec + + static long get_precision(DBADDR *paddr, long *precision) + +Retrieves PREC if field is VAL field. Otherwise, calls C<<< recGblGetPrec() >>>. + +=head4 get_graphic_double + + static long get_graphic_double(DBADDR *paddr, struct dbr_grDouble *pgd) + +Sets the upper display and lower display limits for a field. If the field is VAL +the limits are set to HOPR and LOPR, else if the field has upper and lower +limits defined they will be used, else the upper and lower maximum values for +the field type will be used. + +Sets the following values: + + upper_disp_limit = HOPR + lower_disp_limit = LOPR + +=head4 get_control_double + + static long get_control_double(DBADDR *paddr, struct dbr_ctrlDouble *pcd) + +Sets the upper control and the lower control limits for a field. If the field is +VAL the limits are set to HOPR and LOPR, else if the field has upper and lower +limits defined they will be used, else the upper and lower maximum values for +the field type will be used. + +Sets the following values + + upper_ctrl_limit = HOPR + lower_ctrl_limit = LOPR + +=head3 Record Processing + +Routine process implements the following algorithm: + +=over + +=item 1. + +Check to see that the appropriate device support module exists. If it doesn't, +an error message is issued and processing is terminated with the PACT field +still set to TRUE. This ensures that processes will no longer be called for this +record. Thus error storms will not occur. + +=item 2. + +Call device support write routine C. + +=item 3. + +If PACT has been changed to TRUE, the device support read routine has started +but has not completed writing the new value. In this case, the processing +routine merely returns, leaving PACT TRUE. + +=item 4. + +Check to see if monitors should be invoked. + +=over + +=item * + +Alarm monitors are invoked if the alarm status or severity has changed. + +=item * + +Archive and value change monitors are invoked if APST or MPST are Always or if +the result of the hash calculation is different. + +=item * + +NSEV and NSTA are reset to 0. + +=back + +=item 5. + +Scan forward link if necessary, set PACT FALSE, and return. + +=back + +=begin html + +
+
+
+ +=end html + +=cut + include "dbCommon.dbd" field(VAL,DBF_NOACCESS) { prompt("Value") @@ -114,3 +390,87 @@ recordtype(aao) { interest(3) } } + +=head2 Device Support + +=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: + +=fields PACT, DPVT, NSEV, NSTA, OUT, NELM, FTVL, BPTR, NORD + +=head3 Device Support Routines + +Device support consists of the following routines: + +=head4 report + + long report(int level) + +This optional routine is called by the IOC command C and is passed the +report level that was requested by the user. +It should print a report on the state of the device support to stdout. +The C parameter may be used to output increasingly more detailed +information at higher levels, or to select different types of information with +different levels. +Level zero should print no more than a small summary. + +=head4 init + + long init(int after) + +This optional routine is called twice at IOC initialization time. +The first call happens before any of the C calls are made, with +the integer parameter C set to 0. +The second call happens after all of the C calls have been made, +with C set to 1. + +=head4 init_record + + init_record(dbCommon *precord) + +This routine is optional. If provided, it is called by the record support +C routine. + +=head4 get_ioint_info + + long get_ioint_info(int cmd, dbCommon *precord, IOSCANPVT *ppvt) + +This routine is called by the ioEventScan system each time the record is added +or deleted from an I/O event scan list. cmd has the value (0,1) if the +record is being (added to, deleted from) an I/O event list. It must be +provided for any device type that can use the ioEvent scanner. + +=head4 write_aao + + long write_aao(dbCommon *precord) + +This routine must write the array data to output. It returns the following values: + +=over + +=item * + +0: Success. + +=item * + +Other: Error. + +=back + +=head3 Device Support For Soft Records + +The C<<< Soft Channel >>> device support module is provided to write values to +other records and store them in arrays. If OUT is a constant link, then +C does nothing. In this case, the record can be used to hold arrays +written via dbPuts. If OUT is a database or channel access link, the array value +is written to the link. NORD is set to the number of items in the array. + + +If the OUT link type is constant, then NORD is set to zero. + +=cut diff --git a/src/std/rec/stringinRecord.dbd.pod b/src/std/rec/stringinRecord.dbd.pod index ef1d93fa7..ecb17fe83 100644 --- a/src/std/rec/stringinRecord.dbd.pod +++ b/src/std/rec/stringinRecord.dbd.pod @@ -42,7 +42,7 @@ The string input record has the standard fields for specifying under what circumstances it will be processed. These fields are listed in L. In addition, L explains how these fields are used. -=head3 Read Parameters +=head3 Input Specification The INP field determines where the string input record gets its string. It can be a database or channel access link, or a constant. If constant, the VAL field @@ -112,8 +112,6 @@ monitors for VAL. If VAL is not equal to OVAL, then monitors are triggered. =fields OVAL - - The following fields are used to operate the string input in the simulation mode. See L for more information on simulation mode fields. @@ -251,15 +249,48 @@ routines are primarily interested in the following fields: =fields PACT, DPVT, UDF, VAL, INP -=head3 Device Support Routines (devSiSoft.c) +=head3 Device Support Routines + +Device support consists of the following routines: + +=head4 report + + long report(int level) + +This optional routine is called by the IOC command C and is passed the +report level that was requested by the user. +It should print a report on the state of the device support to stdout. +The C parameter may be used to output increasingly more detailed +information at higher levels, or to select different types of information with +different levels. +Level zero should print no more than a small summary. + +=head4 init + + long init(int after) + +This optional routine is called twice at IOC initialization time. +The first call happens before any of the C calls are made, with +the integer parameter C set to 0. +The second call happens after all of the C calls have been made, +with C set to 1. =head4 init_record - long init_record(stringinRecord *prec) + long init_record(dbCommon *prec) This routine is optional. If provided, it is called by the record support C routine. +=head4 get_ioint_info + + long get_ioint_info(int cmd, dbCommon *precord, IOSCANPVT *ppvt) + +This routine is called by the ioEventScan system each time the record is added +or deleted from an I/O event scan list. C has the value (0,1) if the +record is being (added to, deleted from) an I/O event list. It must be +provided for any device type that can use the ioEvent scanner. + =head4 read_stringin long read_stringin(stringinRecord *prec) @@ -276,17 +307,11 @@ This routine must provide a new input value. It returns the following values: =head3 Device Support for Soft Records -The C<<< Soft Channel >>> module places a value directly in VAL. +The C<<< Soft Channel >>> module reads a value directly into VAL. -If the INP link type is constant, the double constant, if non-zero, is converted -to a string and stored into VAL by C, and UDF is set to FALSE. If -the INP link type is PV_LINK, then dbCaAddInlink is called by C. - -read_stringin calls recGblGetLinkValue to read the current value of VAL. See -L. - -If the return status of recGblGetLinkValue is zero, then read_stringin sets UDF -to FALSE. The status of recGblGetLinkValue is returned. +Device support for DTYP C is provided for retrieving strings from environment variables. +C addressing C!!@ !!is used on the C link field to select the +desired environment variable. =cut diff --git a/src/std/rec/stringoutRecord.dbd.pod b/src/std/rec/stringoutRecord.dbd.pod index 773f0a7bf..123632ce8 100644 --- a/src/std/rec/stringoutRecord.dbd.pod +++ b/src/std/rec/stringoutRecord.dbd.pod @@ -334,7 +334,47 @@ primarily interested in the following fields: =fields PACT, DPVT, NSEV, NSTA, VAL, OUT -=head3 Device Support Routines (devSoSoft.c) +=head3 Device Support Routines + +Device support consists of the following routines: + +=head4 report + + long report(int level) + +This optional routine is called by the IOC command C and is passed the +report level that was requested by the user. +It should print a report on the state of the device support to stdout. +The C parameter may be used to output increasingly more detailed +information at higher levels, or to select different types of information with +different levels. +Level zero should print no more than a small summary. + +=head4 init + + long init(int after) + +This optional routine is called twice at IOC initialization time. +The first call happens before any of the C calls are made, with +the integer parameter C set to 0. +The second call happens after all of the C calls have been made, +with C set to 1. + +=head4 init_record + + long init_record(dbCommon *prec) + +This routine is optional. If provided, it is called by the record support +C routine. + +=head4 get_ioint_info + + long get_ioint_info(int cmd, dbCommon *precord, IOSCANPVT *ppvt) + +This routine is called by the ioEventScan system each time the record is added +or deleted from an I/O event scan list. C has the value (0,1) if the +record is being (added to, deleted from) an I/O event list. It must be +provided for any device type that can use the ioEvent scanner. =head4 write_stringout @@ -354,15 +394,9 @@ This routine must output a new value. It returns the following values: The C<<< Soft Channel >>> device support module writes the current value of VAL. -If the OUT link type is PV_LINK, then dbCaAddInlink is called by -C. - -write_so calls recGblPutLinkValue to write the current value of VAL. See -L. - -Device support for DTYP C is provided for writing values to the stdout, stderr, or errlog streams. -C addressing C<@stdout>, C<@stderr> or C<@errlog> is used on the OUT link field to select the desired -stream. +Device support for DTYP C is provided for writing values to the stdout, +stderr, or errlog streams. C addressing C<@stdout>, C<@stderr> or +C<@errlog> is used on the OUT link field to select the desired stream. =cut diff --git a/src/std/rec/waveformRecord.dbd.pod b/src/std/rec/waveformRecord.dbd.pod index dfc944647..fa41b89bd 100644 --- a/src/std/rec/waveformRecord.dbd.pod +++ b/src/std/rec/waveformRecord.dbd.pod @@ -303,7 +303,9 @@ interested in the following fields: Device support consists of the following routines: -=head4 long report(int level) +=head4 report + + long report(int level) This optional routine is called by the IOC command C and is passed the report level that was requested by the user. @@ -313,7 +315,9 @@ information at higher levels, or to select different types of information with different levels. Level zero should print no more than a small summary. -=head4 long init(int after) +=head4 init + + long init(int after) This optional routine is called twice at IOC initialization time. The first call happens before any of the C calls are made, with @@ -323,14 +327,14 @@ with C set to 1. =head4 init_record - init_record(precord) + long init_record(dbCommon *precord) This routine is optional. If provided, it is called by the record support C routine. =head4 get_ioint_info - get_ioint_info(int cmd,struct dbCommon *precord,IOSCANPVT *ppvt) + long get_ioint_info(int cmd, dbCommon *precord, IOSCANPVT *ppvt) This routine is called by the ioEventScan system each time the record is added or deleted from an I/O event scan list. cmd has the value (0,1) if the @@ -339,7 +343,7 @@ provided for any device type that can use the ioEvent scanner. =head4 read_wf - read_wf(precord) + long read_wf(waveformRecord *prec) This routine must provide a new input value. It returns the following values: @@ -361,38 +365,11 @@ The C<<< Soft Channel >>> device support module is provided to read values from other records and store them in arrays. If INP is a constant link, then read_wf does nothing. In this case, the record can be used to hold arrays written via dbPuts. If INP is a database or channel access link, the new array value is read -from the link. NORD is set. +from the link. NORD is set to the number of items in the array. This module places a value directly in VAL. -If the INP link type is constant, then NORD is set to zero. If the INP link type -is PV_LINK, then dbCaAddInlink is called by C. - -read_wf calls recGblGetLinkValue which performs the following steps: - -=over - -=item * - -If the INP link type is CONSTANT recGblGetLinkValue does nothing. - -=item * - -If the INP link type is DB_LINK, then dbGetLink is called to obtain a new input -value. If dbGetLink returns an error, a LINK_ALARM with a severity of -INVALID_ALARM is raised. - -=item * - -If the INP link type is CA_LINK, then dbCaGetLink is called to obtain a new -input value. If dbCaGetLink returns an error, a LINK_ALARM with a severity of -INVALID_ALARM is raised. - -=item * - -NORD is set to the number of values returned and read_wf returns. - -=back +If the INP link type is constant, then NORD is set to zero. =cut From 7f02f8a386f37a24507a0dfd7e51bf69466951a0 Mon Sep 17 00:00:00 2001 From: Freddie Akeroyd Date: Sat, 28 Mar 2020 15:51:49 +0100 Subject: [PATCH 51/59] Exclude VS2012 from -FS option (cherry picked from 7.0 / commit 4aee25e8 and e29a53f0) --- configure/os/CONFIG.win32-x86.win32-x86 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/configure/os/CONFIG.win32-x86.win32-x86 b/configure/os/CONFIG.win32-x86.win32-x86 index 7d6c11b6b..6a5078d60 100644 --- a/configure/os/CONFIG.win32-x86.win32-x86 +++ b/configure/os/CONFIG.win32-x86.win32-x86 @@ -141,13 +141,16 @@ STATIC_LDFLAGS= RANLIB= # -# option needed for parallel builds with Visual Studio 2015 onward -# +# option needed for parallel builds with Visual Studio 2013 onward +# VS2012 and above have VisualStudioVersion, so just need to exclude 2012 (11.0) # -FS Force Synchronous PDB Writes +# ifneq ($(VisualStudioVersion),) +ifneq ($(VisualStudioVersion),11.0) OPT_CXXFLAGS_NO += -FS OPT_CFLAGS_NO += -FS endif +endif # From 933e276e1a751985e796456ca8b0c9e90be81465 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 2 Apr 2020 15:47:10 -0500 Subject: [PATCH 52/59] Rolf Keitel's POD documentation for dbCommon (from the Wiki) I split his two "Fields Common to ..." sections back into separate docs, added links between them all, and made the appropriate build changes. Also added these and the aai/aao records to the documentation index. --- documentation/RecordReference.md | 17 +- src/ioc/db/Makefile | 6 +- src/ioc/db/RULES | 18 +- src/ioc/db/dbCommon.dbd | 261 --------------- src/ioc/db/dbCommon.dbd.pod | 514 ++++++++++++++++++++++++++++++ src/ioc/db/dbCommonInput.pod | 181 +++++++++++ src/ioc/db/dbCommonOutput.pod | 211 ++++++++++++ src/ioc/db/dbCommonRecord.dbd | 12 - src/ioc/db/dbCommonRecord.dbd.pod | 20 ++ 9 files changed, 956 insertions(+), 284 deletions(-) delete mode 100644 src/ioc/db/dbCommon.dbd create mode 100644 src/ioc/db/dbCommon.dbd.pod create mode 100644 src/ioc/db/dbCommonInput.pod create mode 100644 src/ioc/db/dbCommonOutput.pod delete mode 100644 src/ioc/db/dbCommonRecord.dbd create mode 100644 src/ioc/db/dbCommonRecord.dbd.pod diff --git a/documentation/RecordReference.md b/documentation/RecordReference.md index 469c39626..fbd5c1fe3 100644 --- a/documentation/RecordReference.md +++ b/documentation/RecordReference.md @@ -1,9 +1,17 @@ # Record Reference Documentation -The following documentation for the record types and menus include with Base was converted from the old EPICS Wiki pages and updated. This list does not include all of the available record types as some have not been documented yet. +The following documentation for the record types and menus include with Base was +converted from the old EPICS Wiki pages and updated. This list only includes the +record types supplied with Base. + +* [Fields Common to All Record Types](dbCommonRecord.html) +* [Fields Common to Input Record Types](dbCommonInputs.html) +* [Fields Common to Output Record Types](dbCommonOutputs.html) ## Record Types +* [Analog Array Input Record (aai)](aaiRecord.html) +* [Analog Array Output Record (aao)](aaoRecord.html) * [Analog Input Record (ai)](aiRecord.html) * [Analog Output Record (ao)](aoRecord.html) * [Array Subroutine Record (aSub)](aSubRecord.html) @@ -49,4 +57,9 @@ The following documentation for the record types and menus include with Base was ## Corrections and Updates -Corrections to these documents can be submitted as patch files to the EPICS core developers, or as merge requests or pull requests to the 3.15 branch of epics-base. The document sources can be found in the `src/std/rec` and `src/ioc/db` directories in files with extension `.dbd.pod`. The documentation format is an extended version of Perl POD, run `perldoc pod` for details. +Corrections to these documents can be submitted as patch files to the EPICS core +developers, or as merge requests or pull requests to the 3.15 branch of Base. +The document sources can be found in the `src/std/rec` and `src/ioc/db` +directories in files with extension `.dbd.pod`. The documentation source format +is a combination of the EPICS DBD file format with an extended version of Perl's +POD (plain old documentation); run `perldoc pod` for details of POD. diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index 5092092e9..dea95c923 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -4,7 +4,7 @@ # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. +# in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. @@ -59,6 +59,9 @@ DBDINC += dbCommon dbMenusPod = $(notdir $(wildcard ../db/menu*.dbd.pod)) HTMLS += $(patsubst %.dbd.pod,%.html,$(dbMenusPod)) +HTMLS += dbCommonRecord.html +HTMLS += dbCommonInput.html +HTMLS += dbCommonOutput.html dbCore_SRCS += dbLock.c dbCore_SRCS += dbAccess.c @@ -91,4 +94,3 @@ dbCore_SRCS += chfPlugin.c dbCore_SRCS += dbState.c dbCore_SRCS += dbUnitTest.c dbCore_SRCS += dbServer.c - diff --git a/src/ioc/db/RULES b/src/ioc/db/RULES index 41b07f5af..5fa0a6749 100644 --- a/src/ioc/db/RULES +++ b/src/ioc/db/RULES @@ -6,21 +6,25 @@ # Copyright (c) 2002 The Regents of the University of California, as # Operator of Los Alamos National Laboratory. # EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. +# in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/ioc/Makefile. -dbCommon.h$(DEP): $(IOCDIR)/db/dbCommonRecord.dbd $(IOCDIR)/db/RULES - @$(RM) $@ - @$(DBTORECORDTYPEH) -D -I ../db -o $(COMMONDEP_TARGET) $< > $@ +THESE_RULES := $(IOCDIR)/db/RULES -$(COMMON_DIR)/dbCommon.h: $(IOCDIR)/db/dbCommonRecord.dbd $(IOCDIR)/db/RULES +dbCommon.h$(DEP): $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES) + @$(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) @$(RM) $(notdir $@) - $(DBTORECORDTYPEH) -I ../db -o $(notdir $@) $< + $(DBTORECORDTYPEH) -I ../db -I $(COMMON_DIR) -o $(notdir $@) $< @$(MV) $(notdir $@) $@ -$(COMMON_DIR)/menuGlobal.dbd: $(IOCDIR)/db/Makefile $(IOCDIR)/db/RULES +$(COMMON_DIR)/menuGlobal.dbd: $(IOCDIR)/db/Makefile $(THESE_RULES) # This is a target-specific variable $(COMMON_DIR)/menuGlobal.dbd: DBDCAT_COMMAND = \ diff --git a/src/ioc/db/dbCommon.dbd b/src/ioc/db/dbCommon.dbd deleted file mode 100644 index aac8e9576..000000000 --- a/src/ioc/db/dbCommon.dbd +++ /dev/null @@ -1,261 +0,0 @@ -#************************************************************************* -# Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne -# National Laboratory. -# Copyright (c) 2002 The Regents of the University of California, as -# Operator of Los Alamos National Laboratory. -# EPICS BASE is distributed subject to a Software License Agreement found -# in file LICENSE that is included with this distribution. -#************************************************************************* - %#include "epicsTypes.h" - %#include "link.h" - field(NAME,DBF_STRING) { - prompt("Record Name") - special(SPC_NOMOD) - size(61) - } - field(DESC,DBF_STRING) { - prompt("Descriptor") - promptgroup("10 - Common") - size(41) - } - field(ASG,DBF_STRING) { - prompt("Access Security Group") - promptgroup("10 - Common") - special(SPC_AS) - size(29) - } - field(SCAN,DBF_MENU) { - prompt("Scan Mechanism") - promptgroup("20 - Scan") - special(SPC_SCAN) - interest(1) - menu(menuScan) - } - field(PINI,DBF_MENU) { - prompt("Process at iocInit") - promptgroup("20 - Scan") - interest(1) - menu(menuPini) - } - field(PHAS,DBF_SHORT) { - prompt("Scan Phase") - promptgroup("20 - Scan") - special(SPC_SCAN) - interest(1) - } - field(EVNT,DBF_STRING) { - prompt("Event Name") - promptgroup("20 - Scan") - special(SPC_SCAN) - size(40) - interest(1) - } - field(TSE,DBF_SHORT) { - prompt("Time Stamp Event") - promptgroup("20 - Scan") - interest(1) - } - field(TSEL,DBF_INLINK) { - prompt("Time Stamp Link") - promptgroup("20 - Scan") - interest(1) - } - field(DTYP,DBF_DEVICE) { - prompt("Device Type") - promptgroup("10 - Common") - interest(1) - } - field(DISV,DBF_SHORT) { - prompt("Disable Value") - promptgroup("20 - Scan") - initial("1") - } - field(DISA,DBF_SHORT) { - prompt("Disable") - } - field(SDIS,DBF_INLINK) { - prompt("Scanning Disable") - promptgroup("20 - Scan") - interest(1) - } - %#include "epicsMutex.h" - field(MLOK,DBF_NOACCESS) { - prompt("Monitor lock") - special(SPC_NOMOD) - interest(4) - extra("epicsMutexId mlok") - } - %#include "ellLib.h" - field(MLIS,DBF_NOACCESS) { - prompt("Monitor List") - special(SPC_NOMOD) - interest(4) - extra("ELLLIST mlis") - } - field(DISP,DBF_UCHAR) { - prompt("Disable putField") - } - field(PROC,DBF_UCHAR) { - prompt("Force Processing") - pp(TRUE) - interest(3) - } - field(STAT,DBF_MENU) { - prompt("Alarm Status") - special(SPC_NOMOD) - menu(menuAlarmStat) - initial("UDF") - } - field(SEVR,DBF_MENU) { - prompt("Alarm Severity") - special(SPC_NOMOD) - menu(menuAlarmSevr) - } - field(NSTA,DBF_MENU) { - prompt("New Alarm Status") - special(SPC_NOMOD) - interest(2) - menu(menuAlarmStat) - } - field(NSEV,DBF_MENU) { - prompt("New Alarm Severity") - special(SPC_NOMOD) - interest(2) - menu(menuAlarmSevr) - } - field(ACKS,DBF_MENU) { - prompt("Alarm Ack Severity") - special(SPC_NOMOD) - interest(2) - menu(menuAlarmSevr) - } - field(ACKT,DBF_MENU) { - prompt("Alarm Ack Transient") - promptgroup("70 - Alarm") - special(SPC_NOMOD) - interest(2) - menu(menuYesNo) - initial("YES") - } - field(DISS,DBF_MENU) { - prompt("Disable Alarm Sevrty") - promptgroup("70 - Alarm") - interest(1) - menu(menuAlarmSevr) - } - field(LCNT,DBF_UCHAR) { - prompt("Lock Count") - special(SPC_NOMOD) - interest(2) - } - field(PACT,DBF_UCHAR) { - prompt("Record active") - special(SPC_NOMOD) - interest(1) - } - field(PUTF,DBF_UCHAR) { - prompt("dbPutField process") - special(SPC_NOMOD) - interest(1) - } - field(RPRO,DBF_UCHAR) { - prompt("Reprocess ") - special(SPC_NOMOD) - interest(1) - } - field(ASP,DBF_NOACCESS) { - prompt("Access Security Pvt") - special(SPC_NOMOD) - interest(4) - extra("struct asgMember *asp") - } - field(PPN,DBF_NOACCESS) { - prompt("pprocessNotify") - special(SPC_NOMOD) - interest(4) - extra("struct processNotify *ppn") - } - field(PPNR,DBF_NOACCESS) { - prompt("pprocessNotifyRecord") - special(SPC_NOMOD) - interest(4) - extra("struct processNotifyRecord *ppnr") - } - field(SPVT,DBF_NOACCESS) { - prompt("Scan Private") - special(SPC_NOMOD) - interest(4) - extra("struct scan_element *spvt") - } - field(RSET,DBF_NOACCESS) { - prompt("Address of RSET") - special(SPC_NOMOD) - interest(4) - extra("struct rset *rset") - } - field(DSET,DBF_NOACCESS) { - prompt("DSET address") - special(SPC_NOMOD) - interest(4) - extra("struct dset *dset") - } - field(DPVT,DBF_NOACCESS) { - prompt("Device Private") - special(SPC_NOMOD) - interest(4) - extra("void *dpvt") - } - field(RDES,DBF_NOACCESS) { - prompt("Address of dbRecordType") - special(SPC_NOMOD) - interest(4) - extra("struct dbRecordType *rdes") - } - field(LSET,DBF_NOACCESS) { - prompt("Lock Set") - special(SPC_NOMOD) - interest(4) - extra("struct lockRecord *lset") - } - field(PRIO,DBF_MENU) { - prompt("Scheduling Priority") - promptgroup("20 - Scan") - special(SPC_SCAN) - interest(1) - menu(menuPriority) - } - field(TPRO,DBF_UCHAR) { - prompt("Trace Processing") - } - field(BKPT,DBF_NOACCESS) { - prompt("Break Point") - special(SPC_NOMOD) - interest(1) - extra("char bkpt") - } - field(UDF,DBF_UCHAR) { - prompt("Undefined") - promptgroup("10 - Common") - pp(TRUE) - interest(1) - initial("1") - } - field(UDFS,DBF_MENU) { - prompt("Undefined Alarm Sevrty") - promptgroup("70 - Alarm") - interest(1) - menu(menuAlarmSevr) - initial("INVALID") - } - %#include "epicsTime.h" - field(TIME,DBF_NOACCESS) { - prompt("Time") - special(SPC_NOMOD) - interest(2) - extra("epicsTimeStamp time") - } - field(FLNK,DBF_FWDLINK) { - prompt("Forward Process Link") - promptgroup("20 - Scan") - interest(1) - } diff --git a/src/ioc/db/dbCommon.dbd.pod b/src/ioc/db/dbCommon.dbd.pod new file mode 100644 index 000000000..d3829e1f6 --- /dev/null +++ b/src/ioc/db/dbCommon.dbd.pod @@ -0,0 +1,514 @@ +#************************************************************************* +# Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +=head3 Operator Display Parameters + +The B field contains the record name which must be unique within an +EPICS Channel Access name space. The name is supplied by the application +developer and is the means of identifying a specific record. The name has a +maximum length of 60 characters and should use only this limited set of +characters: + + a-z A-Z 0-9 _ - : [ ] < > ; + +The B field may be set to provide a meaningful description of the +record's purpose. Maximum length is 40 characters. + +=fields NAME, DESC + +=cut + + %#include "epicsTypes.h" + %#include "link.h" + field(NAME,DBF_STRING) { + prompt("Record Name") + special(SPC_NOMOD) + size(61) + } + field(DESC,DBF_STRING) { + prompt("Descriptor") + promptgroup("10 - Common") + size(41) + } + field(ASG,DBF_STRING) { + prompt("Access Security Group") + promptgroup("10 - Common") + special(SPC_AS) + size(29) + } + +=head3 Scan Fields + +These fields contain information related to how and when a record processes. A +few records have unique fields that also affect how they process. These +fields, if any, will be listed and explained in the section for each record. + +The B field specifies the scanning period for periodic record scans or the +scan type for non-periodic record scans. The default set of values for SCAN can +be found in L. + +The choices provided by this menu are: + +=over + +=item * + +C for the record scan to be triggered by other records or Channel +Access + +=item * + +C for event-driven scan + +=item * + +C for interrupt-driven scan + +=item * + +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: + + 1 hour + 0.5 hours + 15 minutes + 3 seconds + 1 second + 2 Hertz + +The B field specifies record processing at initialization. If it is set +to YES during database configuration, the record is processed once at IOC +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 +linked passive records to enforce the order of processing rather than a phase +number. + +The B field specifies an event number. This event number is used if the +SCAN field is set to C. All records with scan type C and the +same EVNT value will be processed when a call to post_event for EVNT is made. +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 +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 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. + +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 +are updated whenever a database link is added, removed or altered. + +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 +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 for internal use by the scanning system. + +=fields SCAN, PINI, PHAS, EVNT, PRIO, DISV, DISA, SDIS, PROC, DISS, LCNT, PACT, FLNK, SPVT + +=cut + + field(SCAN,DBF_MENU) { + prompt("Scan Mechanism") + promptgroup("20 - Scan") + special(SPC_SCAN) + interest(1) + menu(menuScan) + } + field(PINI,DBF_MENU) { + prompt("Process at iocInit") + promptgroup("20 - Scan") + interest(1) + menu(menuPini) + } + field(PHAS,DBF_SHORT) { + prompt("Scan Phase") + promptgroup("20 - Scan") + special(SPC_SCAN) + interest(1) + } + field(EVNT,DBF_STRING) { + prompt("Event Name") + promptgroup("20 - Scan") + special(SPC_SCAN) + size(40) + interest(1) + } + field(TSE,DBF_SHORT) { + prompt("Time Stamp Event") + promptgroup("20 - Scan") + interest(1) + } + field(TSEL,DBF_INLINK) { + prompt("Time Stamp Link") + promptgroup("20 - Scan") + interest(1) + } + field(DTYP,DBF_DEVICE) { + prompt("Device Type") + promptgroup("10 - Common") + interest(1) + } + field(DISV,DBF_SHORT) { + prompt("Disable Value") + promptgroup("20 - Scan") + initial("1") + } + field(DISA,DBF_SHORT) { + prompt("Disable") + } + field(SDIS,DBF_INLINK) { + prompt("Scanning Disable") + promptgroup("20 - Scan") + interest(1) + } + %#include "epicsMutex.h" + field(MLOK,DBF_NOACCESS) { + prompt("Monitor lock") + special(SPC_NOMOD) + interest(4) + extra("epicsMutexId mlok") + } + %#include "ellLib.h" + field(MLIS,DBF_NOACCESS) { + prompt("Monitor List") + special(SPC_NOMOD) + interest(4) + extra("ELLLIST mlis") + } + field(DISP,DBF_UCHAR) { + prompt("Disable putField") + } + field(PROC,DBF_UCHAR) { + prompt("Force Processing") + pp(TRUE) + interest(3) + } + +=head3 Alarm Fields + +These fields indicate the status and severity of alarms, or else determine the +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 +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 field contains the highest unacknowledged alarm severity. + +The B field specifies if 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. + +=fields STAT, SEVR, NSTA, NSEV, ACKS, ACKT, UDF + +=cut + + field(STAT,DBF_MENU) { + prompt("Alarm Status") + special(SPC_NOMOD) + menu(menuAlarmStat) + initial("UDF") + } + field(SEVR,DBF_MENU) { + prompt("Alarm Severity") + special(SPC_NOMOD) + menu(menuAlarmSevr) + } + field(NSTA,DBF_MENU) { + prompt("New Alarm Status") + special(SPC_NOMOD) + interest(2) + menu(menuAlarmStat) + } + field(NSEV,DBF_MENU) { + prompt("New Alarm Severity") + special(SPC_NOMOD) + interest(2) + menu(menuAlarmSevr) + } + field(ACKS,DBF_MENU) { + prompt("Alarm Ack Severity") + special(SPC_NOMOD) + interest(2) + menu(menuAlarmSevr) + } + field(ACKT,DBF_MENU) { + prompt("Alarm Ack Transient") + promptgroup("70 - Alarm") + special(SPC_NOMOD) + interest(2) + menu(menuYesNo) + initial("YES") + } + field(DISS,DBF_MENU) { + prompt("Disable Alarm Sevrty") + promptgroup("70 - Alarm") + interest(1) + menu(menuAlarmSevr) + } + field(LCNT,DBF_UCHAR) { + prompt("Lock Count") + special(SPC_NOMOD) + interest(2) + } + field(PACT,DBF_UCHAR) { + prompt("Record active") + special(SPC_NOMOD) + interest(1) + } + field(PUTF,DBF_UCHAR) { + prompt("dbPutField process") + special(SPC_NOMOD) + interest(1) + } + field(RPRO,DBF_UCHAR) { + prompt("Reprocess ") + special(SPC_NOMOD) + interest(1) + } + field(ASP,DBF_NOACCESS) { + prompt("Access Security Pvt") + special(SPC_NOMOD) + interest(4) + extra("struct asgMember *asp") + } + field(PPN,DBF_NOACCESS) { + prompt("pprocessNotify") + special(SPC_NOMOD) + interest(4) + extra("struct processNotify *ppn") + } + field(PPNR,DBF_NOACCESS) { + prompt("pprocessNotifyRecord") + special(SPC_NOMOD) + interest(4) + extra("struct processNotifyRecord *ppnr") + } + field(SPVT,DBF_NOACCESS) { + prompt("Scan Private") + special(SPC_NOMOD) + interest(4) + extra("struct scan_element *spvt") + } + +=head3 Device Fields + +The B field contains the address of the Record Support Entry Table. See +the Application Developers Guide for details on usage. + +The B field contains the address of Device Support Entry Table. The +value of this field is determined at IOC initialization time. Record support +routines use this field to locate their device support routines. + +The B field is is for private use of the device support modules. + +=fields RSET, DSET, DPVT + +=cut + + field(RSET,DBF_NOACCESS) { + prompt("Address of RSET") + special(SPC_NOMOD) + interest(4) + extra("struct rset *rset") + } + field(DSET,DBF_NOACCESS) { + prompt("DSET address") + special(SPC_NOMOD) + interest(4) + extra("struct dset *dset") + } + field(DPVT,DBF_NOACCESS) { + prompt("Device Private") + special(SPC_NOMOD) + interest(4) + extra("void *dpvt") + } + field(RDES,DBF_NOACCESS) { + prompt("Address of dbRecordType") + special(SPC_NOMOD) + interest(4) + extra("struct dbRecordType *rdes") + } + field(LSET,DBF_NOACCESS) { + prompt("Lock Set") + special(SPC_NOMOD) + interest(4) + extra("struct lockRecord *lset") + } + field(PRIO,DBF_MENU) { + prompt("Scheduling Priority") + promptgroup("20 - Scan") + special(SPC_SCAN) + interest(1) + menu(menuPriority) + } + +=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 indicates if there is a breakpoint set at this record. This +supports setting a debug breakpoint in the record processing. STEP through +database processing can be supported using this. + +=fields TPRO, BKPT + + +=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 field is a field for private use of 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 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 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 is the head of the list of 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. + +The B field contains the address of a putNotify callback. + +The B field contains the next record for PutNotify. + +The B field is set to TRUE if dbPutField caused the current record +processing. + +The B field contains the address of dbRecordType + +The B field specifies a reprocessing of the record when current +processing completes. + +The B