From 2d1d14e4b49f5c5eafef5afd570b51bda8ddd3c4 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 6 Jun 2017 23:18:55 -0400 Subject: [PATCH 01/47] Make rsrv register itself using a DBD file This allows the rsrv to be excluded by removing 'include rsrv.dbd' from a local copy of the base.dbd file. Server initialization, running and pausing are moved to new methods of the dbServer object, and the existing registration path is converted to just use the new rsrv.dbd file. --- src/ioc/db/dbServer.c | 16 ++++++++++++ src/ioc/db/dbServer.h | 11 ++++++++ src/ioc/misc/iocInit.c | 14 +++++----- src/ioc/misc/iocshRegisterCommon.c | 4 +-- src/ioc/rsrv/Makefile | 6 ++--- src/ioc/rsrv/caservertask.c | 42 ++++++++++++++++-------------- src/ioc/rsrv/rsrv.dbd | 3 +++ src/ioc/rsrv/rsrv.h | 6 ++--- src/ioc/rsrv/rsrvIocRegister.c | 11 +++++--- src/ioc/rsrv/rsrvIocRegister.h | 25 ------------------ src/std/softIoc/base.dbd | 2 ++ 11 files changed, 75 insertions(+), 65 deletions(-) create mode 100644 src/ioc/rsrv/rsrv.dbd delete mode 100644 src/ioc/rsrv/rsrvIocRegister.h diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index bc1094ce7..cb3830230 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -56,3 +56,19 @@ int dbServerClient(char *pBuf, size_t bufSize) return -1; } +#define STARTSTOP(routine, method) \ +void routine(void) \ +{ \ + dbServer *psrv = (dbServer *)ellFirst(&serverList); \ +\ + while (psrv) { \ + if (psrv->method) \ + psrv->method(); \ + psrv = (dbServer *)ellNext(&psrv->node); \ + } \ +} + +STARTSTOP(dbInitServers, init) +STARTSTOP(dbRunServers, run) +STARTSTOP(dbPauseServers, pause) +STARTSTOP(dbStopServers, stop) diff --git a/src/ioc/db/dbServer.h b/src/ioc/db/dbServer.h index 345468676..0ed7d3799 100644 --- a/src/ioc/db/dbServer.h +++ b/src/ioc/db/dbServer.h @@ -36,6 +36,12 @@ typedef struct dbServer { /* Get identity of client initiating the calling thread */ /* Must return 0 (OK), or -1 (ERROR) from unknown threads */ int (* client) (char *pBuf, size_t bufSize); + + /* Start/stop methods */ + void (* init) (void); + void (* run) (void); + void (* pause) (void); + void (* stop) (void); } dbServer; @@ -51,6 +57,11 @@ epicsShareFunc void dbsr(unsigned level); epicsShareFunc int dbServerClient(char *pBuf, size_t bufSize); +epicsShareFunc void dbInitServers(void); +epicsShareFunc void dbRunServers(void); +epicsShareFunc void dbPauseServers(void); +epicsShareFunc void dbStopServers(void); + #ifdef __cplusplus } #endif diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 98f7dae11..738d9b472 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -6,7 +6,7 @@ * Copyright (c) 2013 Helmholtz-Zentrum Berlin * für Materialien und Energie GmbH. * 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. \*************************************************************************/ /* * Original Author: Marty Kraimer @@ -53,6 +53,7 @@ #include "dbLock.h" #include "dbNotify.h" #include "dbScan.h" +#include "dbServer.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" #include "devSup.h" @@ -69,7 +70,6 @@ #include "registryDriverSupport.h" #include "registryJLinks.h" #include "registryRecordType.h" -#include "rsrv.h" static enum { iocVirgin, iocBuilding, iocBuilt, iocRunning, iocPaused, iocStopped @@ -203,8 +203,7 @@ int iocBuild(void) status = iocBuild_2(); if (status) return status; - /* Start CA server threads */ - rsrv_init(); + dbInitServers(); status = iocBuild_3(); @@ -247,7 +246,8 @@ int iocRun(void) if (iocState == iocBuilt) initHookAnnounce(initHookAfterInterruptAccept); - rsrv_run(); + dbRunServers(); + initHookAnnounce(initHookAfterCaServerRunning); if (iocState == iocBuilt) initHookAnnounce(initHookAtEnd); @@ -268,7 +268,7 @@ int iocPause(void) } initHookAnnounce(initHookAtIocPause); - rsrv_pause(); + dbPauseServers(); initHookAnnounce(initHookAfterCaServerPaused); dbCaPause(); @@ -421,7 +421,7 @@ static void initRecSup(void) static void initDevSup(void) { dbRecordType *pdbRecordType; - + for (pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { diff --git a/src/ioc/misc/iocshRegisterCommon.c b/src/ioc/misc/iocshRegisterCommon.c index fefa716b9..4f8ce6646 100644 --- a/src/ioc/misc/iocshRegisterCommon.c +++ b/src/ioc/misc/iocshRegisterCommon.c @@ -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. \*************************************************************************/ #include "envDefs.h" @@ -21,7 +21,6 @@ #include "iocshRegisterCommon.h" #include "miscIocRegister.h" #include "registryIocRegister.h" -#include "rsrvIocRegister.h" #define quote(v) #v #define str(v) quote(v) @@ -51,7 +50,6 @@ void iocshRegisterCommon(void) dbIocRegister(); dbtoolsIocRegister(); asIocRegister(); - rsrvIocRegister(); miscIocRegister(); libComRegister(); } diff --git a/src/ioc/rsrv/Makefile b/src/ioc/rsrv/Makefile index ba6ed6bd6..8ad7d01c4 100644 --- a/src/ioc/rsrv/Makefile +++ b/src/ioc/rsrv/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. @@ -16,7 +16,8 @@ caserverio_INCLUDES = -I$(SRC)/ca/client camessage_INCLUDES = -I$(SRC)/ca/client INC += rsrv.h -INC += rsrvIocRegister.h + +DBD += rsrv.dbd dbCore_SRCS += caserverio.c dbCore_SRCS += caservertask.c @@ -25,4 +26,3 @@ dbCore_SRCS += camessage.c dbCore_SRCS += cast_server.c dbCore_SRCS += online_notify.c dbCore_SRCS += rsrvIocRegister.c - diff --git a/src/ioc/rsrv/caservertask.c b/src/ioc/rsrv/caservertask.c index 37c721f8a..eb18b0d40 100644 --- a/src/ioc/rsrv/caservertask.c +++ b/src/ioc/rsrv/caservertask.c @@ -469,18 +469,11 @@ void rsrv_build_addr_lists(void) } } -static dbServer rsrv_server = { - ELLNODE_INIT, - "rsrv", - casr, - casStatsFetch, - casClientInitiatingCurrentThread -}; - /* * rsrv_init () */ -int rsrv_init (void) +static +void rsrv_init (void) { long maxBytesAsALong; long status; @@ -499,8 +492,6 @@ int rsrv_init (void) rsrvCurrentClient = epicsThreadPrivateCreate (); - dbRegisterServer(&rsrv_server); - if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) { ca_server_port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT, (unsigned short) CA_SERVER_PORT ); @@ -768,26 +759,22 @@ int rsrv_init (void) &rsrv_online_notify_task, NULL); epicsEventMustWait(beacon_startStopEvent); - - return RSRV_OK; } -int rsrv_run (void) +static +void rsrv_run (void) { castcp_ctl = ctlRun; casudp_ctl = ctlRun; beacon_ctl = ctlRun; - - return RSRV_OK; } -int rsrv_pause (void) +static +void rsrv_pause (void) { beacon_ctl = ctlPause; casudp_ctl = ctlPause; castcp_ctl = ctlPause; - - return RSRV_OK; } static unsigned countChanListBytes ( @@ -1551,3 +1538,20 @@ void casStatsFetch ( unsigned *pChanCount, unsigned *pCircuitCount ) } UNLOCK_CLIENTQ; } + + +static dbServer rsrv_server = { + ELLNODE_INIT, + "rsrv", + casr, + casStatsFetch, + casClientInitiatingCurrentThread, + rsrv_init, + rsrv_run, + rsrv_pause +}; + +void rsrv_register_server(void) +{ + dbRegisterServer(&rsrv_server); +} diff --git a/src/ioc/rsrv/rsrv.dbd b/src/ioc/rsrv/rsrv.dbd new file mode 100644 index 000000000..0c3118f82 --- /dev/null +++ b/src/ioc/rsrv/rsrv.dbd @@ -0,0 +1,3 @@ +# This DBD file links the RSRV CA server into the IOC + +registrar(rsrvRegistrar) diff --git a/src/ioc/rsrv/rsrv.h b/src/ioc/rsrv/rsrv.h index 10947cdf0..cb966df2f 100644 --- a/src/ioc/rsrv/rsrv.h +++ b/src/ioc/rsrv/rsrv.h @@ -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. \*************************************************************************/ /* @@ -27,9 +27,7 @@ extern "C" { #endif -epicsShareFunc int rsrv_init(void); -epicsShareFunc int rsrv_run(void); -epicsShareFunc int rsrv_pause(void); +epicsShareFunc void rsrv_register_server(void); epicsShareFunc void casr (unsigned level); epicsShareFunc int casClientInitiatingCurrentThread ( diff --git a/src/ioc/rsrv/rsrvIocRegister.c b/src/ioc/rsrv/rsrvIocRegister.c index 7a01fc9f6..74336817d 100644 --- a/src/ioc/rsrv/rsrvIocRegister.c +++ b/src/ioc/rsrv/rsrvIocRegister.c @@ -4,14 +4,14 @@ * 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. \*************************************************************************/ #include "iocsh.h" +#include "epicsExport.h" #define epicsExportSharedSymbols #include "rsrv.h" -#include "rsrvIocRegister.h" /* casr */ static const iocshArg casrArg0 = { "level",iocshArgInt}; @@ -22,8 +22,11 @@ static void casrCallFunc(const iocshArgBuf *args) casr(args[0].ival); } - -void rsrvIocRegister(void) +static +void rsrvRegistrar(void) { + rsrv_register_server(); iocshRegister(&casrFuncDef,casrCallFunc); } + +epicsExportRegistrar(rsrvRegistrar); diff --git a/src/ioc/rsrv/rsrvIocRegister.h b/src/ioc/rsrv/rsrvIocRegister.h deleted file mode 100644 index f4c7e31f1..000000000 --- a/src/ioc/rsrv/rsrvIocRegister.h +++ /dev/null @@ -1,25 +0,0 @@ -/*************************************************************************\ -* Copyright (c) 2007 The University of Chicago, as Operator of Argonne -* National Laboratory. -* Copyright (c) 2002 The Regents of the University of California, as -* Operator of Los Alamos National Laboratory. -* EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ - -#ifndef INC_rsrvIocRegister_H -#define INC_rsrvIocRegister_H - -#include "shareLib.h" - -#ifdef __cplusplus -extern "C" { -#endif - -epicsShareFunc void rsrvIocRegister(void); - -#ifdef __cplusplus -} -#endif - -#endif /* INC_rsrvIocRegister_H */ diff --git a/src/std/softIoc/base.dbd b/src/std/softIoc/base.dbd index 58f4884ea..564a83845 100644 --- a/src/std/softIoc/base.dbd +++ b/src/std/softIoc/base.dbd @@ -26,3 +26,5 @@ include "asSub.dbd" # IOC Core variables include "dbCore.dbd" +# RSRV server +include "rsrv.dbd" From 6efe292494a8ad41ee5d0d298787195375abc8c1 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 13 Jun 2017 23:36:30 -0500 Subject: [PATCH 02/47] Add EPICS_IOC_IGNORE_SERVERS environment variable dbServers cannot be registered when their names appear in this variable, so they should remain inactive. This feature is mainly intended for debugging, so the crude name search should be OK. --- configure/CONFIG_ENV | 3 +++ src/ioc/db/dbServer.c | 19 +++++++++++++++++++ src/ioc/db/dbServer.h | 2 ++ src/libCom/env/envDefs.h | 1 + 4 files changed, 25 insertions(+) diff --git a/configure/CONFIG_ENV b/configure/CONFIG_ENV index 1565b2ec9..59e72d883 100644 --- a/configure/CONFIG_ENV +++ b/configure/CONFIG_ENV @@ -47,6 +47,9 @@ EPICS_CAS_SERVER_PORT= EPICS_CAS_INTF_ADDR_LIST="" EPICS_CAS_IGNORE_ADDR_LIST="" +# Servers to disable +EPICS_IOC_IGNORE_SERVERS="" + # Log Server: # EPICS_IOC_LOG_PORT Log server port number etc. EPICS_IOC_LOG_PORT=7004 diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index cb3830230..e75622904 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -10,8 +10,10 @@ */ #include +#include #include "ellLib.h" +#include "envDefs.h" #include "epicsStdio.h" #define epicsExportSharedSymbols @@ -22,6 +24,23 @@ static ELLLIST serverList = ELLLIST_INIT; void dbRegisterServer(dbServer *psrv) { + const char * ignore = envGetConfigParamPtr(&EPICS_IOC_IGNORE_SERVERS); + + if (!psrv || !psrv->name) + return; + + if (strchr(psrv->name, ' ')) { + fprintf(stderr, "dbRegisterServer: Bad server name '%s'\n", + psrv->name); + return; + } + + if (ignore && strstr(ignore, psrv->name)) { + fprintf(stderr, "dbRegisterServer: Ignoring '%s', per environment\n", + psrv->name); + return; + } + if (ellNext(&psrv->node)) { fprintf(stderr, "dbRegisterServer: '%s' registered twice?\n", psrv->name); diff --git a/src/ioc/db/dbServer.h b/src/ioc/db/dbServer.h index 0ed7d3799..83d065ce9 100644 --- a/src/ioc/db/dbServer.h +++ b/src/ioc/db/dbServer.h @@ -25,6 +25,8 @@ extern "C" { typedef struct dbServer { ELLNODE node; + + /* A short server identifier, printable, no spaces */ const char *name; /* Print level-dependent status report to stdout */ diff --git a/src/libCom/env/envDefs.h b/src/libCom/env/envDefs.h index 650f8ee58..13df94733 100644 --- a/src/libCom/env/envDefs.h +++ b/src/libCom/env/envDefs.h @@ -65,6 +65,7 @@ epicsShareExtern const ENV_PARAM EPICS_BUILD_OS_CLASS; epicsShareExtern const ENV_PARAM EPICS_BUILD_TARGET_ARCH; epicsShareExtern const ENV_PARAM EPICS_TIMEZONE; epicsShareExtern const ENV_PARAM EPICS_TS_NTP_INET; +epicsShareExtern const ENV_PARAM EPICS_IOC_IGNORE_SERVERS; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_PORT; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_INET; epicsShareExtern const ENV_PARAM EPICS_IOC_LOG_FILE_LIMIT; From c2d22ed925a90ae965c708801209f3700730022c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 24 Jun 2017 00:04:20 -0500 Subject: [PATCH 03/47] Added Doxygen comments for dbServer API --- src/ioc/db/dbServer.h | 122 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 108 insertions(+), 14 deletions(-) diff --git a/src/ioc/db/dbServer.h b/src/ioc/db/dbServer.h index 83d065ce9..f1a48ab40 100644 --- a/src/ioc/db/dbServer.h +++ b/src/ioc/db/dbServer.h @@ -5,8 +5,17 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* - * Author: Andrew Johnson +/** + * @file dbServer.h + * @author Andrew Johnson + * + * @brief The IOC's interface to the server layers that publish its PVs. + * + * All server layers which publish IOC record data should initialize a + * dbServer structure and register it with the IOC. The methods that + * the dbServer interface provides allow the IOC to start, pause and stop + * the servers together, and to provide status and debugging information + * to the IOC user/developer through a common set of commands. */ #ifndef INC_dbServer_H @@ -21,47 +30,132 @@ extern "C" { #endif -/* Server information structure */ +/** @brief Server information structure. + * + * Every server layer should initialize and register an instance of this + * structure with the IOC by passing it to the dbRegisterServer() routine. + * + * All methods in this struct are optional; use @c NULL if a server is + * unable to support a particular operation (or if it hasn't been + * implemented yet). + */ typedef struct dbServer { + /** @brief Linked list node; initialize to @c ELLNODE_INIT */ ELLNODE node; - /* A short server identifier, printable, no spaces */ + /** @brief A short server identifier; printable, with no spaces */ const char *name; - /* Print level-dependent status report to stdout */ + /** @brief Print level-dependent status report to stdout. + * + * @param level Interest level, specifies how much detail to print. + */ void (* report) (unsigned level); - /* Get number of channels and clients connected */ + /** @brief Get number of channels and clients currently connected. + * + * @param channels NULL or pointer for returning channel count. + * @param clients NULL or pointer for returning client count. + */ void (* stats) (unsigned *channels, unsigned *clients); - /* Get identity of client initiating the calling thread */ - /* Must return 0 (OK), or -1 (ERROR) from unknown threads */ + /** @brief Get identity of client initiating the calling thread. + * + * Must fill in the buffer with the client's identity when called from a + * thread that belongs to this server layer. For other threads, the + * method should do nothing, just return -1. + * @param pBuf Buffer for client identity string. + * @param bufSize Number of chars available in pBuf. + * @return -1 means calling thread is not owned by this server. + * 0 means the thread was recognized and pBuf has been filled in. + */ int (* client) (char *pBuf, size_t bufSize); - /* Start/stop methods */ + /** @name Control Methods + * These control methods for the server will be called by routines + * related to iocInit for all registered servers in turn when the IOC + * is being initialized, run, paused and stopped respectively. + * + * @{ + */ + + /** @brief Server init method. + * + * Called for all registered servers by dbInitServers(). + */ void (* init) (void); + + /** @brief Server run method. + * + * Called for all registered servers by dbRunServers(). + */ void (* run) (void); + + /** @brief Server pause method. + * + * Called for all registered servers by dbPauseServers(). + */ void (* pause) (void); + + /** @brief Server stop method. + * + * Called for all registered servers by dbStopServers(). + */ void (* stop) (void); + + /** @} + */ } dbServer; +/** @brief Register a server layer with the IOC + * + * This should only be called once for each server layer. + * @param psrv Server information structure for the server + */ epicsShareFunc void dbRegisterServer(dbServer *psrv); -/* Extra routines could be added if/when needed: - * - * epicsShareFunc const dbServer* dbFindServer(const char *name); - * epicsShareFunc void dbIterateServers(srvIterFunc func, void *user); +/** @brief Print dbServer Reports. +* + * Calls the report methods of all registered servers. + * This routine is provided as an IOC Shell command. + * @param level Interest level, specifies how much detail to print. */ - epicsShareFunc void dbsr(unsigned level); +/** @brief Query servers for client's identity. + * + * This routine is called by code that wants to identify who (or what) + * is responsible for the thread which is currently running. Setting + * the @c TPRO field of a record is one way to trigger this; the identity + * of the calling thread is printed along with the record name whenever + * the record is subsequently processed. + */ epicsShareFunc int dbServerClient(char *pBuf, size_t bufSize); +/** @brief Initialize all registered servers. + * + * Calls all dbServer::init() methods. + */ epicsShareFunc void dbInitServers(void); + +/** @brief Run all registered servers. + * + * Calls all dbServer::run() methods. + */ epicsShareFunc void dbRunServers(void); + +/** @brief Pause all registered servers. + * + * Calls all dbServer::pause() methods. + */ epicsShareFunc void dbPauseServers(void); + +/** @brief Stop all registered servers. + * + * Calls all dbServer::stop() methods. + */ epicsShareFunc void dbStopServers(void); #ifdef __cplusplus From 17e6060d163cfccab1da3d9ecdcef30603e5bff8 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 23 Jul 2017 23:30:04 -0500 Subject: [PATCH 04/47] Added some dbServer unit tests --- src/ioc/db/dbServer.h | 4 + src/ioc/db/test/Makefile | 8 +- src/ioc/db/test/dbServerTest.c | 131 ++++++++++++++++++++++++++++++ src/ioc/db/test/epicsRunDbTests.c | 2 + 4 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 src/ioc/db/test/dbServerTest.c diff --git a/src/ioc/db/dbServer.h b/src/ioc/db/dbServer.h index f1a48ab40..c2ebdb467 100644 --- a/src/ioc/db/dbServer.h +++ b/src/ioc/db/dbServer.h @@ -16,6 +16,10 @@ * the dbServer interface provides allow the IOC to start, pause and stop * the servers together, and to provide status and debugging information * to the IOC user/developer through a common set of commands. + * + * @todo Should dbRegisterServer() return an error status value? + * No API is provided yet for calling stats() methods. + * Nothing in the IOC calls dbStopServers(), not sure where it should go. */ #ifndef INC_dbServer_H diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 37ec3da74..ff464f02a 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/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 the file LICENSE that is included with this distribution. +# in the file LICENSE that is included with this distribution. #************************************************************************* TOP=../../../.. @@ -94,6 +94,11 @@ dbStateTest_SRCS += dbStateTest.c testHarness_SRCS += dbStateTest.c TESTS += dbStateTest +TESTPROD_HOST += dbServerTest +dbServerTest_SRCS += dbServerTest.c +testHarness_SRCS += dbServerTest.c +TESTS += dbServerTest + TESTPROD_HOST += dbCaStatsTest dbCaStatsTest_SRCS += dbCaStatsTest.c dbCaStatsTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp @@ -192,4 +197,3 @@ dbStressLock$(DEP): $(COMMON_DIR)/xRecord.h devx$(DEP): $(COMMON_DIR)/xRecord.h scanIoTest$(DEP): $(COMMON_DIR)/xRecord.h xRecord$(DEP): $(COMMON_DIR)/xRecord.h - diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c new file mode 100644 index 000000000..6c474ecb4 --- /dev/null +++ b/src/ioc/db/test/dbServerTest.c @@ -0,0 +1,131 @@ +/*************************************************************************\ +* Copyright (c) 2017 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. + \*************************************************************************/ + +/* + * Author: Andrew Johnson + */ + +#include + +#include "dbServer.h" + +#include +#include + +enum { + NOTHING_CALLED, + REPORT_CALLED, + CLIENT_CALLED_UNKNOWN, + CLIENT_CALLED_KNOWN, + STATS_CALLED, + INIT_CALLED, + RUN_CALLED, + PAUSE_CALLED, + STOP_CALLED +} oneState; + +char *oneSim; + +void oneReport(unsigned level) +{ + oneState = REPORT_CALLED; +} + +void oneStats(unsigned *channels, unsigned *clients) +{ + oneState = STATS_CALLED; +} + +int oneClient(char *pbuf, size_t len) +{ + if (oneSim) { + strncpy(pbuf, oneSim, len); + oneState = CLIENT_CALLED_KNOWN; + oneSim = NULL; + return 0; + } + oneState = CLIENT_CALLED_UNKNOWN; + return -1; +} + +void oneInit(void) +{ + oneState = INIT_CALLED; +} + +void oneRun(void) +{ + oneState = RUN_CALLED; +} + +void onePause(void) +{ + oneState = PAUSE_CALLED; +} + +void oneStop(void) +{ + oneState = STOP_CALLED; +} + +dbServer one = { + ELLNODE_INIT, + "one", + oneReport, oneStats, oneClient, + oneInit, oneRun, onePause, oneStop +}; + +dbServer no_routines = { + ELLNODE_INIT, + "no-routines", + NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +MAIN(dbServerTest) +{ + char name[16]; + char *theName = "The One"; + int status; + + testPlan(0); + + testDiag("Registering dbServer 'one'"); + dbRegisterServer(&one); + testOk1(oneState == NOTHING_CALLED); + + testDiag("Registering dbServer 'no-routines'"); + dbRegisterServer(&no_routines); + + dbInitServers(); + testOk(oneState == INIT_CALLED, "dbInitServers"); + + dbRunServers(); + testOk(oneState == RUN_CALLED, "dbRunServers"); + + dbPauseServers(); + testOk(oneState == PAUSE_CALLED, "dbPauseServers"); + + dbStopServers(); + testOk(oneState == STOP_CALLED, "dbStopServers"); + + dbsr(0); + testOk(oneState == REPORT_CALLED, "dbsr"); + + oneSim = NULL; + name[0] = 0; + status = dbServerClient(name, sizeof(name)); + testOk(status == -1 && name[0] == 0, + "dbServerClient mismatch"); + + oneSim = theName; + name[0] = 0; + status = dbServerClient(name, sizeof(name)); + testOk(status == 0 && strcmp(name, theName) == 0, + "dbServerClient match"); + + return testDone(); +} diff --git a/src/ioc/db/test/epicsRunDbTests.c b/src/ioc/db/test/epicsRunDbTests.c index 69f6b082e..4036268fa 100644 --- a/src/ioc/db/test/epicsRunDbTests.c +++ b/src/ioc/db/test/epicsRunDbTests.c @@ -20,6 +20,7 @@ int testdbConvert(void); int callbackTest(void); int callbackParallelTest(void); int dbStateTest(void); +int dbServerTest(void); int dbCaStatsTest(void); int dbShutdownTest(void); int dbScanTest(void); @@ -41,6 +42,7 @@ void epicsRunDbTests(void) runTest(callbackTest); runTest(callbackParallelTest); runTest(dbStateTest); + runTest(dbServerTest); runTest(dbCaStatsTest); runTest(dbShutdownTest); runTest(dbScanTest); From ba9763f5513da59fd36b0d0699bcfffb3a5fd90d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 23 Jul 2017 23:35:03 -0500 Subject: [PATCH 05/47] Set test plan --- src/ioc/db/test/dbServerTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index 6c474ecb4..769a07f41 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -91,7 +91,7 @@ MAIN(dbServerTest) char *theName = "The One"; int status; - testPlan(0); + testPlan(8); testDiag("Registering dbServer 'one'"); dbRegisterServer(&one); From 2cf997ddbfb66f573a58c45d5ed23de158d8c914 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 25 Jul 2017 00:38:34 -0500 Subject: [PATCH 06/47] Test EPICS_IOC_IGNORE_SERVERS variable --- src/ioc/db/test/dbServerTest.c | 37 ++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index 769a07f41..83b3eb684 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -13,6 +13,7 @@ #include "dbServer.h" +#include #include #include @@ -73,25 +74,44 @@ void oneStop(void) } dbServer one = { - ELLNODE_INIT, - "one", + ELLNODE_INIT, "one", oneReport, oneStats, oneClient, oneInit, oneRun, onePause, oneStop }; + +/* Server layer for testing NULL methods */ + dbServer no_routines = { - ELLNODE_INIT, - "no-routines", - NULL, NULL, NULL, NULL, NULL, NULL, NULL + ELLNODE_INIT, "no-routines", + NULL, NULL, NULL, + NULL, NULL, NULL, NULL }; + +/* Server layer which should be disabled */ + +int disInitialized = 0; + +void disInit(void) +{ + disInitialized = 1; +} + +dbServer disabled = { + ELLNODE_INIT, "disabled", + NULL, NULL, NULL, + disInit, NULL, NULL, NULL +}; + + MAIN(dbServerTest) { char name[16]; char *theName = "The One"; int status; - testPlan(8); + testPlan(9); testDiag("Registering dbServer 'one'"); dbRegisterServer(&one); @@ -100,8 +120,13 @@ MAIN(dbServerTest) testDiag("Registering dbServer 'no-routines'"); dbRegisterServer(&no_routines); + epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none disabled nonexistent"); + testDiag("Registering dbServer 'disabled'"); + dbRegisterServer(&disabled); + dbInitServers(); testOk(oneState == INIT_CALLED, "dbInitServers"); + testOk(disInitialized == 0, "Disabled server not initialized"); dbRunServers(); testOk(oneState == RUN_CALLED, "dbRunServers"); From 461daadd5bf229dfc8ef871ddcd2ed0626037f04 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 25 Jul 2017 00:39:40 -0500 Subject: [PATCH 07/47] Release Notes entry --- documentation/RELEASE_NOTES.html | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 7c5ade339..2735e40da 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -17,6 +17,19 @@ --> +

Extend the dbServer API with init/run/pause/stop methods

+ +

This change permits the IOC to be built that omits the CA server (RSRV) by +removing its registrar entry which is now provided in the new rsrv.dbd +file. Other server layers can be built into the IOC (alongside RSRV or in place +of it) by registering them in a similar manner. The dbServer API is documented +with Doxygen comments in the header file.

+ +

Specific IOC server layers can be disabled at runtime by adding their name to +the environment variable EPICS_IOC_IGNORE_SERVERS (separated by spaces if more +than one should be ignored).

+ +

Changes made between 3.16.0.1 and 3.16.1

IOC Database Support for 64-bit integers

From 7c6b037252e90e13f2e219e88976c0c67ccacf4a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 29 Jul 2017 00:51:31 -0500 Subject: [PATCH 08/47] Add explicit "no servers" message to dbsr() Make dbsr explicitly say so if no dbServer layers have been registered with the dbServer API. --- src/ioc/db/dbServer.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index e75622904..9b7cccb8d 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -54,6 +54,11 @@ void dbsr(unsigned level) { dbServer *psrv = (dbServer *)ellFirst(&serverList); + if (!psrv) { + printf("No server layers registered with IOC\n"); + return; + } + while (psrv) { printf("Server '%s':\n", psrv->name); if (psrv->report) From d1f60e4c8c9babbf23046cc0e4506d2877e391a1 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 29 Jul 2017 01:30:45 -0500 Subject: [PATCH 09/47] dbRegisterServer: Don't ignore sub-string names Also adjusts the tests to check we do it properly. --- src/ioc/db/dbServer.c | 18 ++++++++++++++---- src/ioc/db/test/dbServerTest.c | 5 ++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index 9b7cccb8d..f20c63c4f 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -35,10 +35,20 @@ void dbRegisterServer(dbServer *psrv) return; } - if (ignore && strstr(ignore, psrv->name)) { - fprintf(stderr, "dbRegisterServer: Ignoring '%s', per environment\n", - psrv->name); - return; + if (ignore) { + size_t len = strlen(psrv->name); + const char *found; + while ((found = strstr(ignore, psrv->name))) { + /* Make sure the name isn't just a substring */ + if ((found == ignore || (found > ignore && found[-1] == ' ')) && + (found[len] == 0 || found[len] == ' ')) { + fprintf(stderr, "dbRegisterServer: Ignoring '%s', per environment\n", + psrv->name); + return; + } + /* It was, try again further down */ + ignore = found + len; + } } if (ellNext(&psrv->node)) { diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index 83b3eb684..c6dae1529 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -113,6 +113,9 @@ MAIN(dbServerTest) testPlan(9); + /* Prove that we handle substring names properly */ + epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); + testDiag("Registering dbServer 'one'"); dbRegisterServer(&one); testOk1(oneState == NOTHING_CALLED); @@ -120,7 +123,7 @@ MAIN(dbServerTest) testDiag("Registering dbServer 'no-routines'"); dbRegisterServer(&no_routines); - epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none disabled nonexistent"); + epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "disabled nonexistent"); testDiag("Registering dbServer 'disabled'"); dbRegisterServer(&disabled); From d1018d27a4b0cbd51dd6261c10b10fb075055530 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 19 Sep 2017 22:09:41 -0500 Subject: [PATCH 10/47] Fix check for double-registration, add test --- src/ioc/db/dbServer.c | 2 +- src/ioc/db/test/dbServerTest.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index f20c63c4f..31ba11d80 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -51,7 +51,7 @@ void dbRegisterServer(dbServer *psrv) } } - if (ellNext(&psrv->node)) { + if (ellNext(&psrv->node) || ellLast(&serverList) == &psrv->node) { fprintf(stderr, "dbRegisterServer: '%s' registered twice?\n", psrv->name); return; diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index c6dae1529..31ffbd8b6 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -120,6 +120,9 @@ MAIN(dbServerTest) dbRegisterServer(&one); testOk1(oneState == NOTHING_CALLED); + testDiag("Expect double-registration warning for 'one':"); + dbRegisterServer(&one); + testDiag("Registering dbServer 'no-routines'"); dbRegisterServer(&no_routines); From a861760dbb84bcca8c92ced22e5c64dbbaaa8e10 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 19 Sep 2017 22:24:06 -0500 Subject: [PATCH 11/47] Give dbRegisterServer a return value (0/-1) Add tests to check registration failures. --- src/ioc/db/dbServer.c | 11 ++++++----- src/ioc/db/dbServer.h | 5 ++--- src/ioc/db/test/dbServerTest.c | 19 +++++++++++++------ 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index 31ba11d80..47ed5d8e8 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -22,17 +22,17 @@ static ELLLIST serverList = ELLLIST_INIT; -void dbRegisterServer(dbServer *psrv) +int dbRegisterServer(dbServer *psrv) { const char * ignore = envGetConfigParamPtr(&EPICS_IOC_IGNORE_SERVERS); if (!psrv || !psrv->name) - return; + return -1; if (strchr(psrv->name, ' ')) { fprintf(stderr, "dbRegisterServer: Bad server name '%s'\n", psrv->name); - return; + return -1; } if (ignore) { @@ -44,7 +44,7 @@ void dbRegisterServer(dbServer *psrv) (found[len] == 0 || found[len] == ' ')) { fprintf(stderr, "dbRegisterServer: Ignoring '%s', per environment\n", psrv->name); - return; + return 0; } /* It was, try again further down */ ignore = found + len; @@ -54,10 +54,11 @@ void dbRegisterServer(dbServer *psrv) if (ellNext(&psrv->node) || ellLast(&serverList) == &psrv->node) { fprintf(stderr, "dbRegisterServer: '%s' registered twice?\n", psrv->name); - return; + return -1; } ellAdd(&serverList, &psrv->node); + return 0; } void dbsr(unsigned level) diff --git a/src/ioc/db/dbServer.h b/src/ioc/db/dbServer.h index c2ebdb467..1d184329c 100644 --- a/src/ioc/db/dbServer.h +++ b/src/ioc/db/dbServer.h @@ -17,8 +17,7 @@ * the servers together, and to provide status and debugging information * to the IOC user/developer through a common set of commands. * - * @todo Should dbRegisterServer() return an error status value? - * No API is provided yet for calling stats() methods. + * @todo No API is provided yet for calling stats() methods. * Nothing in the IOC calls dbStopServers(), not sure where it should go. */ @@ -118,7 +117,7 @@ typedef struct dbServer { * This should only be called once for each server layer. * @param psrv Server information structure for the server */ -epicsShareFunc void dbRegisterServer(dbServer *psrv); +epicsShareFunc int dbRegisterServer(dbServer *psrv); /** @brief Print dbServer Reports. * diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index 31ffbd8b6..e9c6556c5 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -104,6 +104,12 @@ dbServer disabled = { disInit, NULL, NULL, NULL }; +dbServer illegal = { + ELLNODE_INIT, "bad name", + NULL, NULL, NULL, + disInit, NULL, NULL, NULL +}; + MAIN(dbServerTest) { @@ -111,24 +117,24 @@ MAIN(dbServerTest) char *theName = "The One"; int status; - testPlan(9); + testPlan(14); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); testDiag("Registering dbServer 'one'"); - dbRegisterServer(&one); + testOk(dbRegisterServer(&one) == 0, "Registered 'one'"); testOk1(oneState == NOTHING_CALLED); - testDiag("Expect double-registration warning for 'one':"); - dbRegisterServer(&one); + testOk(dbRegisterServer(&one) != 0, "Duplicate registration rejected"); + testOk(dbRegisterServer(&illegal) != 0, "Illegal registration rejected"); testDiag("Registering dbServer 'no-routines'"); - dbRegisterServer(&no_routines); + testOk(dbRegisterServer(&no_routines) == 0, "Registered 'no-routines'"); epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "disabled nonexistent"); testDiag("Registering dbServer 'disabled'"); - dbRegisterServer(&disabled); + testOk(dbRegisterServer(&disabled) == 0, "Registration accepted"); dbInitServers(); testOk(oneState == INIT_CALLED, "dbInitServers"); @@ -143,6 +149,7 @@ MAIN(dbServerTest) dbStopServers(); testOk(oneState == STOP_CALLED, "dbStopServers"); + testDiag("Printing server report"); dbsr(0); testOk(oneState == REPORT_CALLED, "dbsr"); From 6aec8d9bcb4ee7f0c68aed5abddcc034fbbe45f4 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 25 Aug 2015 18:09:35 -0400 Subject: [PATCH 12/47] libCom/osi: fetch monotonic time --- src/libCom/osi/Makefile | 1 + src/libCom/osi/epicsTime.h | 11 ++++ src/libCom/osi/os/Darwin/osdMonotonic.c | 32 +++++++++++ src/libCom/osi/os/Darwin/osdTime.cpp | 3 + src/libCom/osi/os/RTEMS/osdTime.cpp | 3 + src/libCom/osi/os/WIN32/osdMonotonic.c | 53 +++++++++++++++++ src/libCom/osi/os/WIN32/osdTime.cpp | 3 + src/libCom/osi/os/iOS/osdMonotonic.c | 31 ++++++++++ src/libCom/osi/os/posix/osdMonotonic.c | 75 +++++++++++++++++++++++++ src/libCom/osi/os/posix/osdTime.cpp | 3 + src/libCom/osi/os/vxWorks/osdTime.cpp | 2 + src/libCom/test/epicsTimeTest.cpp | 45 ++++++++++++++- 12 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 src/libCom/osi/os/Darwin/osdMonotonic.c create mode 100644 src/libCom/osi/os/WIN32/osdMonotonic.c create mode 100644 src/libCom/osi/os/iOS/osdMonotonic.c create mode 100644 src/libCom/osi/os/posix/osdMonotonic.c diff --git a/src/libCom/osi/Makefile b/src/libCom/osi/Makefile index c06a862ed..ecbf4c23b 100644 --- a/src/libCom/osi/Makefile +++ b/src/libCom/osi/Makefile @@ -119,6 +119,7 @@ Com_SRCS += osdMutex.c Com_SRCS += osdSpin.c Com_SRCS += osdEvent.c Com_SRCS += osdTime.cpp +Com_SRCS += osdMonotonic.c Com_SRCS += osdProcess.c Com_SRCS += osdNetIntf.c Com_SRCS += osdMessageQueue.c diff --git a/src/libCom/osi/epicsTime.h b/src/libCom/osi/epicsTime.h index 149f6f2d3..862bc22d2 100644 --- a/src/libCom/osi/epicsTime.h +++ b/src/libCom/osi/epicsTime.h @@ -258,6 +258,17 @@ epicsShareFunc void epicsShareAPI epicsTimeShow ( epicsShareFunc int epicsShareAPI epicsTime_localtime ( const time_t * clock, struct tm * result ); epicsShareFunc int epicsShareAPI epicsTime_gmtime ( const time_t * clock, struct tm * result ); +/* Advertised monotonic counter resolution (may not be accurate). + * Minimum non-zero difference between two calls to epicsMonotonicGet() + */ +epicsShareFunc epicsUInt64 epicsMonotonicResolution(void); +/* Fetch monotonic counter, return is nano-seconds since an unspecified time */ +epicsShareFunc epicsUInt64 epicsMonotonicGet(void); + +#ifdef EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +epicsShareFunc void osdMonotonicInit(void); +#endif + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/libCom/osi/os/Darwin/osdMonotonic.c b/src/libCom/osi/os/Darwin/osdMonotonic.c new file mode 100644 index 000000000..50ca82ef6 --- /dev/null +++ b/src/libCom/osi/os/Darwin/osdMonotonic.c @@ -0,0 +1,32 @@ +/*************************************************************************\ +* Copyright (c) 2015 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "errlog.h" +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "epicsTime.h" +#include "generalTimeSup.h" + +void osdMonotonicInit(void) +{ + /* no-op */ +} + +epicsUInt64 epicsMonotonicResolution(void) +{ + return 1; /* TODO, how to find ? */ +} + +epicsUInt64 epicsMonotonicGet(void) +{ + uint64_t val = mach_absolute_time(), ret; + absolutetime_to_nanoseconds(val, &ret); + return ret; +} diff --git a/src/libCom/osi/os/Darwin/osdTime.cpp b/src/libCom/osi/os/Darwin/osdTime.cpp index 72b3dc874..7bddc7da2 100644 --- a/src/libCom/osi/os/Darwin/osdTime.cpp +++ b/src/libCom/osi/os/Darwin/osdTime.cpp @@ -19,6 +19,7 @@ #define epicsExportSharedSymbols #include "cantProceed.h" +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "generalTimeSup.h" @@ -45,6 +46,8 @@ static int timeRegister(void) generalTimeCurrentTpRegister("MachTime", \ LAST_RESORT_PRIORITY, osdTimeGetCurrent); + + osdMonotonicInit(); return 1; } static int done = timeRegister(); diff --git a/src/libCom/osi/os/RTEMS/osdTime.cpp b/src/libCom/osi/os/RTEMS/osdTime.cpp index 4947c568e..a9d5c2ea6 100644 --- a/src/libCom/osi/os/RTEMS/osdTime.cpp +++ b/src/libCom/osi/os/RTEMS/osdTime.cpp @@ -20,6 +20,7 @@ #include #include #include +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "osdTime.h" #include "osiNTPTime.h" @@ -37,6 +38,8 @@ void osdTimeRegister(void) /* Init NTP first so it can be used to sync ClockTime */ NTPTime_Init(100); ClockTime_Init(CLOCKTIME_SYNC); + + osdMonotonicInit(); } int osdNTPGet(struct timespec *ts) diff --git a/src/libCom/osi/os/WIN32/osdMonotonic.c b/src/libCom/osi/os/WIN32/osdMonotonic.c new file mode 100644 index 000000000..342ee7d60 --- /dev/null +++ b/src/libCom/osi/os/WIN32/osdMonotonic.c @@ -0,0 +1,53 @@ +/*************************************************************************\ +* Copyright (c) 2015 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "errlog.h" +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "epicsTime.h" +#include "generalTimeSup.h" + +static unsigned char osdUsePrefCounter; +static epicsUInt64 osdMonotonicResolution; + +void osdMonotonicInit(void) +{ + LARGE_INTEGER freq, val; + + if(!QueryPerformanceFrequency(&freq) || + !QueryPerformanceCounter(&val)) + { + double period = 1.0/freq.QuadPart; + osdMonotonicResolution = period*1e9; + osdUsePrefCounter = 1; + } else { + osdMonotonicResolution = 1e6; /* 1 ms TODO place holder */ + } +} + +epicsUInt64 epicsMonotonicResolution(void) +{ + return osdMonotonicResolution; +} + +epicsUInt64 epicsMonotonicGet(void) +{ + LARGE_INTEGER val; + if(osdUsePrefCounter) { + if(!QueryPerformanceCounter(&val)) { + errMessage(errlogMinor, "Warning: failed to fetch performance counter\n"); + return 0; + } else + return val.QuadPart; + } else { + epicsUInt64 ret = GetTickCount(); + ret *= 1000000; + return ret; + } +} diff --git a/src/libCom/osi/os/WIN32/osdTime.cpp b/src/libCom/osi/os/WIN32/osdTime.cpp index 2ee0d3066..348fe7c2f 100644 --- a/src/libCom/osi/os/WIN32/osdTime.cpp +++ b/src/libCom/osi/os/WIN32/osdTime.cpp @@ -31,6 +31,7 @@ // EPICS // #define epicsExportSharedSymbols +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "generalTimeSup.h" #include "epicsTimer.h" @@ -106,6 +107,8 @@ static int timeRegister(void) generalTimeCurrentTpRegister("PerfCounter", 150, osdTimeGetCurrent); pCurrentTime->startPLL (); + + osdMonotonicInit(); return 1; } static int done = timeRegister(); diff --git a/src/libCom/osi/os/iOS/osdMonotonic.c b/src/libCom/osi/os/iOS/osdMonotonic.c new file mode 100644 index 000000000..0c2c7718f --- /dev/null +++ b/src/libCom/osi/os/iOS/osdMonotonic.c @@ -0,0 +1,31 @@ +/*************************************************************************\ +* Copyright (c) 2015 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#import + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "errlog.h" +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "epicsTime.h" +#include "generalTimeSup.h" + +void osdMonotonicInit(void) +{ + /* no-op */ +} + +epicsUInt64 epicsMonotonicResolution(void) +{ + return 1; /* TODO, how to find ? */ +} + +epicsUInt64 epicsMonotonicGet(void) +{ + uint64_t val = mach_absolute_time(), ret; + absolutetime_to_nanoseconds(val, &ret); + return ret; +} diff --git a/src/libCom/osi/os/posix/osdMonotonic.c b/src/libCom/osi/os/posix/osdMonotonic.c new file mode 100644 index 000000000..203702dd5 --- /dev/null +++ b/src/libCom/osi/os/posix/osdMonotonic.c @@ -0,0 +1,75 @@ +/*************************************************************************\ +* Copyright (c) 2015 Michael Davidsaver +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#define epicsExportSharedSymbols +#include "dbDefs.h" +#include "errlog.h" +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "epicsTime.h" +#include "generalTimeSup.h" + +static clockid_t osdMonotonicID; +static epicsUInt64 osdMonotonicResolution; + +void osdMonotonicInit(void) +{ + unsigned i; +clockid_t ids[] = { +#ifdef CLOCK_MONOTONIC_RAW + CLOCK_MONOTONIC_RAW, /* Linux specific */ +#endif +#ifdef CLOCK_HIGHRES + CLOCK_HIGHRES, /* solaris specific */ +#endif +#ifdef CLOCK_MONOTONIC + CLOCK_MONOTONIC, /* Linux, RTEMS, and probably others */ +#endif + /* fallback and vxWorks, not actually monotonic, but always available */ + CLOCK_REALTIME + }; + + for(i=0; i #include +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "osiNTPTime.h" #include "osiClockTime.h" @@ -63,6 +64,7 @@ static int timeRegister(void) } else { ClockTime_Init(CLOCKTIME_NOSYNC); } + osdMonotonicInit(); return 1; } static int done = timeRegister(); diff --git a/src/libCom/test/epicsTimeTest.cpp b/src/libCom/test/epicsTimeTest.cpp index 55ad43cdd..8eb8de6d4 100644 --- a/src/libCom/test/epicsTimeTest.cpp +++ b/src/libCom/test/epicsTimeTest.cpp @@ -38,12 +38,53 @@ static const unsigned uSecPerSec = 1000u * mSecPerSec; static const unsigned nSecPerSec = 1000u * uSecPerSec; static const double precisionEPICS = 1.0 / nSecPerSec; +static void crossCheck(double delay) +{ + double mindelta = 2*epicsMonotonicResolution()*1e-9, + tres = epicsThreadSleepQuantum(); + epicsUInt64 A = epicsMonotonicGet(); + epicsThreadSleep(delay); + epicsUInt64 B = epicsMonotonicGet(); + + double actual = (B-A)*1e-9, percent; + + if(mindelta Date: Mon, 11 Jan 2016 21:49:49 -0500 Subject: [PATCH 13/47] move EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE before all local includes --- src/libCom/osi/os/Darwin/osdMonotonic.c | 2 +- src/libCom/osi/os/Darwin/osdTime.cpp | 2 +- src/libCom/osi/os/RTEMS/osdTime.cpp | 2 +- src/libCom/osi/os/WIN32/osdMonotonic.c | 2 +- src/libCom/osi/os/iOS/osdMonotonic.c | 2 +- src/libCom/osi/os/posix/osdMonotonic.c | 2 +- src/libCom/osi/os/posix/osdTime.cpp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libCom/osi/os/Darwin/osdMonotonic.c b/src/libCom/osi/os/Darwin/osdMonotonic.c index 50ca82ef6..9ce14fa8e 100644 --- a/src/libCom/osi/os/Darwin/osdMonotonic.c +++ b/src/libCom/osi/os/Darwin/osdMonotonic.c @@ -8,9 +8,9 @@ #include #define epicsExportSharedSymbols +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "dbDefs.h" #include "errlog.h" -#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "generalTimeSup.h" diff --git a/src/libCom/osi/os/Darwin/osdTime.cpp b/src/libCom/osi/os/Darwin/osdTime.cpp index 7bddc7da2..315caeb9b 100644 --- a/src/libCom/osi/os/Darwin/osdTime.cpp +++ b/src/libCom/osi/os/Darwin/osdTime.cpp @@ -15,11 +15,11 @@ #include #include +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "osiSock.h" #define epicsExportSharedSymbols #include "cantProceed.h" -#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "generalTimeSup.h" diff --git a/src/libCom/osi/os/RTEMS/osdTime.cpp b/src/libCom/osi/os/RTEMS/osdTime.cpp index a9d5c2ea6..1350c1cf2 100644 --- a/src/libCom/osi/os/RTEMS/osdTime.cpp +++ b/src/libCom/osi/os/RTEMS/osdTime.cpp @@ -12,6 +12,7 @@ #include #include +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include #include #include @@ -20,7 +21,6 @@ #include #include #include -#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "osdTime.h" #include "osiNTPTime.h" diff --git a/src/libCom/osi/os/WIN32/osdMonotonic.c b/src/libCom/osi/os/WIN32/osdMonotonic.c index 342ee7d60..e7da25da4 100644 --- a/src/libCom/osi/os/WIN32/osdMonotonic.c +++ b/src/libCom/osi/os/WIN32/osdMonotonic.c @@ -7,9 +7,9 @@ #include #define epicsExportSharedSymbols +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "dbDefs.h" #include "errlog.h" -#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "generalTimeSup.h" diff --git a/src/libCom/osi/os/iOS/osdMonotonic.c b/src/libCom/osi/os/iOS/osdMonotonic.c index 0c2c7718f..0eab29c91 100644 --- a/src/libCom/osi/os/iOS/osdMonotonic.c +++ b/src/libCom/osi/os/iOS/osdMonotonic.c @@ -7,9 +7,9 @@ #import #define epicsExportSharedSymbols +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "dbDefs.h" #include "errlog.h" -#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "generalTimeSup.h" diff --git a/src/libCom/osi/os/posix/osdMonotonic.c b/src/libCom/osi/os/posix/osdMonotonic.c index 203702dd5..00c9c2871 100644 --- a/src/libCom/osi/os/posix/osdMonotonic.c +++ b/src/libCom/osi/os/posix/osdMonotonic.c @@ -5,9 +5,9 @@ \*************************************************************************/ #define epicsExportSharedSymbols +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "dbDefs.h" #include "errlog.h" -#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "generalTimeSup.h" diff --git a/src/libCom/osi/os/posix/osdTime.cpp b/src/libCom/osi/os/posix/osdTime.cpp index d56fc15d8..1952babd9 100644 --- a/src/libCom/osi/os/posix/osdTime.cpp +++ b/src/libCom/osi/os/posix/osdTime.cpp @@ -13,11 +13,11 @@ #include #include +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "osiSock.h" #define epicsExportSharedSymbols #include "cantProceed.h" -#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTime.h" #include "generalTimeSup.h" From 1c00e79b6b0c45f020ede5a0b6806d3ae5b37ca4 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 14 Jun 2017 17:02:10 +0200 Subject: [PATCH 14/47] monotonic: fix osx --- src/libCom/osi/os/Darwin/osdMonotonic.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/libCom/osi/os/Darwin/osdMonotonic.c b/src/libCom/osi/os/Darwin/osdMonotonic.c index 9ce14fa8e..6f8cb85af 100644 --- a/src/libCom/osi/os/Darwin/osdMonotonic.c +++ b/src/libCom/osi/os/Darwin/osdMonotonic.c @@ -1,10 +1,10 @@ /*************************************************************************\ -* Copyright (c) 2015 Michael Davidsaver +* Copyright (c) 2017 Michael Davidsaver * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -#include +#include #include #define epicsExportSharedSymbols @@ -14,19 +14,21 @@ #include "epicsTime.h" #include "generalTimeSup.h" +/* see https://developer.apple.com/library/content/qa/qa1398/_index.html */ +static mach_timebase_info_data_t tbinfo; + void osdMonotonicInit(void) { - /* no-op */ + (void)mach_timebase_info(&tbinfo); } epicsUInt64 epicsMonotonicResolution(void) { - return 1; /* TODO, how to find ? */ + return 1e-9 * tbinfo.numer / tbinfo.denom; } epicsUInt64 epicsMonotonicGet(void) { uint64_t val = mach_absolute_time(), ret; - absolutetime_to_nanoseconds(val, &ret); - return ret; + return val * tbinfo.numer / tbinfo.denom; } From 06b2d4bb76c829f30bb0b4d4c4627a056671112d Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Mon, 21 Dec 2015 14:35:35 -0500 Subject: [PATCH 15/47] db/test: dbStressTest use monotonic Conflicts: src/ioc/db/test/dbStressLock.c --- src/ioc/db/test/dbStressLock.c | 59 ++++++++++++++++------------------ 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/ioc/db/test/dbStressLock.c b/src/ioc/db/test/dbStressLock.c index 376587975..aed0116e0 100644 --- a/src/ioc/db/test/dbStressLock.c +++ b/src/ioc/db/test/dbStressLock.c @@ -27,7 +27,10 @@ #include "epicsSpin.h" #include "epicsThread.h" #include "epicsMutex.h" +#include "epicsEvent.h" +#include "epicsMath.h" #include "dbCommon.h" +#include "epicsTime.h" #include "dbLockPvt.h" #include "dbStaticLib.h" @@ -40,10 +43,6 @@ #include "xRecord.h" -#if defined(CLOCK_REALTIME) && defined(CLOCK_MONOTONIC) && !defined(_WIN32) -# define TIME_STATS -#endif - #define testIntOk1(A, OP, B) testOk((A) OP (B), "%s (%d) %s %s (%d)", #A, A, #OP, #B, B); #define testPtrOk1(A, OP, B) testOk((A) OP (B), "%s (%p) %s %s (%p)", #A, A, #OP, #B, B); @@ -64,10 +63,9 @@ static dbCommon **precords; typedef struct { int id; unsigned long N[3]; -#ifdef TIME_STATS double X[3]; double X2[3]; -#endif + double min[3], max[3]; unsigned int done; epicsEventId donevent; @@ -158,25 +156,19 @@ void doreTarget(workerPriv *p) static void worker(void *raw) { -#ifdef TIME_STATS - struct timespec before; -#endif + unsigned init = 1; workerPriv *priv = raw; testDiag("worker %d is %p", priv->id, epicsThreadGetIdSelf()); -#ifdef TIME_STATS - clock_gettime(CLOCK_MONOTONIC, &before); -#endif - while(!priv->done) { + epicsUInt64 after, before; double sel = getRand(); -#ifdef TIME_STATS - struct timespec after; double duration; -#endif - int act; + + before = epicsMonotonicGet(); + if(sel<0.33) { doSingle(priv); act = 0; @@ -188,19 +180,18 @@ void worker(void *raw) act = 2; } -#ifdef TIME_STATS - clock_gettime(CLOCK_MONOTONIC, &after); - - duration = (double)((long)after.tv_nsec - (long)before.tv_nsec); - duration *= 1e-9; - duration += (double)(after.tv_sec - before.tv_sec); -#endif + after = epicsMonotonicGet(); + duration = (after-before)*1e-9; priv->N[act]++; -#ifdef TIME_STATS priv->X[act] += duration; priv->X2[act] += duration*duration; -#endif + if(durationmin[act] || init) { + priv->min[act] = duration; + init = 0; + } + if(duration>priv->max[act]) + priv->max[act] = duration; } epicsEventMustTrigger(priv->donevent); @@ -333,12 +324,18 @@ MAIN(dbStressTest) testDiag("Statistics"); for(i=0; i0); testOk1(priv[i].N[1]>0); From d8d52e433cff58e7db6e8e38c017025f6578fe7d Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 15 Sep 2017 18:36:48 -0500 Subject: [PATCH 16/47] rsrv: epicsExport.h is last import --- src/ioc/rsrv/rsrvIocRegister.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ioc/rsrv/rsrvIocRegister.c b/src/ioc/rsrv/rsrvIocRegister.c index 74336817d..00c58e828 100644 --- a/src/ioc/rsrv/rsrvIocRegister.c +++ b/src/ioc/rsrv/rsrvIocRegister.c @@ -9,10 +9,11 @@ #include "iocsh.h" -#include "epicsExport.h" #define epicsExportSharedSymbols #include "rsrv.h" +#include "epicsExport.h" + /* casr */ static const iocshArg casrArg0 = { "level",iocshArgInt}; static const iocshArg * const casrArgs[1] = {&casrArg0}; From c623715604700b4baec63c27597acae56261b0bf Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 13 Oct 2017 13:15:21 +0200 Subject: [PATCH 17/47] dbstatic: allow initial value of 65535 for MENU fields --- src/ioc/dbStatic/dbStaticRun.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/dbStatic/dbStaticRun.c b/src/ioc/dbStatic/dbStaticRun.c index efc672270..d3817ffc5 100644 --- a/src/ioc/dbStatic/dbStaticRun.c +++ b/src/ioc/dbStatic/dbStaticRun.c @@ -483,7 +483,7 @@ long dbPutStringNum(DBENTRY *pdbentry, const char *pstring) return status; index = dbGetNMenuChoices(pdbentry); - if (value > index && index > 0) + if (value > index && index > 0 && value < USHRT_MAX) return S_dbLib_badField; *field = value; From 597c93681f5cb44135704a9672075c6d58cc8ac0 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Mon, 16 Oct 2017 17:13:24 +0200 Subject: [PATCH 18/47] db: add utility functions for simulation mode --- src/ioc/db/recGbl.c | 67 ++++++++++++++++++++++++++++++++++++++++++--- src/ioc/db/recGbl.h | 9 ++++++ 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/ioc/db/recGbl.c b/src/ioc/db/recGbl.c index a614b5268..01e597a1a 100644 --- a/src/ioc/db/recGbl.c +++ b/src/ioc/db/recGbl.c @@ -29,9 +29,11 @@ #define epicsExportSharedSymbols #include "dbAccessDefs.h" +#include "dbStaticLib.h" #include "dbAddr.h" #include "dbBase.h" #include "dbCommon.h" +#include "menuSimm.h" #include "dbEvent.h" #include "db_field_log.h" #include "dbFldTypes.h" @@ -255,7 +257,12 @@ void recGblFwdLink(void *precord) void recGblGetTimeStamp(void *pvoid) { - dbCommon* prec = (dbCommon*)pvoid; + recGblGetTimeStampSimm(pvoid, menuSimmNO, 0); +} + +void recGblGetTimeStampSimm(void *pvoid, const epicsEnum16 simm, struct link *siol) +{ + dbCommon *prec = (dbCommon *)pvoid; struct link *plink = &prec->tsel; if (!dbLinkIsConstant(plink)) { @@ -263,7 +270,7 @@ void recGblGetTimeStamp(void *pvoid) if (ppv_link->pvlMask & pvlOptTSELisTime) { if (dbGetTimeStamp(plink, &prec->time)) - errlogPrintf("recGblGetTimeStamp: dbGetTimeStamp failed, %s.TSEL = %s\n", + errlogPrintf("recGblGetTimeStampSimm: dbGetTimeStamp failed, %s.TSEL = %s\n", prec->name, ppv_link->pvname); return; } @@ -271,8 +278,22 @@ void recGblGetTimeStamp(void *pvoid) } if (prec->tse != epicsTimeEventDeviceTime) { if (epicsTimeGetEvent(&prec->time, prec->tse)) - errlogPrintf("recGblGetTimeStamp: epicsTimeGetEvent failed, %s.TSE = %d\n", - prec->name, prec->tse); + errlogPrintf("recGblGetTimeStampSimm: epicsTimeGetEvent failed, %s.TSE = %d\n", + prec->name, prec->tse); + } else { + if (simm != menuSimmNO) { + if (siol && !dbLinkIsConstant(siol)) { + if (dbGetTimeStamp(siol, &prec->time)) + errlogPrintf("recGblGetTimeStampSimm: dbGetTimeStamp (sim mode) failed, %s.SIOL = %s\n", + prec->name, siol->value.pv_link.pvname); + return; + } else { + if (epicsTimeGetCurrent(&prec->time)) + errlogPrintf("recGblGetTimeStampSimm: epicsTimeGetCurrent (sim mode) failed for %s.\n", + prec->name); + return; + } + } } } @@ -369,3 +390,41 @@ static void getMaxRangeValues(short field_type, double *pupper_limit, } return; } + +void recGblSaveSimm(const epicsEnum16 sscn, + epicsEnum16 *poldsimm, const epicsEnum16 simm) { + if (sscn == USHRT_MAX) return; + *poldsimm = simm; +} + +void recGblCheckSimm(struct dbCommon *pcommon, epicsEnum16 *psscn, + const epicsEnum16 oldsimm, const epicsEnum16 simm) { + if (*psscn == USHRT_MAX) return; + if (simm != oldsimm) { + epicsUInt16 scan = pcommon->scan; + scanDelete(pcommon); + pcommon->scan = *psscn; + scanAdd(pcommon); + *psscn = scan; + } +} + +void recGblInitSimm(struct dbCommon *pcommon, epicsEnum16 *psscn, + epicsEnum16 *poldsimm, epicsEnum16 *psimm, struct link *psiml) { + if (dbLinkIsConstant(psiml)) { + recGblSaveSimm(*psscn, poldsimm, *psimm); + dbLoadLink(psiml, DBF_USHORT, psimm); + recGblCheckSimm(pcommon, psscn, *poldsimm, *psimm); + } +} + +long recGblGetSimm(struct dbCommon *pcommon, epicsEnum16 *psscn, + epicsEnum16 *poldsimm, epicsEnum16 *psimm, struct link *psiml) { + long status; + + recGblSaveSimm(*psscn, poldsimm, *psimm); + status = dbGetLink(psiml, DBR_USHORT, psimm, 0, 0); + if (status) return status; + recGblCheckSimm(pcommon, psscn, *poldsimm, *psimm); + return 0; +} diff --git a/src/ioc/db/recGbl.h b/src/ioc/db/recGbl.h index 8eb589450..c5565476f 100644 --- a/src/ioc/db/recGbl.h +++ b/src/ioc/db/recGbl.h @@ -63,9 +63,18 @@ epicsShareFunc void recGblInheritSevr(int msMode, void *precord, epicsEnum16 sta epicsEnum16 sevr); epicsShareFunc void recGblFwdLink(void *precord); epicsShareFunc void recGblGetTimeStamp(void *precord); +epicsShareFunc void recGblGetTimeStampSimm(void *prec, const epicsEnum16 simm, struct link *siol); epicsShareFunc void recGblTSELwasModified(struct link *plink); epicsShareFunc void recGblCheckDeadband(epicsFloat64 *poldval, const epicsFloat64 newval, const epicsFloat64 deadband, unsigned *monitor_mask, const unsigned add_mask); +epicsShareFunc void recGblSaveSimm(const epicsEnum16 sscn, + epicsEnum16 *poldsimm, const epicsEnum16 simm); +epicsShareFunc void recGblCheckSimm(struct dbCommon *prec, epicsEnum16 *psscn, + const epicsEnum16 oldsimm, const epicsEnum16 simm); +epicsShareFunc void recGblInitSimm(struct dbCommon *prec, epicsEnum16 *psscn, + epicsEnum16 *poldsimm, epicsEnum16 *psimm, struct link *psiml); +epicsShareFunc long recGblGetSimm(struct dbCommon *prec, epicsEnum16 *psscn, + epicsEnum16 *poldsimm, epicsEnum16 *psimm, struct link *psiml); #ifdef __cplusplus } From 45415512110b43d89981e8b201443c697a7508c8 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Mon, 16 Oct 2017 17:15:01 +0200 Subject: [PATCH 19/47] rec: improve and streamline simulation mode --- src/std/rec/aaiRecord.c | 94 +++++++++++++------ src/std/rec/aaiRecord.dbd | 71 +++++++++----- src/std/rec/aaoRecord.c | 92 ++++++++++++------ src/std/rec/aaoRecord.dbd | 71 +++++++++----- src/std/rec/aiRecord.c | 146 ++++++++++++++++------------- src/std/rec/aiRecord.dbd.pod | 31 +++++- src/std/rec/aoRecord.c | 73 +++++++++------ src/std/rec/aoRecord.dbd.pod | 33 ++++++- src/std/rec/biRecord.c | 112 ++++++++++++++-------- src/std/rec/biRecord.dbd | 35 ++++++- src/std/rec/boRecord.c | 86 +++++++++++------ src/std/rec/boRecord.dbd | 37 +++++++- src/std/rec/eventRecord.c | 83 +++++++++------- src/std/rec/eventRecord.dbd | 35 ++++++- src/std/rec/histogramRecord.c | 63 ++++++++----- src/std/rec/histogramRecord.dbd | 37 +++++++- src/std/rec/int64inRecord.c | 107 +++++++++++++-------- src/std/rec/int64inRecord.dbd.pod | 33 ++++++- src/std/rec/int64outRecord.c | 90 ++++++++++++------ src/std/rec/int64outRecord.dbd.pod | 33 ++++++- src/std/rec/longinRecord.c | 96 ++++++++++++------- src/std/rec/longinRecord.dbd | 35 ++++++- src/std/rec/longoutRecord.c | 85 +++++++++++------ src/std/rec/longoutRecord.dbd | 37 +++++++- src/std/rec/lsiRecord.c | 52 ++++++---- src/std/rec/lsiRecord.dbd | 33 ++++++- src/std/rec/lsoRecord.c | 43 ++++++--- src/std/rec/lsoRecord.dbd | 33 ++++++- src/std/rec/mbbiDirectRecord.c | 85 +++++++++++------ src/std/rec/mbbiDirectRecord.dbd | 33 ++++++- src/std/rec/mbbiRecord.c | 80 +++++++++------- src/std/rec/mbbiRecord.dbd | 33 ++++++- src/std/rec/mbboDirectRecord.c | 55 ++++++++--- src/std/rec/mbboDirectRecord.dbd | 33 ++++++- src/std/rec/mbboRecord.c | 58 ++++++++---- src/std/rec/mbboRecord.dbd | 37 +++++++- src/std/rec/stringinRecord.c | 96 ++++++++++++------- src/std/rec/stringinRecord.dbd | 35 ++++++- src/std/rec/stringoutRecord.c | 90 ++++++++++++------ src/std/rec/stringoutRecord.dbd | 35 ++++++- src/std/rec/waveformRecord.c | 88 +++++++++++------ src/std/rec/waveformRecord.dbd.pod | 33 ++++++- 42 files changed, 1835 insertions(+), 732 deletions(-) diff --git a/src/std/rec/aaiRecord.c b/src/std/rec/aaiRecord.c index 0dfed346c..230c060c4 100644 --- a/src/std/rec/aaiRecord.c +++ b/src/std/rec/aaiRecord.c @@ -31,6 +31,7 @@ #include "epicsPrint.h" #include "epicsString.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -40,6 +41,7 @@ #include "recSup.h" #include "recGbl.h" #include "cantProceed.h" +#include "special.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET @@ -52,7 +54,7 @@ #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); -#define special NULL +static long special(DBADDR *, int); #define get_value NULL static long cvt_dbaddr(DBADDR *); static long get_array_info(DBADDR *, long *, long *); @@ -141,8 +143,8 @@ static long init_record(struct dbCommon *pcommon, int pass) return 0; } - recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); - + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + /* must have read_aai function defined */ if (pdset->number < 5 || pdset->read_aai == NULL) { recGblRecordError(S_dev_missingSup, prec, "aai: init_record"); @@ -169,7 +171,7 @@ static long process(struct dbCommon *pcommon) prec->pact = TRUE; prec->udf = FALSE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); monitor(prec); /* process the forward scan link record */ @@ -179,6 +181,26 @@ static long process(struct dbCommon *pcommon) return status; } +static long special(DBADDR *paddr, int after) +{ + aaiRecord *prec = (aaiRecord *)(paddr->precord); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == aaiRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } + default: + recGblDbaddrError(S_db_badChoice, paddr, "aai: special"); + return(S_db_badChoice); + } +} + static long cvt_dbaddr(DBADDR *paddr) { aaiRecord *prec = (aaiRecord *)paddr->precord; @@ -313,35 +335,49 @@ static void monitor(aaiRecord *prec) static long readValue(aaiRecord *prec) { - long status; - struct aaidset *pdset = (struct aaidset *)prec->dset; + struct aaidset *pdset = (struct aaidset *) prec->dset; + long status = 0; - if (prec->pact == TRUE){ + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } + + switch (prec->simm) { + case menuYesNoNO: status = pdset->read_aai(prec); - return status; - } + break; - status = dbGetLink(&prec->siml, DBR_ENUM, &prec->simm, 0, 0); - if (status) - return status; - - if (prec->simm == menuYesNoNO){ - return pdset->read_aai(prec); - } - - if (prec->simm == menuYesNoYES){ - /* Device suport is responsible for buffer - which might be read-only so we may not be - allowed to call dbGetLink on it. - Maybe also device support has an advanced - simulation mode. - Thus call device now. - */ + case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); - return pdset->read_aai(prec); + if (prec->pact || (prec->sdly < 0.)) { + /* Device suport is responsible for buffer + which might be read-only so we may not be + allowed to call dbGetLink on it. + Maybe also device support has an advanced + simulation mode. + Thus call device now. + + Reading through SIOL is handled in Soft Channel Device Support + */ + status = pdset->read_aai(prec); + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; } - recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); - return -1; -} + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } + return status; +} diff --git a/src/std/rec/aaiRecord.dbd b/src/std/rec/aaiRecord.dbd index 06ab7f7b8..15db6d360 100644 --- a/src/std/rec/aaiRecord.dbd +++ b/src/std/rec/aaiRecord.dbd @@ -76,28 +76,55 @@ recordtype(aai) { interest(4) extra("void * bptr") } - field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") - promptgroup("90 - Simulate") - interest(1) - } - field(SIMM,DBF_MENU) { - prompt("Simulation Mode") - interest(1) - menu(menuYesNo) - } - field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") - promptgroup("90 - Simulate") - interest(2) - menu(menuAlarmSevr) - } - field(SIOL,DBF_INLINK) { - prompt("Sim Input Specifctn") - promptgroup("90 - Simulate") - interest(1) - } - field(MPST,DBF_MENU) { + field(SIML,DBF_INLINK) { + prompt("Simulation Mode Link") + promptgroup("90 - Simulate") + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + special(SPC_MOD) + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Simulation Mode Severity") + promptgroup("90 - Simulate") + interest(2) + menu(menuAlarmSevr) + } + field(SIOL,DBF_INLINK) { + prompt("Simulation Input Link") + promptgroup("90 - Simulate") + interest(1) + } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } + field(MPST,DBF_MENU) { prompt("Post Value Monitors") promptgroup("80 - Display") interest(1) diff --git a/src/std/rec/aaoRecord.c b/src/std/rec/aaoRecord.c index 1cc2541eb..68a76a66a 100644 --- a/src/std/rec/aaoRecord.c +++ b/src/std/rec/aaoRecord.c @@ -31,6 +31,7 @@ #include "epicsPrint.h" #include "epicsString.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -39,6 +40,7 @@ #include "errMdef.h" #include "recSup.h" #include "recGbl.h" +#include "special.h" #include "cantProceed.h" #include "menuYesNo.h" @@ -52,7 +54,7 @@ #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); -#define special NULL +static long special(DBADDR *, int); #define get_value NULL static long cvt_dbaddr(DBADDR *); static long get_array_info(DBADDR *, long *, long *); @@ -141,7 +143,7 @@ static long init_record(struct dbCommon *pcommon, int pass) return 0; } - recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); /* must have write_aao function defined */ if (pdset->number < 5 || pdset->write_aao == NULL) { @@ -169,7 +171,7 @@ static long process(struct dbCommon *pcommon) prec->pact = TRUE; prec->udf = FALSE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, NULL); monitor(prec); /* process the forward scan link record */ @@ -179,6 +181,26 @@ static long process(struct dbCommon *pcommon) return status; } +static long special(DBADDR *paddr, int after) +{ + aaoRecord *prec = (aaoRecord *)(paddr->precord); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == aaoRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } + default: + recGblDbaddrError(S_db_badChoice, paddr, "aao: special"); + return(S_db_badChoice); + } +} + static long cvt_dbaddr(DBADDR *paddr) { aaoRecord *prec = (aaoRecord *)paddr->precord; @@ -313,33 +335,49 @@ static void monitor(aaoRecord *prec) static long writeValue(aaoRecord *prec) { - long status; - struct aaodset *pdset = (struct aaodset *)prec->dset; + struct aaodset *pdset = (struct aaodset *) prec->dset; + long status = 0; - if (prec->pact == TRUE) { - /* no asyn allowed, pact true means do not process */ - return 0; + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; } - status = dbGetLink(&prec->siml, DBR_ENUM, &prec->simm, 0, 0); - if (status) - return status; + switch (prec->simm) { + case menuYesNoNO: + status = pdset->write_aao(prec); + break; - if (prec->simm == menuYesNoNO) { - return pdset->write_aao(prec); - } - if (prec->simm == menuYesNoYES) { - /* Device suport is responsible for buffer - which might be write-only so we may not be - allowed to call dbPutLink on it. - Maybe also device support has an advanced - simulation mode. - Thus call device now. - */ + case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); - return pdset->write_aao(prec); - } - recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); - return -1; -} + if (prec->pact || (prec->sdly < 0.)) { + /* Device suport is responsible for buffer + which might be write-only so we may not be + allowed to call dbPutLink on it. + Maybe also device support has an advanced + simulation mode. + Thus call device now. + Writing through SIOL is handled in Soft Channel Device Support + */ + status = pdset->write_aao(prec); + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } + + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } + + return status; +} diff --git a/src/std/rec/aaoRecord.dbd b/src/std/rec/aaoRecord.dbd index 57d842f4f..a1731a3cb 100644 --- a/src/std/rec/aaoRecord.dbd +++ b/src/std/rec/aaoRecord.dbd @@ -76,28 +76,55 @@ recordtype(aao) { interest(4) extra("void * bptr") } - field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") - promptgroup("90 - Simulate") - interest(1) - } - field(SIMM,DBF_MENU) { - prompt("Simulation Mode") - interest(1) - menu(menuYesNo) - } - field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") - promptgroup("90 - Simulate") - interest(2) - menu(menuAlarmSevr) - } - field(SIOL,DBF_OUTLINK) { - prompt("Sim Output Specifctn") - promptgroup("90 - Simulate") - interest(1) - } - field(MPST,DBF_MENU) { + field(SIML,DBF_INLINK) { + prompt("Simulation Mode Link") + promptgroup("90 - Simulate") + interest(1) + } + field(SIMM,DBF_MENU) { + prompt("Simulation Mode") + special(SPC_MOD) + interest(1) + menu(menuYesNo) + } + field(SIMS,DBF_MENU) { + prompt("Simulation Mode Severity") + promptgroup("90 - Simulate") + interest(2) + menu(menuAlarmSevr) + } + field(SIOL,DBF_OUTLINK) { + prompt("Simulation Output Link") + promptgroup("90 - Simulate") + interest(1) + } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } + field(MPST,DBF_MENU) { prompt("Post Value Monitors") promptgroup("80 - Display") interest(1) diff --git a/src/std/rec/aiRecord.c b/src/std/rec/aiRecord.c index 81f730f6d..500193360 100644 --- a/src/std/rec/aiRecord.c +++ b/src/std/rec/aiRecord.c @@ -24,6 +24,7 @@ #include "errlog.h" #include "epicsMath.h" #include "alarm.h" +#include "callback.h" #include "cvtTable.h" #include "dbAccess.h" #include "dbScan.h" @@ -96,22 +97,21 @@ typedef struct aidset { /* analog input dset */ DEVSUPFUN special_linconv; }aidset; - static void checkAlarms(aiRecord *prec, epicsTimeStamp *lastTime); static void convert(aiRecord *prec); static void monitor(aiRecord *prec); static long readValue(aiRecord *prec); - + static long init_record(struct dbCommon *pcommon, int pass) { struct aiRecord *prec = (struct aiRecord *)pcommon; aidset *pdset; double eoff = prec->eoff, eslo = prec->eslo; - if (pass==0) return(0); + if (pass == 0) return 0; - recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); - recGblInitConstantLink(&prec->siol,DBF_DOUBLE,&prec->sval); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + recGblInitConstantLink(&prec->siol, DBF_DOUBLE, &prec->sval); if(!(pdset = (aidset *)(prec->dset))) { recGblRecordError(S_dev_noDSET,(void *)prec,"ai: init_record"); @@ -147,8 +147,8 @@ static long process(struct dbCommon *pcommon) { struct aiRecord *prec = (struct aiRecord *)pcommon; aidset *pdset = (aidset *)(prec->dset); - long status; - unsigned char pact=prec->pact; + long status; + unsigned char pact=prec->pact; epicsTimeStamp timeLast; if( (pdset==NULL) || (pdset->read_ai==NULL) ) { @@ -163,10 +163,13 @@ static long process(struct dbCommon *pcommon) if ( !pact && prec->pact ) return(0); prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); + if (status==0) convert(prec); else if (status==2) status=0; + if (status == 0) prec->udf = isnan(prec->val); + /* check for alarms */ checkAlarms(prec,&timeLast); /* check event list */ @@ -187,27 +190,35 @@ static long special(DBADDR *paddr,int after) switch(special_type) { case(SPC_LINCONV): - if(pdset->number<6) { - recGblDbaddrError(S_db_noMod,paddr,"ai: special"); - return(S_db_noMod); - } - prec->init=TRUE; - if ((prec->linr == menuConvertLINEAR) && pdset->special_linconv) { - double eoff = prec->eoff; - double eslo = prec->eslo; - long status; - prec->eoff = prec->egul; - status = (*pdset->special_linconv)(prec,after); - if (eoff != prec->eoff) - db_post_events(prec, &prec->eoff, DBE_VALUE|DBE_LOG); - if (eslo != prec->eslo) - db_post_events(prec, &prec->eslo, DBE_VALUE|DBE_LOG); - return(status); - } - return(0); + if(pdset->number<6) { + recGblDbaddrError(S_db_noMod,paddr,"ai: special"); + return(S_db_noMod); + } + prec->init=TRUE; + if ((prec->linr == menuConvertLINEAR) && pdset->special_linconv) { + double eoff = prec->eoff; + double eslo = prec->eslo; + long status; + prec->eoff = prec->egul; + status = (*pdset->special_linconv)(prec,after); + if (eoff != prec->eoff) + db_post_events(prec, &prec->eoff, DBE_VALUE|DBE_LOG); + if (eslo != prec->eslo) + db_post_events(prec, &prec->eslo, DBE_VALUE|DBE_LOG); + return(status); + } + return(0); + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == aiRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } default: - recGblDbaddrError(S_db_badChoice,paddr,"ai: special"); - return(S_db_badChoice); + recGblDbaddrError(S_db_badChoice,paddr,"ai: special"); + return(S_db_badChoice); } } @@ -442,7 +453,6 @@ static void convert(aiRecord *prec) }else{ prec->val = val; } - prec->udf = isnan(prec->val); return; } @@ -469,46 +479,50 @@ static void monitor(aiRecord *prec) static long readValue(aiRecord *prec) { - long status; - aidset *pdset = (aidset *) (prec->dset); + aidset *pdset = (aidset *)prec->dset; + long status = 0; - if (prec->pact == TRUE){ - status=(*pdset->read_ai)(prec); - return(status); - } + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } - status = dbGetLink(&(prec->siml),DBR_USHORT,&(prec->simm),0,0); + switch (prec->simm) { + case menuSimmNO: + status = pdset->read_ai(prec); + break; - if (status) - return(status); + case menuSimmYES: + case menuSimmRAW: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbGetLink(&prec->siol, DBR_DOUBLE, &prec->sval, 0, 0); + if (status == 0) { + if (prec->simm == menuSimmYES) { + prec->val = prec->sval; + status = 2; /* don't convert */ + } else { + prec->rval = (long)floor(prec->sval); + status = 0; /* convert RVAL */ + } + } + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } - if (prec->simm == menuSimmNO){ - status=(*pdset->read_ai)(prec); - return(status); - } - if (prec->simm == menuSimmYES){ - status = dbGetLink(&(prec->siol),DBR_DOUBLE,&(prec->sval),0,0); - if (status==0){ - prec->val=prec->sval; - prec->udf=isnan(prec->val); - } - status=2; /* dont convert */ - } - else if (prec->simm == menuSimmRAW){ - status = dbGetLink(&(prec->siol),DBR_DOUBLE,&(prec->sval),0,0); - if (status==0) { - prec->udf=isnan(prec->sval); - if (!prec->udf) { - prec->rval=(long)floor(prec->sval); - } - } - status=0; /* convert since we've written RVAL */ - } else { - status=-1; - recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); - return(status); - } - recGblSetSevr(prec,SIMM_ALARM,prec->sims); + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } - return(status); + return status; } diff --git a/src/std/rec/aiRecord.dbd.pod b/src/std/rec/aiRecord.dbd.pod index 2973ec831..69ca65f3f 100644 --- a/src/std/rec/aiRecord.dbd.pod +++ b/src/std/rec/aiRecord.dbd.pod @@ -468,7 +468,7 @@ simulation mode. =cut field(SIOL,DBF_INLINK) { - prompt("Sim. Input Specification") + prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } @@ -476,12 +476,13 @@ simulation mode. prompt("Simulation Value") } field(SIML,DBF_INLINK) { - prompt("Sim. Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") + special(SPC_MOD) interest(1) menu(menuSimm) } @@ -491,6 +492,32 @@ simulation mode. interest(2) menu(menuAlarmSevr) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } } =head2 Device Support Interface diff --git a/src/std/rec/aoRecord.c b/src/std/rec/aoRecord.c index 0d923af5c..c5a84f615 100644 --- a/src/std/rec/aoRecord.c +++ b/src/std/rec/aoRecord.c @@ -24,6 +24,7 @@ #include "epicsPrint.h" #include "epicsMath.h" #include "alarm.h" +#include "callback.h" #include "cvtTable.h" #include "dbAccess.h" #include "dbEvent.h" @@ -94,7 +95,6 @@ struct aodset { /* analog input dset */ epicsExportAddress(rset,aoRSET); - static void checkAlarms(aoRecord *); static long fetch_value(aoRecord *, double *); static void convert(aoRecord *, double); @@ -107,10 +107,11 @@ static long init_record(struct dbCommon *pcommon, int pass) struct aodset *pdset; double eoff = prec->eoff, eslo = prec->eslo; double value; + long status = 0; - if (pass==0) return(0); + if (pass == 0) return 0; - recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if(!(pdset = (struct aodset *)(prec->dset))) { recGblRecordError(S_dev_noDSET,(void *)prec,"ao: init_record"); @@ -132,7 +133,7 @@ static long init_record(struct dbCommon *pcommon, int pass) } if (pdset->init_record) { - long status=(*pdset->init_record)(prec); + status = (*pdset->init_record)(prec); if (prec->linr == menuConvertSLOPE) { prec->eoff = eoff; prec->eslo = eslo; @@ -228,7 +229,7 @@ static long process(struct dbCommon *pcommon) if ( !pact && prec->pact ) return(0); prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, NULL); /* check event list */ monitor(prec); @@ -267,6 +268,14 @@ static long special(DBADDR *paddr, int after) return (status); } return (0); + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == aoRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } default: recGblDbaddrError(S_db_badChoice,paddr,"ao: special"); return(S_db_badChoice); @@ -546,30 +555,40 @@ static void monitor(aoRecord *prec) static long writeValue(aoRecord *prec) { - long status; - struct aodset *pdset = (struct aodset *) (prec->dset); + struct aodset *pdset = (struct aodset *) prec->dset; + long status = 0; - if (prec->pact == TRUE){ - status=(*pdset->write_ao)(prec); - return(status); - } + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } - status = dbGetLink(&prec->siml,DBR_USHORT,&(prec->simm),0,0); - if (status) - return(status); + switch (prec->simm) { + case menuYesNoNO: + status = pdset->write_ao(prec); + break; - if (prec->simm == menuYesNoNO){ - status=(*pdset->write_ao)(prec); - return(status); - } - if (prec->simm == menuYesNoYES){ - status = dbPutLink(&(prec->siol),DBR_DOUBLE,&(prec->oval),1); - } else { - status=-1; - recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); - return(status); - } - recGblSetSevr(prec,SIMM_ALARM,prec->sims); + case menuYesNoYES: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbPutLink(&prec->siol, DBR_DOUBLE, &prec->oval, 1); + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } - return(status); + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } + + return status; } diff --git a/src/std/rec/aoRecord.dbd.pod b/src/std/rec/aoRecord.dbd.pod index 41467dcd3..f2361df7e 100644 --- a/src/std/rec/aoRecord.dbd.pod +++ b/src/std/rec/aoRecord.dbd.pod @@ -528,26 +528,53 @@ information on these fields. interest(3) } field(SIOL,DBF_OUTLINK) { - prompt("Sim Output Specifctn") + prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") + special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } field(IVOA,DBF_MENU) { prompt("INVALID output action") promptgroup("50 - Output") diff --git a/src/std/rec/biRecord.c b/src/std/rec/biRecord.c index 7afea3d9d..be700a56a 100644 --- a/src/std/rec/biRecord.c +++ b/src/std/rec/biRecord.c @@ -23,6 +23,7 @@ #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbFldTypes.h" #include "dbEvent.h" @@ -43,7 +44,7 @@ #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); -#define special NULL +static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL @@ -95,10 +96,11 @@ static long init_record(struct dbCommon *pcommon, int pass) struct bidset *pdset; long status; - if (pass==0) return(0); + if (pass == 0) return 0; + + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + recGblInitConstantLink(&prec->siol, DBF_USHORT, &prec->sval); - recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); - recGblInitConstantLink(&prec->siol,DBF_USHORT,&prec->sval); if(!(pdset = (struct bidset *)(prec->dset))) { recGblRecordError(S_dev_noDSET,(void *)prec,"bi: init_record"); return(S_dev_noDSET); @@ -135,8 +137,9 @@ static long process(struct dbCommon *pcommon) if ( !pact && prec->pact ) return(0); prec->pact = TRUE; - recGblGetTimeStamp(prec); - if(status==0) { /* convert rval to val */ + recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); + + if(status==0) { /* convert rval to val */ if(prec->rval==0) prec->val =0; else prec->val = 1; prec->udf = FALSE; @@ -153,6 +156,26 @@ static long process(struct dbCommon *pcommon) return(status); } +static long special(DBADDR *paddr, int after) +{ + biRecord *prec = (biRecord *)(paddr->precord); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == biRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } + default: + recGblDbaddrError(S_db_badChoice, paddr, "bi: special"); + return(S_db_badChoice); + } +} + static long get_enum_str(const DBADDR *paddr, char *pstring) { biRecord *prec=(biRecord *)paddr->precord; @@ -252,43 +275,50 @@ static void monitor(biRecord *prec) static long readValue(biRecord *prec) { - long status; - struct bidset *pdset = (struct bidset *) (prec->dset); + struct bidset *pdset = (struct bidset *)prec->dset; + long status = 0; - if (prec->pact == TRUE){ - status=(*pdset->read_bi)(prec); - return(status); - } + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } - status = dbGetLink(&(prec->siml),DBR_USHORT, &(prec->simm),0,0); - if (status) - return(status); + switch (prec->simm) { + case menuSimmNO: + status = pdset->read_bi(prec); + break; - if (prec->simm == menuSimmNO){ - status=(*pdset->read_bi)(prec); - return(status); - } - if (prec->simm == menuSimmYES){ - status=dbGetLink(&(prec->siol),DBR_ULONG,&(prec->sval),0,0); - if (status==0){ - prec->val=(unsigned short)prec->sval; - prec->udf=FALSE; - } - status=2; /* dont convert */ - } - else if (prec->simm == menuSimmRAW){ - status=dbGetLink(&(prec->siol),DBR_ULONG,&(prec->sval),0,0); - if (status==0){ - prec->rval=prec->sval; - prec->udf=FALSE; - } - status=0; /* convert since we've written RVAL */ - } else { - status=-1; - recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); - return(status); - } - recGblSetSevr(prec,SIMM_ALARM,prec->sims); + case menuSimmYES: + case menuSimmRAW: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbGetLink(&prec->siol, DBR_ULONG, &prec->sval, 0, 0); + if (status == 0) { + if (prec->simm == menuSimmYES) { + prec->val = prec->sval; + status = 2; /* Don't convert */ + } else { + prec->rval = (unsigned short) prec->sval; + } + prec->udf = FALSE; + } + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } - return(status); + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } + + return status; } diff --git a/src/std/rec/biRecord.dbd b/src/std/rec/biRecord.dbd index fb3588fb1..1646d5df1 100644 --- a/src/std/rec/biRecord.dbd +++ b/src/std/rec/biRecord.dbd @@ -81,7 +81,7 @@ recordtype(bi) { interest(3) } field(SIOL,DBF_INLINK) { - prompt("Sim Input Specifctn") + prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } @@ -89,19 +89,46 @@ recordtype(bi) { prompt("Simulation Value") } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") - interest(1) + special(SPC_MOD) + interest(1) menu(menuSimm) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } } diff --git a/src/std/rec/boRecord.c b/src/std/rec/boRecord.c index 8f60a0be5..5fb881f55 100644 --- a/src/std/rec/boRecord.c +++ b/src/std/rec/boRecord.c @@ -45,7 +45,7 @@ #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); -#define special NULL +static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL @@ -136,10 +136,9 @@ static long init_record(struct dbCommon *pcommon,int pass) long status = 0; myCallback *pcallback; - if (pass == 0) - return 0; + if (pass == 0) return 0; - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "bo: init_record"); @@ -255,7 +254,8 @@ static long process(struct dbCommon *pcommon) if ( !pact && prec->pact ) return(0); prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, NULL); + if((prec->val==1) && (prec->high>0)){ myCallback *pcallback; pcallback = (myCallback *)(prec->rpvt); @@ -270,7 +270,27 @@ static long process(struct dbCommon *pcommon) prec->pact=FALSE; return(status); } - + +static long special(DBADDR *paddr, int after) +{ + boRecord *prec = (boRecord *)(paddr->precord); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == boRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } + default: + recGblDbaddrError(S_db_badChoice, paddr, "bo: special"); + return(S_db_badChoice); + } +} + #define indexof(field) boRecord##field static long get_units(DBADDR *paddr, char *units) @@ -400,30 +420,40 @@ static void monitor(boRecord *prec) static long writeValue(boRecord *prec) { - long status; - struct bodset *pdset = (struct bodset *) (prec->dset); + struct bodset *pdset = (struct bodset *) prec->dset; + long status = 0; - if (prec->pact == TRUE){ - status=(*pdset->write_bo)(prec); - return(status); - } + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } - status=dbGetLink(&prec->siml,DBR_USHORT, &prec->simm,0,0); - if (status) - return(status); + switch (prec->simm) { + case menuYesNoNO: + status = pdset->write_bo(prec); + break; - if (prec->simm == menuYesNoNO){ - status=(*pdset->write_bo)(prec); - return(status); - } - if (prec->simm == menuYesNoYES){ - status=dbPutLink(&(prec->siol),DBR_USHORT, &(prec->val),1); - } else { - status=-1; - recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); - return(status); - } - recGblSetSevr(prec,SIMM_ALARM,prec->sims); + case menuYesNoYES: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1); + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } - return(status); + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } + + return status; } diff --git a/src/std/rec/boRecord.dbd b/src/std/rec/boRecord.dbd index fd002c368..346f0f22c 100644 --- a/src/std/rec/boRecord.dbd +++ b/src/std/rec/boRecord.dbd @@ -118,27 +118,54 @@ recordtype(bo) { interest(3) } field(SIOL,DBF_OUTLINK) { - prompt("Sim Output Specifctn") + prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") - interest(1) + special(SPC_MOD) + interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } - field(IVOA,DBF_MENU) { + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } + field(IVOA,DBF_MENU) { prompt("INVALID outpt action") promptgroup("50 - Output") interest(2) diff --git a/src/std/rec/eventRecord.c b/src/std/rec/eventRecord.c index 38a4ad62f..dbaef0114 100644 --- a/src/std/rec/eventRecord.c +++ b/src/std/rec/eventRecord.c @@ -22,6 +22,7 @@ #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbScan.h" @@ -97,9 +98,9 @@ static long init_record(struct dbCommon *pcommon, int pass) struct eventdset *pdset; long status=0; - if (pass==0) return(0); + if (pass == 0) return 0; - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_STRING, &prec->sval); if( (pdset=(struct eventdset *)(prec->dset)) && (pdset->init_record) ) @@ -125,7 +126,7 @@ static long process(struct dbCommon *pcommon) postEvent(prec->epvt); - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); /* check event list */ monitor(prec); @@ -142,10 +143,20 @@ static long special(DBADDR *paddr, int after) { eventRecord *prec = (eventRecord *)paddr->precord; + if (dbGetFieldIndex(paddr) == eventRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return 0; + } + if (!after) return 0; + if (dbGetFieldIndex(paddr) == eventRecordVAL) { prec->epvt = eventNameToHandle(prec->val); } + return 0; } @@ -162,39 +173,47 @@ static void monitor(eventRecord *prec) static long readValue(eventRecord *prec) { - long status; - struct eventdset *pdset = (struct eventdset *) (prec->dset); + struct eventdset *pdset = (struct eventdset *) prec->dset; + long status = 0; - if (prec->pact == TRUE){ - status=(*pdset->read_event)(prec); - return(status); - } + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } - status=dbGetLink(&(prec->siml),DBR_USHORT,&(prec->simm),0,0); + switch (prec->simm) { + case menuYesNoNO: + status = pdset->read_event(prec); + break; - if (status) - return(status); - - if (prec->simm == menuYesNoNO){ - status=(*pdset->read_event)(prec); - return(status); - } - if (prec->simm == menuYesNoYES){ - status=dbGetLink(&(prec->siol),DBR_STRING, - &(prec->sval),0,0); - if (status==0) { - if (strcmp(prec->sval, prec->val) != 0) { - strcpy(prec->val, prec->sval); - prec->epvt = eventNameToHandle(prec->val); - } - prec->udf=FALSE; + case menuYesNoYES: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbGetLink(&prec->siol, DBR_STRING, &prec->sval, 0, 0); + if (status == 0) { + if (strcmp(prec->sval, prec->val) != 0) { + strcpy(prec->val, prec->sval); + prec->epvt = eventNameToHandle(prec->val); } - } else { - status=-1; - recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); - return(status); + prec->udf = FALSE; + } + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; } - recGblSetSevr(prec,SIMM_ALARM,prec->sims); + break; + } - return(status); + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } + + return status; } diff --git a/src/std/rec/eventRecord.dbd b/src/std/rec/eventRecord.dbd index 348902e4b..57306f5f5 100644 --- a/src/std/rec/eventRecord.dbd +++ b/src/std/rec/eventRecord.dbd @@ -28,7 +28,7 @@ recordtype(event) { interest(1) } field(SIOL,DBF_INLINK) { - prompt("Sim Input Specifctn") + prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } @@ -37,19 +37,46 @@ recordtype(event) { size(40) } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") - interest(1) + special(SPC_MOD) + interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } } diff --git a/src/std/rec/histogramRecord.c b/src/std/rec/histogramRecord.c index a9563500f..cac81ffd0 100644 --- a/src/std/rec/histogramRecord.c +++ b/src/std/rec/histogramRecord.c @@ -182,7 +182,7 @@ static long init_record(struct dbCommon *pcommon, int pass) wdogInit(prec); - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_DOUBLE, &prec->sval); /* must have device support defined */ @@ -228,7 +228,7 @@ static long process(struct dbCommon *pcommon) return 0; prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); if (status == 0) add_count(prec); @@ -246,6 +246,14 @@ static long special(DBADDR *paddr, int after) { histogramRecord *prec = (histogramRecord *) paddr->precord; + if (paddr->special == SPC_MOD && dbGetFieldIndex(paddr) == histogramRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return 0; + } + if (!after) return 0; @@ -373,33 +381,44 @@ static long clear_histogram(histogramRecord *prec) static long readValue(histogramRecord *prec) { struct histogramdset *pdset = (struct histogramdset *) prec->dset; - long status; + long status = 0; - if (prec->pact) { + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } + + switch (prec->simm) { + case menuYesNoNO: status = pdset->read_histogram(prec); - return status; + break; + + case menuYesNoYES: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbGetLink(&prec->siol, DBR_DOUBLE, &prec->sval, 0, 0); + if (status == 0) { + prec->sgnl = prec->sval; + prec->udf = FALSE; + } + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; } - status = dbGetLink(&prec->siml, DBR_USHORT, &prec->simm, 0, 0); - if (status) - return(status); - - if (prec->simm == menuYesNoNO) { - status = pdset->read_histogram(prec); - return status; - } - if (prec->simm == menuYesNoYES) { - status = dbGetLink(&prec->siol,DBR_DOUBLE, &prec->sval, 0, 0); - if (status == 0) - prec->sgnl = prec->sval; - } - else { - status = -1; + default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); - return status; + status = -1; } - recGblSetSevr(prec, SIMM_ALARM, prec->sims); return status; } diff --git a/src/std/rec/histogramRecord.dbd b/src/std/rec/histogramRecord.dbd index 075400fc6..545ae3fcc 100644 --- a/src/std/rec/histogramRecord.dbd +++ b/src/std/rec/histogramRecord.dbd @@ -106,7 +106,7 @@ recordtype(histogram) { interest(1) } field(SIOL,DBF_INLINK) { - prompt("Sim Input Specifctn") + prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } @@ -114,22 +114,49 @@ recordtype(histogram) { prompt("Simulation Value") } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") - interest(1) + special(SPC_MOD) + interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } - field(HOPR,DBF_ULONG) { + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } + field(HOPR,DBF_ULONG) { prompt("High Operating Range") promptgroup("80 - Display") interest(1) diff --git a/src/std/rec/int64inRecord.c b/src/std/rec/int64inRecord.c index 85063028d..865d05fbb 100644 --- a/src/std/rec/int64inRecord.c +++ b/src/std/rec/int64inRecord.c @@ -23,6 +23,7 @@ #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -30,6 +31,7 @@ #include "errMdef.h" #include "recSup.h" #include "recGbl.h" +#include "special.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET @@ -44,7 +46,7 @@ #define initialize NULL static long init_record(dbCommon *, int); static long process(dbCommon *); -#define special NULL +static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL @@ -100,17 +102,11 @@ static long init_record(dbCommon *pcommon, int pass) struct int64indset *pdset; long status; - if (pass==0) return(0); + if (pass == 0) return 0; - /* int64in.siml must be a CONSTANT or a PV_LINK or a DB_LINK */ - if (prec->siml.type == CONSTANT) { - recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); - } - - /* int64in.siol must be a CONSTANT or a PV_LINK or a DB_LINK */ - if (prec->siol.type == CONSTANT) { - recGblInitConstantLink(&prec->siol,DBF_LONG,&prec->sval); - } + /* int64in.siml and .siol must be a CONSTANT or a PV_LINK or a DB_LINK */ + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + recGblInitConstantLink(&prec->siol, DBF_INT64, &prec->sval); if(!(pdset = (struct int64indset *)(prec->dset))) { recGblRecordError(S_dev_noDSET,(void *)prec,"int64in: init_record"); @@ -150,8 +146,9 @@ static long process(dbCommon *pcommon) if ( !pact && prec->pact ) return(0); prec->pact = TRUE; - recGblGetTimeStamp(prec); - if (status==0) prec->udf = FALSE; + recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); + + if (status==0) prec->udf = FALSE; /* check for alarms */ checkAlarms(prec, &timeLast); @@ -163,7 +160,27 @@ static long process(dbCommon *pcommon) prec->pact=FALSE; return(status); } - + +static long special(DBADDR *paddr, int after) +{ + int64inRecord *prec = (int64inRecord *)(paddr->precord); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == int64inRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } + default: + recGblDbaddrError(S_db_badChoice, paddr, "int64in: special"); + return(S_db_badChoice); + } +} + #define indexof(field) int64inRecord##field static long get_units(DBADDR *paddr,char *units) @@ -381,36 +398,44 @@ static void monitor(int64inRecord *prec) static long readValue(int64inRecord *prec) { - long status; - struct int64indset *pdset = (struct int64indset *) (prec->dset); + struct int64indset *pdset = (struct int64indset *) prec->dset; + long status = 0; - if (prec->pact == TRUE){ - status=(*pdset->read_int64in)(prec); - return(status); - } + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } - status=dbGetLink(&(prec->siml),DBR_USHORT, &(prec->simm),0,0); - if (status) - return(status); + switch (prec->simm) { + case menuYesNoNO: + status = pdset->read_int64in(prec); + break; - if (prec->simm == menuYesNoNO){ - status=(*pdset->read_int64in)(prec); - return(status); - } - if (prec->simm == menuYesNoYES){ - status=dbGetLink(&(prec->siol),DBR_LONG, - &(prec->sval),0,0); + case menuYesNoYES: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbGetLink(&prec->siol, DBR_INT64, &prec->sval, 0, 0); + if (status == 0) { + prec->val = prec->sval; + prec->udf = FALSE; + } + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } - if (status==0) { - prec->val=prec->sval; - prec->udf=FALSE; - } - } else { - status=-1; - recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); - return(status); - } - recGblSetSevr(prec,SIMM_ALARM,prec->sims); + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } - return(status); + return status; } diff --git a/src/std/rec/int64inRecord.dbd.pod b/src/std/rec/int64inRecord.dbd.pod index b85c68ae0..c85992f05 100644 --- a/src/std/rec/int64inRecord.dbd.pod +++ b/src/std/rec/int64inRecord.dbd.pod @@ -265,7 +265,7 @@ simulation mode. =cut field(SIOL,DBF_INLINK) { - prompt("Sim Input Specifctn") + prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } @@ -273,21 +273,48 @@ simulation mode. prompt("Simulation Value") } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") + special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } } =head2 Record Support diff --git a/src/std/rec/int64outRecord.c b/src/std/rec/int64outRecord.c index ecbe52ce8..da5a76d8f 100644 --- a/src/std/rec/int64outRecord.c +++ b/src/std/rec/int64outRecord.c @@ -20,6 +20,7 @@ #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -27,6 +28,7 @@ #include "errMdef.h" #include "recSup.h" #include "recGbl.h" +#include "special.h" #include "menuYesNo.h" #include "menuIvoa.h" #include "menuOmsl.h" @@ -41,7 +43,7 @@ #define initialize NULL static long init_record(dbCommon *, int); static long process(dbCommon *); -#define special NULL +static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL @@ -98,10 +100,10 @@ static long init_record(dbCommon *pcommon, int pass) struct int64outdset *pdset; long status=0; - if (pass==0) return(0); - if (prec->siml.type == CONSTANT) { - recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); - } + if (pass == 0) return 0; + + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if(!(pdset = (struct int64outdset *)(prec->dset))) { recGblRecordError(S_dev_noDSET,(void *)prec,"int64out: init_record"); return(S_dev_noDSET); @@ -180,7 +182,7 @@ static long process(dbCommon *pcommon) if ( !pact && prec->pact ) return(0); prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, NULL); /* check event list */ monitor(prec); @@ -192,6 +194,26 @@ static long process(dbCommon *pcommon) return(status); } +static long special(DBADDR *paddr, int after) +{ + int64outRecord *prec = (int64outRecord *)(paddr->precord); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == int64outRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } + default: + recGblDbaddrError(S_db_badChoice, paddr, "int64out: special"); + return(S_db_badChoice); + } +} + #define indexof(field) int64outRecord##field static long get_units(DBADDR *paddr,char *units) @@ -352,35 +374,45 @@ static void monitor(int64outRecord *prec) if (monitor_mask) db_post_events(prec, &prec->val, monitor_mask); } - + static long writeValue(int64outRecord *prec) { - long status; - struct int64outdset *pdset = (struct int64outdset *) (prec->dset); + struct int64outdset *pdset = (struct int64outdset *) prec->dset; + long status = 0; - if (prec->pact == TRUE){ - status=(*pdset->write_int64out)(prec); - return(status); - } + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } - status=dbGetLink(&(prec->siml),DBR_USHORT,&(prec->simm),0,0); - if (!RTN_SUCCESS(status)) - return(status); + switch (prec->simm) { + case menuYesNoNO: + status = pdset->write_int64out(prec); + break; - if (prec->simm == menuYesNoNO){ - status=(*pdset->write_int64out)(prec); - return(status); - } - if (prec->simm == menuYesNoYES){ - status=dbPutLink(&prec->siol,DBR_INT64,&prec->val,1); - } else { - status=-1; - recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); - return(status); - } - recGblSetSevr(prec,SIMM_ALARM,prec->sims); + case menuYesNoYES: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbPutLink(&prec->siol, DBR_INT64, &prec->val, 1); + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } - return(status); + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } + + return status; } static void convert(int64outRecord *prec, epicsInt64 value) diff --git a/src/std/rec/int64outRecord.dbd.pod b/src/std/rec/int64outRecord.dbd.pod index b489ada97..f76541489 100644 --- a/src/std/rec/int64outRecord.dbd.pod +++ b/src/std/rec/int64outRecord.dbd.pod @@ -305,26 +305,53 @@ simulation mode. =cut field(SIOL,DBF_OUTLINK) { - prompt("Sim Output Specifctn") + prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") + special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } =head3 Invalid Alarm Output Action diff --git a/src/std/rec/longinRecord.c b/src/std/rec/longinRecord.c index bd3c24ab9..a302a3d29 100644 --- a/src/std/rec/longinRecord.c +++ b/src/std/rec/longinRecord.c @@ -23,6 +23,7 @@ #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -30,6 +31,7 @@ #include "errMdef.h" #include "recSup.h" #include "recGbl.h" +#include "special.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET @@ -44,7 +46,7 @@ #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); -#define special NULL +static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL @@ -99,10 +101,9 @@ static long init_record(struct dbCommon *pcommon, int pass) struct longinRecord *prec = (struct longinRecord *)pcommon; struct longindset *pdset = (struct longindset *) prec->dset; - if (pass==0) - return(0); + if (pass == 0) return 0; - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_LONG, &prec->sval); if (!pdset) { @@ -149,8 +150,9 @@ static long process(struct dbCommon *pcommon) if ( !pact && prec->pact ) return(0); prec->pact = TRUE; - recGblGetTimeStamp(prec); - if (status==0) prec->udf = FALSE; + recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); + + if (status==0) prec->udf = FALSE; /* check for alarms */ checkAlarms(prec, &timeLast); @@ -163,6 +165,26 @@ static long process(struct dbCommon *pcommon) return(status); } +static long special(DBADDR *paddr, int after) +{ + longinRecord *prec = (longinRecord *)(paddr->precord); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == longinRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } + default: + recGblDbaddrError(S_db_badChoice, paddr, "longin: special"); + return(S_db_badChoice); + } +} + #define indexof(field) longinRecord##field static long get_units(DBADDR *paddr,char *units) @@ -380,36 +402,44 @@ static void monitor(longinRecord *prec) static long readValue(longinRecord *prec) { - long status; - struct longindset *pdset = (struct longindset *) (prec->dset); + struct longindset *pdset = (struct longindset *) prec->dset; + long status = 0; - if (prec->pact == TRUE){ - status=(*pdset->read_longin)(prec); - return(status); - } + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } - status=dbGetLink(&(prec->siml),DBR_USHORT, &(prec->simm),0,0); - if (status) - return(status); + switch (prec->simm) { + case menuYesNoNO: + status = pdset->read_longin(prec); + break; - if (prec->simm == menuYesNoNO){ - status=(*pdset->read_longin)(prec); - return(status); - } - if (prec->simm == menuYesNoYES){ - status=dbGetLink(&(prec->siol),DBR_LONG, - &(prec->sval),0,0); + case menuYesNoYES: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbGetLink(&prec->siol, DBR_LONG, &prec->sval, 0, 0); + if (status == 0) { + prec->val = prec->sval; + prec->udf = FALSE; + } + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } - if (status==0) { - prec->val=prec->sval; - prec->udf=FALSE; - } - } else { - status=-1; - recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); - return(status); - } - recGblSetSevr(prec,SIMM_ALARM,prec->sims); + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } - return(status); + return status; } diff --git a/src/std/rec/longinRecord.dbd b/src/std/rec/longinRecord.dbd index 60eee5000..f0e5a8de5 100644 --- a/src/std/rec/longinRecord.dbd +++ b/src/std/rec/longinRecord.dbd @@ -135,7 +135,7 @@ recordtype(longin) { interest(3) } field(SIOL,DBF_INLINK) { - prompt("Sim Input Specifctn") + prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } @@ -143,19 +143,46 @@ recordtype(longin) { prompt("Simulation Value") } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") - interest(1) + special(SPC_MOD) + interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } } diff --git a/src/std/rec/longoutRecord.c b/src/std/rec/longoutRecord.c index 6062ad741..0b1d66d50 100644 --- a/src/std/rec/longoutRecord.c +++ b/src/std/rec/longoutRecord.c @@ -20,6 +20,7 @@ #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -27,6 +28,7 @@ #include "errMdef.h" #include "recSup.h" #include "recGbl.h" +#include "special.h" #include "menuYesNo.h" #include "menuIvoa.h" #include "menuOmsl.h" @@ -41,7 +43,7 @@ #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); -#define special NULL +static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL @@ -96,10 +98,9 @@ static long init_record(struct dbCommon *pcommon, int pass) struct longoutRecord *prec = (struct longoutRecord *)pcommon; struct longoutdset *pdset = (struct longoutdset *) prec->dset; - if (pass==0) - return 0; + if (pass == 0) return 0; - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "longout: init_record"); @@ -183,7 +184,7 @@ static long process(struct dbCommon *pcommon) if ( !pact && prec->pact ) return(0); prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, NULL); /* check event list */ monitor(prec); @@ -195,6 +196,26 @@ static long process(struct dbCommon *pcommon) return(status); } +static long special(DBADDR *paddr, int after) +{ + longoutRecord *prec = (longoutRecord *)(paddr->precord); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == longoutRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } + default: + recGblDbaddrError(S_db_badChoice, paddr, "longout: special"); + return(S_db_badChoice); + } +} + #define indexof(field) longoutRecord##field static long get_units(DBADDR *paddr,char *units) @@ -358,32 +379,42 @@ static void monitor(longoutRecord *prec) static long writeValue(longoutRecord *prec) { - long status; - struct longoutdset *pdset = (struct longoutdset *) (prec->dset); + struct longoutdset *pdset = (struct longoutdset *) prec->dset; + long status = 0; - if (prec->pact == TRUE){ - status=(*pdset->write_longout)(prec); - return(status); - } + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } - status=dbGetLink(&(prec->siml),DBR_USHORT,&(prec->simm),0,0); - if (!RTN_SUCCESS(status)) - return(status); + switch (prec->simm) { + case menuYesNoNO: + status = pdset->write_longout(prec); + break; - if (prec->simm == menuYesNoNO){ - status=(*pdset->write_longout)(prec); - return(status); - } - if (prec->simm == menuYesNoYES){ - status=dbPutLink(&prec->siol,DBR_LONG,&prec->val,1); - } else { - status=-1; - recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); - return(status); - } - recGblSetSevr(prec,SIMM_ALARM,prec->sims); + case menuYesNoYES: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbPutLink(&prec->siol, DBR_LONG, &prec->val, 1); + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } - return(status); + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } + + return status; } static void convert(longoutRecord *prec, epicsInt32 value) diff --git a/src/std/rec/longoutRecord.dbd b/src/std/rec/longoutRecord.dbd index c3ba0b977..626be1645 100644 --- a/src/std/rec/longoutRecord.dbd +++ b/src/std/rec/longoutRecord.dbd @@ -150,27 +150,54 @@ recordtype(longout) { interest(3) } field(SIOL,DBF_OUTLINK) { - prompt("Sim Output Specifctn") + prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") - interest(1) + special(SPC_MOD) + interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } - field(IVOA,DBF_MENU) { + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } + field(IVOA,DBF_MENU) { prompt("INVALID output action") promptgroup("50 - Output") interest(2) diff --git a/src/std/rec/lsiRecord.c b/src/std/rec/lsiRecord.c index 81b6785ab..261313f07 100644 --- a/src/std/rec/lsiRecord.c +++ b/src/std/rec/lsiRecord.c @@ -18,6 +18,7 @@ #include "dbDefs.h" #include "errlog.h" #include "alarm.h" +#include "callback.h" #include "cantProceed.h" #include "dbAccess.h" #include "dbEvent.h" @@ -56,7 +57,7 @@ static long init_record(struct dbCommon *pcommon, int pass) return 0; } - dbLoadLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); pdset = (lsidset *) prec->dset; if (!pdset) { @@ -104,7 +105,7 @@ static long process(struct dbCommon *pcommon) return 0; prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); monitor(prec); @@ -171,6 +172,15 @@ static long put_array_info(DBADDR *paddr, long nNew) static long special(DBADDR *paddr, int after) { lsiRecord *prec = (lsiRecord *) paddr->precord; + int special_type = paddr->special; + + if (special_type == SPC_MOD && dbGetFieldIndex(paddr) == lsiRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return 0; + } if (!after) return 0; @@ -211,40 +221,46 @@ static void monitor(lsiRecord *prec) static long readValue(lsiRecord *prec) { - long status; - lsidset *pdset = (lsidset *) prec->dset; + struct lsidset *pdset = (struct lsidset *) prec->dset; + long status = 0; - if (prec->pact) - goto read; - - status = dbGetLink(&prec->siml, DBR_USHORT, &prec->simm, 0, 0); - if (status) - return status; + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } switch (prec->simm) { case menuYesNoNO: -read: status = pdset->read_string(prec); break; - case menuYesNoYES: + case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); - status = dbGetLinkLS(&prec->siol, prec->val, prec->sizv, &prec->len); + if (prec->pact || (prec->sdly < 0.)) { + status = dbGetLinkLS(&prec->siol, prec->val, prec->sizv, &prec->len); + if (status == 0) prec->udf = FALSE; + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } break; + } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); status = -1; } - if (!status) - prec->udf = FALSE; - return status; } - -/* Create Record Support Entry Table*/ +/* Create Record Support Entry Table */ #define report NULL #define initialize NULL diff --git a/src/std/rec/lsiRecord.dbd b/src/std/rec/lsiRecord.dbd index c50d905d8..689366f98 100644 --- a/src/std/rec/lsiRecord.dbd +++ b/src/std/rec/lsiRecord.dbd @@ -67,11 +67,12 @@ recordtype(lsi) { field(SIML,DBF_INLINK) { prompt("Simulation Mode Link") promptgroup("90 - Simulate") - interest(2) + interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") - interest(2) + special(SPC_MOD) + interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { @@ -81,8 +82,34 @@ recordtype(lsi) { menu(menuAlarmSevr) } field(SIOL,DBF_INLINK) { - prompt("Sim Input Specifctn") + prompt("Simulation Input Link") + promptgroup("90 - Simulate") + interest(1) + } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") promptgroup("90 - Simulate") interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") } } diff --git a/src/std/rec/lsoRecord.c b/src/std/rec/lsoRecord.c index 625f5d77c..dcbe1500c 100644 --- a/src/std/rec/lsoRecord.c +++ b/src/std/rec/lsoRecord.c @@ -19,6 +19,7 @@ #include "dbDefs.h" #include "errlog.h" #include "alarm.h" +#include "callback.h" #include "cantProceed.h" #include "dbAccess.h" #include "dbEvent.h" @@ -60,7 +61,7 @@ static long init_record(struct dbCommon *pcommon, int pass) return 0; } - dbLoadLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); pdset = (lsodset *) prec->dset; if (!pdset) { @@ -146,7 +147,7 @@ static long process(struct dbCommon *pcommon) return status; prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, NULL); monitor(prec); @@ -213,6 +214,15 @@ static long put_array_info(DBADDR *paddr, long nNew) static long special(DBADDR *paddr, int after) { lsoRecord *prec = (lsoRecord *) paddr->precord; + int special_type = paddr->special; + + if (special_type == SPC_MOD && dbGetFieldIndex(paddr) == lsoRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return 0; + } if (!after) return 0; @@ -253,26 +263,35 @@ static void monitor(lsoRecord *prec) static long writeValue(lsoRecord *prec) { - long status; lsodset *pdset = (lsodset *) prec->dset; + long status = 0; - if (prec->pact) - goto write; - - status = dbGetLink(&prec->siml, DBR_USHORT, &prec->simm, 0, 0); - if (status) - return(status); + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } switch (prec->simm) { case menuYesNoNO: -write: status = pdset->write_string(prec); break; - case menuYesNoYES: + case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); - status = dbPutLink(&prec->siol,DBR_STRING, prec->val,1); + if (prec->pact || (prec->sdly < 0.)) { + status = dbPutLinkLS(&prec->siol, prec->val, prec->len); + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } break; + } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); diff --git a/src/std/rec/lsoRecord.dbd b/src/std/rec/lsoRecord.dbd index 69203f2d0..8d3a64edc 100644 --- a/src/std/rec/lsoRecord.dbd +++ b/src/std/rec/lsoRecord.dbd @@ -89,24 +89,51 @@ recordtype(lso) { menu(menuPost) } field(SIML,DBF_INLINK) { - prompt("Sim Mode link") + prompt("Simulation Mode link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") + special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } field(SIOL,DBF_OUTLINK) { - prompt("Sim Output Specifctn") + prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } } diff --git a/src/std/rec/mbbiDirectRecord.c b/src/std/rec/mbbiDirectRecord.c index 112272925..566bbe21d 100644 --- a/src/std/rec/mbbiDirectRecord.c +++ b/src/std/rec/mbbiDirectRecord.c @@ -24,6 +24,7 @@ #include "dbDefs.h" #include "errlog.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -44,7 +45,7 @@ #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); -#define special NULL +static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL @@ -100,8 +101,7 @@ static long init_record(struct dbCommon *pcommon, int pass) struct mbbidset *pdset = (struct mbbidset *) prec->dset; long status = 0; - if (pass == 0) - return status; + if (pass == 0) return 0; if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "mbbiDirect: init_record"); @@ -113,7 +113,7 @@ static long init_record(struct dbCommon *pcommon, int pass) return S_dev_missingSup; } - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_USHORT, &prec->sval); /* Initialize MASK if the user set NOBT instead */ @@ -158,7 +158,7 @@ static long process(struct dbCommon *pcommon) return 0; prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); if (status == 0) { /* Convert RVAL to VAL */ @@ -184,6 +184,26 @@ static long process(struct dbCommon *pcommon) return status; } +static long special(DBADDR *paddr, int after) +{ + mbbiDirectRecord *prec = (mbbiDirectRecord *)(paddr->precord); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == mbbiDirectRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } + default: + recGblDbaddrError(S_db_badChoice, paddr, "mbbiDirect: special"); + return(S_db_badChoice); + } +} + static void monitor(mbbiDirectRecord *prec) { epicsUInt16 events = recGblResetAlarms(prec); @@ -219,42 +239,49 @@ static void monitor(mbbiDirectRecord *prec) static long readValue(mbbiDirectRecord *prec) { struct mbbidset *pdset = (struct mbbidset *) prec->dset; - long status; + long status = 0; - if (prec->pact) - return pdset->read_mbbi(prec); - - status = dbGetLink(&prec->siml, DBR_ENUM, &prec->simm, 0, 0); - if (status) - return status; + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } switch (prec->simm) { case menuSimmNO: - return pdset->read_mbbi(prec); + status = pdset->read_mbbi(prec); + break; case menuSimmYES: - status = dbGetLink(&prec->siol, DBR_ULONG, &prec->sval, 0, 0); - if (status == 0) { - prec->val = prec->sval; - prec->udf = FALSE; + case menuSimmRAW: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbGetLink(&prec->siol, DBR_ULONG, &prec->sval, 0, 0); + if (status == 0) { + if (prec->simm == menuSimmYES) { + prec->val = prec->sval; + status = 2; /* Don't convert */ + } else { + prec->rval = prec->sval; + } + prec->udf = FALSE; + } + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; } - status = 2; /* Don't convert */ - break; - - case menuSimmRAW: - status = dbGetLink(&prec->siol, DBR_ULONG, &prec->sval, 0, 0); - if (status == 0) { - prec->rval = prec->sval; - prec->udf = FALSE; - } - status = 0; /* Convert RVAL */ break; + } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); - return -1; + status = -1; } - recGblSetSevr(prec, SIMM_ALARM, prec->sims); return status; } diff --git a/src/std/rec/mbbiDirectRecord.dbd b/src/std/rec/mbbiDirectRecord.dbd index 3fa3d8823..c7ca0f466 100644 --- a/src/std/rec/mbbiDirectRecord.dbd +++ b/src/std/rec/mbbiDirectRecord.dbd @@ -50,7 +50,7 @@ recordtype(mbbiDirect) { interest(1) } field(SIOL,DBF_INLINK) { - prompt("Sim Input Specifctn") + prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } @@ -58,21 +58,48 @@ recordtype(mbbiDirect) { prompt("Simulation Value") } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") + special(SPC_MOD) interest(1) menu(menuSimm) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } field(B0,DBF_UCHAR) { prompt("Bit 0") pp(TRUE) diff --git a/src/std/rec/mbbiRecord.c b/src/std/rec/mbbiRecord.c index ad5a8ce06..d04161d56 100644 --- a/src/std/rec/mbbiRecord.c +++ b/src/std/rec/mbbiRecord.c @@ -22,6 +22,7 @@ #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -46,7 +47,7 @@ #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); -static long special(DBADDR *, int); +static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL @@ -117,8 +118,7 @@ static long init_record(struct dbCommon *pcommon, int pass) struct mbbidset *pdset = (struct mbbidset *) prec->dset; long status = 0; - if (pass == 0) - return 0; + if (pass == 0) return 0; pdset = (struct mbbidset *) prec->dset; if (!pdset) { @@ -131,7 +131,7 @@ static long init_record(struct dbCommon *pcommon, int pass) return S_dev_missingSup; } - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_USHORT, &prec->sval); /* Initialize MASK if the user set NOBT instead */ @@ -172,7 +172,7 @@ static long process(struct dbCommon *pcommon) return 0; prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); if (status == 0) { /* Convert RVAL to VAL */ @@ -215,20 +215,25 @@ static long special(DBADDR *paddr, int after) mbbiRecord *prec = (mbbiRecord *) paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); - if (!after) - return 0; - switch (paddr->special) { case SPC_MOD: - init_common(prec); if (fieldIndex >= mbbiRecordZRST && fieldIndex <= mbbiRecordFFST) { int event = DBE_PROPERTY; + if (!after) return 0; + init_common(prec); if (prec->val == fieldIndex - mbbiRecordZRST) event |= DBE_VALUE | DBE_LOG; db_post_events(prec, &prec->val, event); + return 0; + } + if (fieldIndex == mbbiRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return 0; } - return 0; default: recGblDbaddrError(S_db_badChoice, paddr, "mbbi: special"); return S_db_badChoice; @@ -372,42 +377,49 @@ static void monitor(mbbiRecord *prec) static long readValue(mbbiRecord *prec) { struct mbbidset *pdset = (struct mbbidset *) prec->dset; - long status; + long status = 0; - if (prec->pact) - return pdset->read_mbbi(prec); - - status = dbGetLink(&prec->siml, DBR_ENUM, &prec->simm, 0, 0); - if (status) - return status; + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } switch (prec->simm) { case menuSimmNO: - return pdset->read_mbbi(prec); + status = pdset->read_mbbi(prec); + break; case menuSimmYES: - status = dbGetLink(&prec->siol, DBR_ULONG, &prec->sval, 0, 0); - if (status == 0) { - prec->val = prec->sval; - prec->udf = FALSE; + case menuSimmRAW: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbGetLink(&prec->siol, DBR_ULONG, &prec->sval, 0, 0); + if (status == 0) { + if (prec->simm == menuSimmYES) { + prec->val = prec->sval; + status = 2; /* Don't convert */ + } else { + prec->rval = prec->sval; + } + prec->udf = FALSE; + } + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; } - status = 2; /* Don't convert */ - break; - - case menuSimmRAW: - status = dbGetLink(&prec->siol, DBR_ULONG, &prec->sval, 0, 0); - if (status == 0) { - prec->rval = prec->sval; - prec->udf = FALSE; - } - status = 0; /* Convert RVAL */ break; + } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); - return -1; + status = -1; } - recGblSetSevr(prec, SIMM_ALARM, prec->sims); return status; } diff --git a/src/std/rec/mbbiRecord.dbd b/src/std/rec/mbbiRecord.dbd index 1f5724c92..52f18e43d 100644 --- a/src/std/rec/mbbiRecord.dbd +++ b/src/std/rec/mbbiRecord.dbd @@ -452,7 +452,7 @@ recordtype(mbbi) { interest(1) } field(SIOL,DBF_INLINK) { - prompt("Sim Input Specifctn") + prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } @@ -460,19 +460,46 @@ recordtype(mbbi) { prompt("Simulation Value") } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") + special(SPC_MOD) interest(1) menu(menuSimm) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } } diff --git a/src/std/rec/mbboDirectRecord.c b/src/std/rec/mbboDirectRecord.c index 0a6e3391e..750228bc9 100644 --- a/src/std/rec/mbboDirectRecord.c +++ b/src/std/rec/mbboDirectRecord.c @@ -22,6 +22,7 @@ #include "dbDefs.h" #include "errlog.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -102,8 +103,7 @@ static long init_record(struct dbCommon *pcommon, int pass) struct mbbodset *pdset = (struct mbbodset *) prec->dset; long status = 0; - if (pass == 0) - return 0; + if (pass == 0) return 0; if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "mbboDirect: init_record"); @@ -115,7 +115,8 @@ static long init_record(struct dbCommon *pcommon, int pass) return S_dev_missingSup; } - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (recGblInitConstantLink(&prec->dol, DBF_USHORT, &prec->val)) prec->udf = FALSE; @@ -232,7 +233,8 @@ CONTINUE: return 0; prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, NULL); + monitor(prec); /* Wrap up */ @@ -244,6 +246,15 @@ CONTINUE: static long special(DBADDR *paddr, int after) { mbboDirectRecord *prec = (mbboDirectRecord *) paddr->precord; + int fieldIndex = dbGetFieldIndex(paddr); + + if (paddr->special == SPC_MOD && fieldIndex == mbboDirectRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return 0; + } if (!after) return 0; @@ -335,26 +346,40 @@ static void convert(mbboDirectRecord *prec) static long writeValue(mbboDirectRecord *prec) { - long status; struct mbbodset *pdset = (struct mbbodset *) prec->dset; + long status = 0; - if (prec->pact) - return pdset->write_mbbo(prec); - - status = dbGetLink(&prec->siml, DBR_ENUM, &prec->simm, 0, 0); - if (status) - return status; + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } switch (prec->simm) { case menuYesNoNO: - return pdset->write_mbbo(prec); + status = pdset->write_mbbo(prec); + break; - case menuYesNoYES: + case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); - return dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1); + if (prec->pact || (prec->sdly < 0.)) { + status = dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1); + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); - return -1; + status = -1; } + + return status; } diff --git a/src/std/rec/mbboDirectRecord.dbd b/src/std/rec/mbboDirectRecord.dbd index 0b4285e32..241199d9e 100644 --- a/src/std/rec/mbboDirectRecord.dbd +++ b/src/std/rec/mbboDirectRecord.dbd @@ -185,26 +185,53 @@ recordtype(mbboDirect) { interest(1) } field(SIOL,DBF_OUTLINK) { - prompt("Sim Output Specifctn") + prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") + special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } field(IVOA,DBF_MENU) { prompt("INVALID outpt action") promptgroup("50 - Output") diff --git a/src/std/rec/mbboRecord.c b/src/std/rec/mbboRecord.c index 0ae6a815a..2e2a35bb2 100644 --- a/src/std/rec/mbboRecord.c +++ b/src/std/rec/mbboRecord.c @@ -23,6 +23,7 @@ #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -135,7 +136,8 @@ static long init_record(struct dbCommon *pcommon, int pass) return S_dev_missingSup; } - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (recGblInitConstantLink(&prec->dol, DBF_USHORT, &prec->val)) prec->udf = FALSE; @@ -254,7 +256,8 @@ CONTINUE: return 0; prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, NULL); + monitor(prec); /* Wrap up */ @@ -268,20 +271,25 @@ static long special(DBADDR *paddr, int after) mbboRecord *prec = (mbboRecord *) paddr->precord; int fieldIndex = dbGetFieldIndex(paddr); - if (!after) - return 0; - switch (paddr->special) { case SPC_MOD: - init_common(prec); if (fieldIndex >= mbboRecordZRST && fieldIndex <= mbboRecordFFST) { int event = DBE_PROPERTY; + if (!after) return 0; + init_common(prec); if (prec->val == fieldIndex - mbboRecordZRST) event |= DBE_VALUE | DBE_LOG; db_post_events(prec, &prec->val, event); + return 0; + } + if (fieldIndex == mbboRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return 0; } - return 0; default: recGblDbaddrError(S_db_badChoice, paddr, "mbbo: special"); return S_db_badChoice; @@ -427,26 +435,40 @@ static void convert(mbboRecord *prec) static long writeValue(mbboRecord *prec) { - long status; struct mbbodset *pdset = (struct mbbodset *) prec->dset; + long status = 0; - if (prec->pact) - return pdset->write_mbbo(prec); - - status = dbGetLink(&prec->siml, DBR_USHORT, &prec->simm, 0, 0); - if (status) - return status; + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } switch (prec->simm) { case menuYesNoNO: - return pdset->write_mbbo(prec); + status = pdset->write_mbbo(prec); + break; - case menuYesNoYES: + case menuYesNoYES: { recGblSetSevr(prec, SIMM_ALARM, prec->sims); - return dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1); + if (prec->pact || (prec->sdly < 0.)) { + status = dbPutLink(&prec->siol, DBR_USHORT, &prec->val, 1); + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } default: recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); - return -1; + status = -1; } + + return status; } diff --git a/src/std/rec/mbboRecord.dbd b/src/std/rec/mbboRecord.dbd index f841ba018..5475029df 100644 --- a/src/std/rec/mbboRecord.dbd +++ b/src/std/rec/mbboRecord.dbd @@ -465,27 +465,54 @@ recordtype(mbbo) { interest(1) } field(SIOL,DBF_OUTLINK) { - prompt("Sim Output Specifctn") + prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") - interest(1) + special(SPC_MOD) + interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } - field(IVOA,DBF_MENU) { + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } + field(IVOA,DBF_MENU) { prompt("INVALID outpt action") promptgroup("50 - Output") interest(2) diff --git a/src/std/rec/stringinRecord.c b/src/std/rec/stringinRecord.c index 163b23a49..212e79378 100644 --- a/src/std/rec/stringinRecord.c +++ b/src/std/rec/stringinRecord.c @@ -23,6 +23,7 @@ #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -30,6 +31,7 @@ #include "errMdef.h" #include "recSup.h" #include "recGbl.h" +#include "special.h" #include "menuYesNo.h" #define GEN_SIZE_OFFSET @@ -42,7 +44,7 @@ #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); -#define special NULL +static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL @@ -96,10 +98,9 @@ static long init_record(struct dbCommon *pcommon, int pass) STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val)); struct stringindset *pdset = (struct stringindset *) prec->dset; - if (pass==0) - return 0; + if (pass == 0) return 0; - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); recGblInitConstantLink(&prec->siol, DBF_STRING, prec->sval); if (!pdset) { @@ -141,9 +142,9 @@ static long process(struct dbCommon *pcommon) status=readValue(prec); /* read the new value */ /* check if device support set pact */ if ( !pact && prec->pact ) return(0); - prec->pact = TRUE; - recGblGetTimeStamp(prec); + prec->pact = TRUE; + recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); /* check event list */ monitor(prec); @@ -154,6 +155,26 @@ static long process(struct dbCommon *pcommon) return(status); } +static long special(DBADDR *paddr, int after) +{ + stringinRecord *prec = (stringinRecord *)(paddr->precord); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == stringinRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } + default: + recGblDbaddrError(S_db_badChoice, paddr, "stringin: special"); + return(S_db_badChoice); + } +} + static void monitor(stringinRecord *prec) { int monitor_mask = recGblResetAlarms(prec); @@ -174,35 +195,44 @@ static void monitor(stringinRecord *prec) static long readValue(stringinRecord *prec) { - long status; - struct stringindset *pdset = (struct stringindset *) (prec->dset); + struct stringindset *pdset = (struct stringindset *) prec->dset; + long status = 0; - if (prec->pact == TRUE){ - status=(*pdset->read_stringin)(prec); - return(status); - } + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } - status=dbGetLink(&(prec->siml),DBR_USHORT, &(prec->simm),0,0); - if (status) - return(status); + switch (prec->simm) { + case menuYesNoNO: + status = pdset->read_stringin(prec); + break; - if (prec->simm == menuYesNoNO){ - status=(*pdset->read_stringin)(prec); - return(status); - } - if (prec->simm == menuYesNoYES){ - status=dbGetLink(&(prec->siol),DBR_STRING, - prec->sval,0,0); - if (status==0) { - strcpy(prec->val,prec->sval); - prec->udf=FALSE; - } - } else { - status=-1; - recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); - return(status); - } - recGblSetSevr(prec,SIMM_ALARM,prec->sims); + case menuYesNoYES: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbGetLink(&prec->siol, DBR_STRING, prec->sval, 0, 0); + if (status == 0) { + strcpy(prec->val, prec->sval); + prec->udf = FALSE; + } + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } - return(status); + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } + + return status; } diff --git a/src/std/rec/stringinRecord.dbd b/src/std/rec/stringinRecord.dbd index 5b0b76813..ec7b2d570 100644 --- a/src/std/rec/stringinRecord.dbd +++ b/src/std/rec/stringinRecord.dbd @@ -43,7 +43,7 @@ recordtype(stringin) { menu(stringinPOST) } field(SIOL,DBF_INLINK) { - prompt("Sim Input Specifctn") + prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } @@ -53,19 +53,46 @@ recordtype(stringin) { size(40) } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") - interest(1) + special(SPC_MOD) + interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } } diff --git a/src/std/rec/stringoutRecord.c b/src/std/rec/stringoutRecord.c index 416a6db5e..fedc9fc68 100644 --- a/src/std/rec/stringoutRecord.c +++ b/src/std/rec/stringoutRecord.c @@ -23,6 +23,7 @@ #include "dbDefs.h" #include "epicsPrint.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -30,6 +31,7 @@ #include "errMdef.h" #include "recSup.h" #include "recGbl.h" +#include "special.h" #include "menuOmsl.h" #include "menuIvoa.h" #include "menuYesNo.h" @@ -44,7 +46,7 @@ #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); -#define special NULL +static long special(DBADDR *, int); #define get_value NULL #define cvt_dbaddr NULL #define get_array_info NULL @@ -98,10 +100,9 @@ static long init_record(struct dbCommon *pcommon, int pass) STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val)); struct stringoutdset *pdset = (struct stringoutdset *) prec->dset; - if (pass==0) - return 0; + if (pass == 0) return 0; - recGblInitConstantLink(&prec->siml, DBF_USHORT, &prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); if (!pdset) { recGblRecordError(S_dev_noDSET, prec, "stringout: init_record"); @@ -179,13 +180,34 @@ static long process(struct dbCommon *pcommon) if ( !pact && prec->pact ) return(0); prec->pact = TRUE; - recGblGetTimeStamp(prec); + recGblGetTimeStampSimm(prec, prec->simm, NULL); + monitor(prec); recGblFwdLink(prec); prec->pact=FALSE; return(status); } - + +static long special(DBADDR *paddr, int after) +{ + stringoutRecord *prec = (stringoutRecord *)(paddr->precord); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == stringoutRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return 0; + } + default: + recGblDbaddrError(S_db_badChoice, paddr, "stringout: special"); + return(S_db_badChoice); + } +} + static void monitor(stringoutRecord *prec) { int monitor_mask = recGblResetAlarms(prec); @@ -206,32 +228,40 @@ static void monitor(stringoutRecord *prec) static long writeValue(stringoutRecord *prec) { - long status; - struct stringoutdset *pdset = (struct stringoutdset *) (prec->dset); + struct stringoutdset *pdset = (struct stringoutdset *) prec->dset; + long status = 0; - if (prec->pact == TRUE){ - status=(*pdset->write_stringout)(prec); - return(status); - } + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; + } - status=dbGetLink(&(prec->siml),DBR_USHORT, - &(prec->simm),0,0); - if (status) - return(status); + switch (prec->simm) { + case menuYesNoNO: + status = pdset->write_stringout(prec); + break; - if (prec->simm == menuYesNoNO){ - status=(*pdset->write_stringout)(prec); - return(status); - } - if (prec->simm == menuYesNoYES){ - status=dbPutLink(&prec->siol,DBR_STRING, - prec->val,1); - } else { - status=-1; - recGblSetSevr(prec,SOFT_ALARM,INVALID_ALARM); - return(status); - } - recGblSetSevr(prec,SIMM_ALARM,prec->sims); + case menuYesNoYES: { + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbPutLink(&prec->siol, DBR_STRING, &prec->val, 1); + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; + } + break; + } - return(status); + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; + } + + return status; } diff --git a/src/std/rec/stringoutRecord.dbd b/src/std/rec/stringoutRecord.dbd index fe0bae5d0..358340c6d 100644 --- a/src/std/rec/stringoutRecord.dbd +++ b/src/std/rec/stringoutRecord.dbd @@ -54,27 +54,54 @@ recordtype(stringout) { menu(stringoutPOST) } field(SIOL,DBF_OUTLINK) { - prompt("Sim Output Specifctn") + prompt("Simulation Output Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") + special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } - field(IVOA,DBF_MENU) { + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } + field(IVOA,DBF_MENU) { prompt("INVALID output action") promptgroup("50 - Output") interest(2) diff --git a/src/std/rec/waveformRecord.c b/src/std/rec/waveformRecord.c index c06c48001..c987cfbc4 100644 --- a/src/std/rec/waveformRecord.c +++ b/src/std/rec/waveformRecord.c @@ -23,6 +23,7 @@ #include "epicsPrint.h" #include "epicsString.h" #include "alarm.h" +#include "callback.h" #include "dbAccess.h" #include "dbEvent.h" #include "dbFldTypes.h" @@ -31,6 +32,7 @@ #include "errMdef.h" #include "recSup.h" #include "recGbl.h" +#include "special.h" #include "cantProceed.h" #include "menuYesNo.h" @@ -44,7 +46,7 @@ #define initialize NULL static long init_record(struct dbCommon *, int); static long process(struct dbCommon *); -#define special NULL +static long special(DBADDR *, int); #define get_value NULL static long cvt_dbaddr(DBADDR *); static long get_array_info(DBADDR *, long *, long *); @@ -95,7 +97,7 @@ static long init_record(struct dbCommon *pcommon, int pass) struct waveformRecord *prec = (struct waveformRecord *)pcommon; struct wfdset *pdset; - if (pass==0){ + if (pass == 0){ if (prec->nelm <= 0) prec->nelm = 1; if (prec->ftvl > DBF_ENUM) @@ -110,7 +112,7 @@ static long init_record(struct dbCommon *pcommon, int pass) return 0; } - recGblInitConstantLink(&prec->siml,DBF_USHORT,&prec->simm); + recGblInitSimm(pcommon, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); /* must have dset defined */ if (!(pdset = (struct wfdset *)(prec->dset))) { @@ -144,9 +146,9 @@ static long process(struct dbCommon *pcommon) readValue(prec); /* read the new value */ if (!pact && prec->pact) return 0; - prec->pact = TRUE; prec->udf = FALSE; - recGblGetTimeStamp(prec); + prec->pact = TRUE; + recGblGetTimeStampSimm(prec, prec->simm, &prec->siol); monitor(prec); @@ -157,6 +159,26 @@ static long process(struct dbCommon *pcommon) return 0; } +static long special(DBADDR *paddr, int after) +{ + waveformRecord *prec = (waveformRecord *)(paddr->precord); + int special_type = paddr->special; + + switch(special_type) { + case(SPC_MOD): + if (dbGetFieldIndex(paddr) == waveformRecordSIMM) { + if (!after) + recGblSaveSimm(prec->sscn, &prec->oldsimm, prec->simm); + else + recGblCheckSimm((dbCommon *)prec, &prec->sscn, prec->oldsimm, prec->simm); + return(0); + } + default: + recGblDbaddrError(S_db_badChoice, paddr, "waveform: special"); + return(S_db_badChoice); + } +} + static long cvt_dbaddr(DBADDR *paddr) { waveformRecord *prec = (waveformRecord *) paddr->precord; @@ -304,43 +326,53 @@ static void monitor(waveformRecord *prec) static long readValue(waveformRecord *prec) { - long status; struct wfdset *pdset = (struct wfdset *) prec->dset; + long status = 0; - if (prec->pact == TRUE){ - return (*pdset->read_wf)(prec); + if (!prec->pact) { + status = recGblGetSimm((dbCommon *)prec, &prec->sscn, &prec->oldsimm, &prec->simm, &prec->siml); + if (status) return status; } - status = dbGetLink(&prec->siml, DBR_ENUM, &prec->simm, 0, 0); - if (status) - return status; - - if (prec->simm == menuYesNoNO){ + switch (prec->simm) { + case menuYesNoNO: { epicsUInt32 nord = prec->nord; - status = (*pdset->read_wf)(prec); + status = pdset->read_wf(prec); if (nord != prec->nord) db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); - return status; + break; } - if (prec->simm == menuYesNoYES){ + case menuYesNoYES: { long nRequest = prec->nelm; - status = dbGetLink(&prec->siol, prec->ftvl, prec->bptr, 0, &nRequest); - /* nord set only for db links: needed for old db_access */ - if (!dbLinkIsConstant(&prec->siol)) { - prec->nord = nRequest; - db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); - if (status == 0) - prec->udf=FALSE; + recGblSetSevr(prec, SIMM_ALARM, prec->sims); + if (prec->pact || (prec->sdly < 0.)) { + status = dbGetLink(&prec->siol, prec->ftvl, prec->bptr, 0, &nRequest); + if (status == 0) prec->udf = FALSE; + /* nord set only for db links: needed for old db_access */ + if (!dbLinkIsConstant(&prec->siol)) { + prec->nord = nRequest; + db_post_events(prec, &prec->nord, DBE_VALUE | DBE_LOG); + } + prec->pact = FALSE; + } else { /* !prec->pact && delay >= 0. */ + CALLBACK *pvt = prec->simpvt; + if (!pvt) { + pvt = calloc(1, sizeof(CALLBACK)); /* very lazy allocation of callback structure */ + prec->simpvt = pvt; + } + if (pvt) callbackRequestProcessCallbackDelayed(pvt, prec->prio, prec, prec->sdly); + prec->pact = TRUE; } - } else { - recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); - return -1; + break; + } + + default: + recGblSetSevr(prec, SOFT_ALARM, INVALID_ALARM); + status = -1; } - recGblSetSevr(prec, SIMM_ALARM, prec->sims); return status; } - diff --git a/src/std/rec/waveformRecord.dbd.pod b/src/std/rec/waveformRecord.dbd.pod index db2fa05fb..82561d076 100644 --- a/src/std/rec/waveformRecord.dbd.pod +++ b/src/std/rec/waveformRecord.dbd.pod @@ -119,26 +119,53 @@ recordtype(waveform) { extra("void * bptr") } field(SIOL,DBF_INLINK) { - prompt("Sim Input Specifctn") + prompt("Simulation Input Link") promptgroup("90 - Simulate") interest(1) } field(SIML,DBF_INLINK) { - prompt("Sim Mode Location") + prompt("Simulation Mode Link") promptgroup("90 - Simulate") interest(1) } field(SIMM,DBF_MENU) { prompt("Simulation Mode") + special(SPC_MOD) interest(1) menu(menuYesNo) } field(SIMS,DBF_MENU) { - prompt("Sim mode Alarm Svrty") + prompt("Simulation Mode Severity") promptgroup("90 - Simulate") interest(2) menu(menuAlarmSevr) } + field(OLDSIMM,DBF_MENU) { + prompt("Prev. Simulation Mode") + special(SPC_NOMOD) + interest(4) + menu(menuSimm) + } + field(SSCN,DBF_MENU) { + prompt("Sim. Mode Scan") + promptgroup("90 - Simulate") + interest(1) + menu(menuScan) + initial("65535") + } + field(SDLY,DBF_DOUBLE) { + prompt("Sim. Mode Async Delay") + promptgroup("90 - Simulate") + interest(2) + initial("-1.0") + } + %#include "callback.h" + field(SIMPVT,DBF_NOACCESS) { + prompt("Sim. Mode Private") + special(SPC_NOMOD) + interest(4) + extra("CALLBACK *simpvt") + } field(MPST,DBF_MENU) { prompt("Post Value Monitors") promptgroup("80 - Display") From f482ee6fe1453993c9514915d796bc22ba0385bf Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Mon, 16 Oct 2017 17:15:36 +0200 Subject: [PATCH 20/47] rec: add tests for simulation mode --- src/std/rec/test/Makefile | 13 + src/std/rec/test/epicsRunRecordTests.c | 3 + src/std/rec/test/simmSetup.db | 16 + src/std/rec/test/simmTest.c | 468 ++++++++++++++++++++ src/std/rec/test/simmTest.substitutions | 30 ++ src/std/rec/test/simmTestArray.template | 15 + src/std/rec/test/simmTestHistogram.template | 12 + src/std/rec/test/simmTestSimple.template | 10 + 8 files changed, 567 insertions(+) create mode 100644 src/std/rec/test/simmSetup.db create mode 100644 src/std/rec/test/simmTest.c create mode 100644 src/std/rec/test/simmTest.substitutions create mode 100644 src/std/rec/test/simmTestArray.template create mode 100644 src/std/rec/test/simmTestHistogram.template create mode 100644 src/std/rec/test/simmTestSimple.template diff --git a/src/std/rec/test/Makefile b/src/std/rec/test/Makefile index 5a591b230..129c6e91f 100644 --- a/src/std/rec/test/Makefile +++ b/src/std/rec/test/Makefile @@ -104,6 +104,19 @@ regressTest_SRCS += regressTest_registerRecordDeviceDriver.cpp TESTFILES += $(COMMON_DIR)/regressTest.dbd ../regressArray1.db ../regressHex.db ../regressLinkMS.db TESTS += regressTest +TARGETS += $(COMMON_DIR)/simmTest.dbd +TARGETS += $(COMMON_DIR)/simmTest.db +DBDDEPENDS_FILES += simmTest.dbd$(DEP) +DBDDEPENDS_FILES += simmTest.db$(DEP) +simmTest_DBD += base.dbd +TESTPROD_HOST += simmTest +simmTest_SRCS += simmTest.c +simmTest_SRCS += simmTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += simmTest.c +testHarness_SRCS += simmTest_registerRecordDeviceDriver.cpp +TESTFILES += $(COMMON_DIR)/simmTest.dbd $(COMMON_DIR)/simmTest.db +TESTS += simmTest + # epicsRunRecordTests runs all the test programs in a known working order. testHarness_SRCS += epicsRunRecordTests.c diff --git a/src/std/rec/test/epicsRunRecordTests.c b/src/std/rec/test/epicsRunRecordTests.c index 8c551c1ab..6214f4aec 100644 --- a/src/std/rec/test/epicsRunRecordTests.c +++ b/src/std/rec/test/epicsRunRecordTests.c @@ -20,6 +20,7 @@ int asTest(void); int linkRetargetLinkTest(void); int linkInitTest(void); int asyncSoftTest(void); +int simmTest(void); void epicsRunRecordTests(void) { @@ -41,5 +42,7 @@ void epicsRunRecordTests(void) runTest(asyncSoftTest); + runTest(simmTest); + epicsExit(0); /* Trigger test harness */ } diff --git a/src/std/rec/test/simmSetup.db b/src/std/rec/test/simmSetup.db new file mode 100644 index 000000000..c8650e08b --- /dev/null +++ b/src/std/rec/test/simmSetup.db @@ -0,0 +1,16 @@ +# no info +record(ai, "ai-0") { +} +# only scan +record(ai, "ai-1") { + field(SSCN,"2 second") +} +# only delay +record(ai, "ai-2") { + field(SDLY,".234") +} +# scan and delay +record(ai, "ai-3") { + field(SSCN,"5 second") + field(SDLY,".345") +} diff --git a/src/std/rec/test/simmTest.c b/src/std/rec/test/simmTest.c new file mode 100644 index 000000000..a828ffe63 --- /dev/null +++ b/src/std/rec/test/simmTest.c @@ -0,0 +1,468 @@ +/*************************************************************************\ +* Copyright (c) 2017 ITER Organization +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "recSup.h" +#include "aiRecord.h" +#include "aoRecord.h" +#include "aaiRecord.h" +#include "aaoRecord.h" +#include "biRecord.h" +#include "boRecord.h" +#include "mbbiRecord.h" +#include "mbboRecord.h" +#include "mbbiDirectRecord.h" +#include "mbboDirectRecord.h" +#include "longinRecord.h" +#include "longoutRecord.h" +#include "int64inRecord.h" +#include "int64outRecord.h" +#include "stringinRecord.h" +#include "stringoutRecord.h" +#include "lsiRecord.h" +#include "lsoRecord.h" +#include "eventRecord.h" +#include "histogramRecord.h" +#include "waveformRecord.h" + +/* + * Tests for simulation mode + */ + +void simmTest_registerRecordDeviceDriver(struct dbBase *); + +static +void startSimmTestIoc(const char *dbfile) +{ + testdbPrepare(); + testdbReadDatabase("simmTest.dbd", NULL, NULL); + simmTest_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase(dbfile, NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); +} + +static char *rawSupp[] = { + "ai", + "bi", + "mbbi", + "mbbiDirect", +}; + +static +int hasRawSimmSupport(const char *rectype) { + int i; + for (i = 0; i < (sizeof(rawSupp)/sizeof(rawSupp[0])); i++) + if (strcmp(rectype, rawSupp[i]) == 0) return 1; + return 0; +} + +#define PVNAMELENGTH 60 +static char nameVAL[PVNAMELENGTH]; +static char nameB0[PVNAMELENGTH]; +static char nameRVAL[PVNAMELENGTH]; +static char nameSGNL[PVNAMELENGTH]; +static char nameSIMM[PVNAMELENGTH]; +static char nameSIML[PVNAMELENGTH]; +static char nameSVAL[PVNAMELENGTH]; +static char nameSIOL[PVNAMELENGTH]; +static char nameSCAN[PVNAMELENGTH]; +static char namePROC[PVNAMELENGTH]; +static char namePACT[PVNAMELENGTH]; +static char nameSTAT[PVNAMELENGTH]; +static char nameSEVR[PVNAMELENGTH]; +static char nameSIMS[PVNAMELENGTH]; +static char nameTSE[PVNAMELENGTH]; +static char nameSimmode[PVNAMELENGTH]; +static char nameSimval[PVNAMELENGTH]; +static char nameSimvalNORD[PVNAMELENGTH]; +static char nameSimvalLEN[PVNAMELENGTH]; + +#define SETNAME(field) strcpy(name ## field, name); strcat(name ## field, "." #field) +static +void setNames(const char *name) +{ + SETNAME(VAL); SETNAME(B0); SETNAME(RVAL); SETNAME(SGNL); + SETNAME(SVAL); SETNAME(SIMM); SETNAME(SIML); SETNAME(SIOL); SETNAME(SIMS); + SETNAME(SCAN); SETNAME(PROC); SETNAME(PACT); + SETNAME(STAT); SETNAME(SEVR); SETNAME(TSE); + strcpy(nameSimmode, name); strcat(nameSimmode, ":simmode"); + strcpy(nameSimval, name); strcat(nameSimval, ":simval"); + strcpy(nameSimvalNORD, name); strcat(nameSimvalNORD, ":simval.NORD"); + strcpy(nameSimvalLEN, name); strcat(nameSimvalLEN, ":simval.LEN"); +} + +/* + * Parsing of info items and xsimm structure setting + */ +static +void testSimmSetup(void) +{ + aiRecord *precai; + + testDiag("##### Simm initialization #####"); + + /* no config */ + precai = (aiRecord*)testdbRecordPtr("ai-0"); + testOk(precai->simpvt == NULL, "ai-0.SIMPVT = %p == NULL [no callback]", precai->simpvt); + testOk(precai->sscn == USHRT_MAX, "ai-0.SSCN = %u == USHRT_MAX (not set)", precai->sscn); + testOk(precai->sdly < 0., "ai-0.SDLY = %g < 0.0 (not set)", precai->sdly); + + /* with SCAN */ + precai = (aiRecord*)testdbRecordPtr("ai-1"); + testOk(precai->sscn == 5, "ai-1.SSCN = %u == 5 (2 second)", precai->sscn); + testOk(precai->sdly < 0., "ai-1.SDLY = %g < 0.0 (not set)", precai->sdly); + + /* with DELAY */ + precai = (aiRecord*)testdbRecordPtr("ai-2"); + testOk(precai->sscn == USHRT_MAX, "ai-2.SSCN = %u == USHRT_MAX (not set)", precai->sscn); + testOk(precai->sdly == 0.234, "ai-2.SDLY = %g == 0.234", precai->sdly); + + /* with SCAN and DELAY */ + precai = (aiRecord*)testdbRecordPtr("ai-3"); + testOk(precai->sscn == 4, "ai-3.SSCN = %u == 4 (5 second)", precai->sscn); + testOk(precai->sdly == 0.345, "ai-3.SDLY = %g == 0.345", precai->sdly); +} + +/* + * SIMM triggered SCAN swapping, by writing to SIMM and through SIML + */ + +static +void testSimmToggle(const char *name, epicsEnum16 *psscn) +{ + testDiag("## SIMM toggle and SCAN swapping ##"); + + /* SIMM mode by setting the field */ + + testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0); + testOk(*psscn == 1, "SSCN = %u == 1 (Event)", *psscn); + + testDiag("set SIMM to YES"); + testdbPutFieldOk(nameSIMM, DBR_STRING, "YES"); + testdbGetFieldEqual(nameSCAN, DBR_USHORT, 1); + testOk(*psscn == 0, "SSCN = %u == 0 (Passive)", *psscn); + + /* Change simm:SCAN when simmYES */ + testdbPutFieldOk(nameSCAN, DBR_USHORT, 3); + + testDiag("set SIMM to NO"); + testdbPutFieldOk(nameSIMM, DBR_STRING, "NO"); + testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0); + testOk(*psscn == 3, "SSCN = %u == 3 (10 second)", *psscn); + *psscn = 1; + + if (hasRawSimmSupport(name)) { + testDiag("set SIMM to RAW"); + testdbPutFieldOk(nameSIMM, DBR_STRING, "RAW"); + testdbGetFieldEqual(nameSCAN, DBR_USHORT, 1); + testOk(*psscn == 0, "SSCN = %u == 0 (Passive)", *psscn); + + testDiag("set SIMM to NO"); + testdbPutFieldOk(nameSIMM, DBR_STRING, "NO"); + testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0); + testOk(*psscn == 1, "SSCN = %u == 1 (Event)", *psscn); + } else { + testDiag("Record type %s has no support for simmRAW", name); + } + + /* SIMM mode through SIML */ + + testdbPutFieldOk(nameSIML, DBR_STRING, nameSimmode); + + testDiag("set SIMM (via SIML -> simmode) to YES"); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 1); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + + testdbGetFieldEqual(nameSIMM, DBR_USHORT, 1); + testdbGetFieldEqual(nameSCAN, DBR_USHORT, 1); + testOk(*psscn == 0, "SSCN = %u == 0 (Passive)", *psscn); + + testDiag("set SIMM (via SIML -> simmode) to NO"); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 0); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + + testdbGetFieldEqual(nameSIMM, DBR_USHORT, 0); + testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0); + testOk(*psscn == 1, "SSCN = %u == 1 (Event)", *psscn); + + if (hasRawSimmSupport(name)) { + testDiag("set SIMM (via SIML -> simmode) to RAW"); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 2); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + + testdbGetFieldEqual(nameSIMM, DBR_USHORT, 2); + testdbGetFieldEqual(nameSCAN, DBR_USHORT, 1); + testOk(*psscn == 0, "SSCN = %u == 0 (Passive)", *psscn); + + testDiag("set SIMM (via SIML -> simmode) to NO"); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 0); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + + testdbGetFieldEqual(nameSIMM, DBR_USHORT, 0); + testdbGetFieldEqual(nameSCAN, DBR_USHORT, 0); + testOk(*psscn == 1, "SSCN = %u == 1 (Event)", *psscn); + } else { + testDiag("Record type %s has no support for simmRAW", name); + } +} + +/* + * Reading from SVAL (direct write or through SIOL link) + */ + +static +void testSvalRead(const char *name, + const epicsTimeStamp *mytime, + const epicsTimeStamp *svtime) +{ + epicsTimeStamp last; + + if (strcmp(name, "histogram") == 0) + strcpy(nameVAL, nameSGNL); + + if (strcmp(name, "aai") != 0 && + strcmp(name, "waveform") != 0 && + strcmp(name, "lsi") != 0) { + + testDiag("## Reading from SVAL ##"); + + testDiag("in simmNO, SVAL must be ignored"); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 0); + testdbPutFieldOk(nameVAL, DBR_LONG, 0); + if (strcmp(name, "stringin") == 0) + testdbPutFieldOk(nameSVAL, DBR_STRING, "1"); + else + testdbPutFieldOk(nameSVAL, DBR_USHORT, 1); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + testdbGetFieldEqual(nameVAL, DBR_USHORT, 0); + + testDiag("in simmYES, SVAL is used for VAL"); + testdbPutFieldOk(nameSIMS, DBR_USHORT, 0); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 1); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + testdbGetFieldEqual(nameVAL, DBR_USHORT, 1); + testDiag("No SIMS setting: STAT/SEVR == NO_ALARM"); + testdbGetFieldEqual(nameSTAT, DBR_STRING, "NO_ALARM"); + testdbGetFieldEqual(nameSEVR, DBR_USHORT, 0); + + if (hasRawSimmSupport(name)) { + testDiag("in simmRAW, SVAL is used for RVAL"); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 2); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + testdbGetFieldEqual(nameRVAL, DBR_USHORT, 1); + } else { + testDiag("Record type %s has no support for simmRAW", name); + } + } + + testDiag("## Reading from SIOL->SVAL ##"); + + /* Set SIOL link to simval */ + testdbPutFieldOk(nameSIOL, DBR_STRING, nameSimval); + + testDiag("in simmNO, SIOL->SVAL must be ignored"); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 0); + testdbPutFieldOk(nameVAL, DBR_LONG, 0); + testdbPutFieldOk(nameSimval, DBR_LONG, 1); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + testdbGetFieldEqual(nameVAL, DBR_USHORT, 0); + + testDiag("in simmYES, SIOL->SVAL is used for VAL"); + testdbPutFieldOk(nameSIMS, DBR_USHORT, 3); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 1); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + testdbGetFieldEqual(nameVAL, DBR_USHORT, 1); + testDiag("SIMS is INVALID: STAT/SEVR == SIMM/INVALID"); + testdbGetFieldEqual(nameSTAT, DBR_STRING, "SIMM"); + testdbGetFieldEqual(nameSEVR, DBR_USHORT, 3); + testdbPutFieldOk(nameSIMS, DBR_USHORT, 0); + + if (hasRawSimmSupport(name)) { + testDiag("in simmRAW, SIOL->SVAL is used for RVAL"); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 2); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + testdbGetFieldEqual(nameRVAL, DBR_USHORT, 1); + } else { + testDiag("Record type %s has no support for simmRAW", name); + } + + /* My timestamp must be later than simval's */ + testOk(epicsTimeLessThan(svtime, mytime), "simval time < my time [TSE = 0]"); + + testDiag("for TSE=-2 (from device) and simmYES, take time stamp from IOC or input link"); + + /* Set simmYES */ + testdbPutFieldOk(nameSimmode, DBR_USHORT, 1); + + /* Set TSE to -2 (from device) and reprocess: timestamps is taken through SIOL from simval */ + testdbPutFieldOk(nameTSE, DBR_SHORT, -2); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + testOk(epicsTimeEqual(svtime, mytime), "simval time == my time [TSE = -2]"); + last = *mytime; + + /* With TSE=-2 and no SIOL, timestamp is taken from IOC */ + testdbPutFieldOk(nameSIOL, DBR_STRING, ""); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + testOk(epicsTimeLessThan(&last, mytime), "new time stamp from IOC [TSE = -2, no SIOL]"); + + /* Reset TSE */ + testdbPutFieldOk(nameTSE, DBR_SHORT, 0); +} + +/* + * Writing through SIOL link + */ + +static +void testSiolWrite(const char *name, + const epicsTimeStamp *mytime) +{ + epicsTimeStamp now; + + testDiag("## Writing through SIOL ##"); + + /* Set SIOL link to simval */ + testdbPutFieldOk(nameSIOL, DBR_STRING, nameSimval); + + testDiag("in simmNO, SIOL must be ignored"); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 0); + if (strcmp(name, "mbboDirect") == 0) + testdbPutFieldOk(nameB0, DBR_LONG, 1); + else + testdbPutFieldOk(nameVAL, DBR_LONG, 1); + + if (strcmp(name, "aao") == 0) + testdbGetFieldEqual(nameSimvalNORD, DBR_USHORT, 0); + else if (strcmp(name, "lso") == 0) + testdbGetFieldEqual(nameSimvalLEN, DBR_USHORT, 0); + else + testdbGetFieldEqual(nameSimval, DBR_USHORT, 0); + + testDiag("in simmYES, SIOL is used to write VAL"); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 1); + if (strcmp(name, "mbboDirect") == 0) + testdbPutFieldOk(nameB0, DBR_LONG, 1); + else + testdbPutFieldOk(nameVAL, DBR_LONG, 1); + testdbGetFieldEqual(nameSimval, DBR_USHORT, 1); + + /* Set TSE to -2 (from device) and reprocess: timestamp is taken from IOC */ + epicsTimeGetCurrent(&now); + testdbPutFieldOk(nameTSE, DBR_SHORT, -2); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + testOk(epicsTimeLessThan(&now, mytime), "new time stamp from IOC [TSE = -2]"); + + /* Reset TSE */ + testdbPutFieldOk(nameTSE, DBR_SHORT, 0); +} + +/* + * Asynchronous processing using simm:DELAY + */ + +static +void testSimmDelay(const char *name, + epicsFloat64 *psdly, + const epicsTimeStamp *mytime) +{ + epicsTimeStamp now; + const double delay = 0.01; /* 10 ms */ + + testDiag("## Asynchronous processing with simm:DELAY ##"); + + /* Set delay to something just long enough */ + *psdly = delay; + + /* Process in simmNO: synchronous */ + testDiag("simm:DELAY and simmNO processes synchronously"); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 0); + epicsTimeGetCurrent(&now); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + testdbGetFieldEqual(namePACT, DBR_USHORT, 0); + testOk(epicsTimeLessThan(&now, mytime), "time stamp is recent"); + + /* Process in simmYES: asynchronous */ + testDiag("simm:DELAY and simmYES processes asynchronously"); + testdbPutFieldOk(nameSimmode, DBR_USHORT, 1); + testdbPutFieldOk(namePROC, DBR_LONG, 0); + testdbGetFieldEqual(namePACT, DBR_USHORT, 1); + epicsTimeGetCurrent(&now); + epicsThreadSleep(1.5*delay); + testdbGetFieldEqual(namePACT, DBR_USHORT, 0); + testOk(epicsTimeLessThan(&now, mytime), "time stamp taken from second pass processing"); + + /* Reset delay */ + *psdly = -1.; +} + +#define RUNALLTESTSREAD(type) \ + testDiag("################################################### Record Type " #type); \ + setNames(#type); \ + testSimmToggle(#type, &((type ## Record*)testdbRecordPtr(#type))->sscn); \ + testSvalRead(#type, &((type ## Record*)testdbRecordPtr(#type))->time, \ + &((type ## Record*)testdbRecordPtr(#type ":simval"))->time); \ + testSimmDelay(#type, &((type ## Record*)testdbRecordPtr(#type))->sdly, \ + &((type ## Record*)testdbRecordPtr(#type))->time) + +#define RUNALLTESTSWRITE(type) \ + testDiag("################################################### Record Type " #type); \ + setNames(#type); \ + testSimmToggle(#type, &((type ## Record*)testdbRecordPtr(#type))->sscn); \ + testSiolWrite(#type, &((type ## Record*)testdbRecordPtr(#type))->time); \ + testSimmDelay(#type, &((type ## Record*)testdbRecordPtr(#type))->sdly, \ + &((type ## Record*)testdbRecordPtr(#type))->time) + +static +void testAllRecTypes(void) +{ + RUNALLTESTSREAD(ai); + RUNALLTESTSWRITE(ao); + RUNALLTESTSREAD(aai); + RUNALLTESTSWRITE(aao); + RUNALLTESTSREAD(bi); + RUNALLTESTSWRITE(bo); + RUNALLTESTSREAD(mbbi); + RUNALLTESTSWRITE(mbbo); + RUNALLTESTSREAD(mbbiDirect); + RUNALLTESTSWRITE(mbboDirect); + RUNALLTESTSREAD(longin); + RUNALLTESTSWRITE(longout); + RUNALLTESTSREAD(int64in); + RUNALLTESTSWRITE(int64out); + RUNALLTESTSREAD(stringin); + RUNALLTESTSWRITE(stringout); + RUNALLTESTSREAD(lsi); + RUNALLTESTSWRITE(lso); + RUNALLTESTSREAD(event); + RUNALLTESTSREAD(waveform); + RUNALLTESTSREAD(histogram); +} + + +MAIN(simmTest) +{ + testPlan(0); + startSimmTestIoc("simmTest.db"); + + testSimmSetup(); + testAllRecTypes(); + + testIocShutdownOk(); + testdbCleanup(); + return testDone(); +} diff --git a/src/std/rec/test/simmTest.substitutions b/src/std/rec/test/simmTest.substitutions new file mode 100644 index 000000000..f3db61da0 --- /dev/null +++ b/src/std/rec/test/simmTest.substitutions @@ -0,0 +1,30 @@ +file "simmTestSimple.template" { +{ TYPE="ai" } +{ TYPE="ao" } +{ TYPE="bi" } +{ TYPE="bo" } +{ TYPE="mbbi" } +{ TYPE="mbbo" } +{ TYPE="mbbiDirect" } +{ TYPE="mbboDirect" } +{ TYPE="longin" } +{ TYPE="longout" } +{ TYPE="int64in" } +{ TYPE="int64out" } +{ TYPE="stringin" } +{ TYPE="stringout" } +{ TYPE="lsi" } +{ TYPE="lso" } +{ TYPE="event" } +} +file "simmTestArray.template" { +{ TYPE="aai" } +{ TYPE="aao" } +{ TYPE="waveform" } +} +file "simmTestHistogram.template" { +{ TYPE="histogram" } +} +file "simmSetup.db" { +{} +} diff --git a/src/std/rec/test/simmTestArray.template b/src/std/rec/test/simmTestArray.template new file mode 100644 index 000000000..36ca2c009 --- /dev/null +++ b/src/std/rec/test/simmTestArray.template @@ -0,0 +1,15 @@ +# Array type records +# Regular simulation mode and simm:SCAN tests +record($(TYPE), "$(TYPE)") { + field(SSCN,"Event") + field(FTVL,"SHORT") + field(NELM,"2") +} +record($(TYPE), "$(TYPE):simval") { + field(FTVL,"SHORT") + field(NELM,"2") +} +record(bo, "$(TYPE):simmode") { + field(ZNAM,"off") + field(ONAM,"on") +} diff --git a/src/std/rec/test/simmTestHistogram.template b/src/std/rec/test/simmTestHistogram.template new file mode 100644 index 000000000..574314f00 --- /dev/null +++ b/src/std/rec/test/simmTestHistogram.template @@ -0,0 +1,12 @@ +# Array type records +# Regular simulation mode and simm:SCAN tests +record($(TYPE), "$(TYPE)") { + field(SSCN,"Event") + field(NELM,"2") +} +record(ai, "$(TYPE):simval") { +} +record(bo, "$(TYPE):simmode") { + field(ZNAM,"off") + field(ONAM,"on") +} diff --git a/src/std/rec/test/simmTestSimple.template b/src/std/rec/test/simmTestSimple.template new file mode 100644 index 000000000..a1a73b5db --- /dev/null +++ b/src/std/rec/test/simmTestSimple.template @@ -0,0 +1,10 @@ +# Regular simulation mode and simm:SCAN tests +record($(TYPE), "$(TYPE)") { + field(SSCN,"Event") +} +record($(TYPE), "$(TYPE):simval") { +} +record(bo, "$(TYPE):simmode") { + field(ZNAM,"off") + field(ONAM,"on") +} From 16b7a413df5d5f855aa2b60037758bca4b88f1a8 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 17 Oct 2017 11:28:02 +0200 Subject: [PATCH 21/47] doc: add simulation mode changes to release notes --- documentation/RELEASE_NOTES.html | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 111c604d3..164268778 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -17,6 +17,18 @@ --> +

Changes made between 3.16.1 and 3.16.2

+ +

Simulation mode improvements

+ +

Records that support simulation mode have two new fields, SSCN +(Simulation Scan Mode) and SDLY (Simulation Delay). +SSCN sets the scan mechanism to be used while the record is in +simulation mode. This is especially useful for I/O scanned records, for which +simulation mode was not working at all. Setting SDLY to a positive +value makes the record process asynchronously in simulation mode, with the +second stage processing happening after the specified time (in seconds).

+

Changes made between 3.16.0.1 and 3.16.1

IOC Database Support for 64-bit integers

From a93932427088c190103424795b1c0074561baf75 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 18 Oct 2017 17:13:46 -0500 Subject: [PATCH 22/47] Added call to dbStopServers() in iocShutdown() Renamed buildRSRV => buildServers. Don't call other dbXxxServers() routines or announce their related initHook states when in buildIsolated mode. --- src/ioc/misc/iocInit.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 738d9b472..c6d6e4a14 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -75,7 +75,7 @@ static enum { iocVirgin, iocBuilding, iocBuilt, iocRunning, iocPaused, iocStopped } iocState = iocVirgin; static enum { - buildRSRV, buildIsolated + buildServers, buildIsolated } iocBuildMode; /* define forward references*/ @@ -210,7 +210,7 @@ int iocBuild(void) if (dbThreadRealtimeLock) epicsThreadRealtimeLock(); - if (!status) iocBuildMode = buildRSRV; + if (!status) iocBuildMode = buildServers; return status; } @@ -246,9 +246,11 @@ int iocRun(void) if (iocState == iocBuilt) initHookAnnounce(initHookAfterInterruptAccept); - dbRunServers(); + if (iocBuildMode == buildServers) { + dbRunServers(); + initHookAnnounce(initHookAfterCaServerRunning); + } - initHookAnnounce(initHookAfterCaServerRunning); if (iocState == iocBuilt) initHookAnnounce(initHookAtEnd); @@ -268,8 +270,10 @@ int iocPause(void) } initHookAnnounce(initHookAtIocPause); - dbPauseServers(); - initHookAnnounce(initHookAfterCaServerPaused); + if (iocBuildMode == buildServers) { + dbPauseServers(); + initHookAnnounce(initHookAfterCaServerPaused); + } dbCaPause(); scanPause(); @@ -702,27 +706,37 @@ static void doFreeRecord(dbRecordType *pdbRecordType, dbCommon *precord, int iocShutdown(void) { - if (iocState == iocVirgin || iocState == iocStopped) return 0; + if (iocState == iocVirgin || iocState == iocStopped) + return 0; + iterateRecords(doCloseLinks, NULL); - if (iocBuildMode==buildIsolated) { + + if (iocBuildMode == buildIsolated) { /* stop and "join" threads */ scanStop(); callbackStop(); } + else + dbStopServers(); + dbCaShutdown(); /* must be before dbFreeRecord and dbChannelExit */ - if (iocBuildMode==buildIsolated) { + + if (iocBuildMode == buildIsolated) { /* free resources */ scanCleanup(); callbackCleanup(); + iterateRecords(doFreeRecord, NULL); dbLockCleanupRecords(pdbbase); + asShutdown(); dbChannelExit(); dbProcessNotifyExit(); iocshFree(); } + iocState = iocStopped; - iocBuildMode = buildRSRV; + iocBuildMode = buildServers; return 0; } From 0406ecebe95ddf29cd9e44ab7c34e3c80997e59f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 18 Oct 2017 17:35:37 -0500 Subject: [PATCH 23/47] Ignore dbServer registrations after init Actually store all server states, and display it in dbsr() --- src/ioc/db/dbServer.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index 47ed5d8e8..a18477723 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -20,13 +20,17 @@ #include "dbServer.h" static ELLLIST serverList = ELLLIST_INIT; - +static enum { registering, initialized, running, paused, stopped } + state = registering; +static char *stateNames[] = { + "registering", "initialized", "running", "paused", "stopped" +}; int dbRegisterServer(dbServer *psrv) { const char * ignore = envGetConfigParamPtr(&EPICS_IOC_IGNORE_SERVERS); - if (!psrv || !psrv->name) + if (!psrv || !psrv->name || state != registering) return -1; if (strchr(psrv->name, ' ')) { @@ -70,6 +74,8 @@ void dbsr(unsigned level) return; } + printf("Server state: %s\n", stateNames[state]); + while (psrv) { printf("Server '%s':\n", psrv->name); if (psrv->report) @@ -91,7 +97,7 @@ int dbServerClient(char *pBuf, size_t bufSize) return -1; } -#define STARTSTOP(routine, method) \ +#define STARTSTOP(routine, method, newState) \ void routine(void) \ { \ dbServer *psrv = (dbServer *)ellFirst(&serverList); \ @@ -101,9 +107,10 @@ void routine(void) \ psrv->method(); \ psrv = (dbServer *)ellNext(&psrv->node); \ } \ + state = newState; \ } -STARTSTOP(dbInitServers, init) -STARTSTOP(dbRunServers, run) -STARTSTOP(dbPauseServers, pause) -STARTSTOP(dbStopServers, stop) +STARTSTOP(dbInitServers, init, initialized) +STARTSTOP(dbRunServers, run, running) +STARTSTOP(dbPauseServers, pause, paused) +STARTSTOP(dbStopServers, stop, stopped) From fb5a2d84759d1cf3bc66e3476b2eae6bd1de5dce Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 18 Oct 2017 18:12:21 -0500 Subject: [PATCH 24/47] Added dbUnregisterServer() routine, plus tests --- src/ioc/db/dbServer.c | 15 +++++++++++++++ src/ioc/db/dbServer.h | 7 +++++++ src/ioc/db/test/dbServerTest.c | 12 +++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index a18477723..495156dcf 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -65,6 +65,21 @@ int dbRegisterServer(dbServer *psrv) return 0; } +int dbUnregisterServer(dbServer *psrv) +{ + if (state != registering && state != stopped) { + fprintf(stderr, "dbUnregisterServer: Servers still active!\n"); + return -1; + } + if (ellFind(&serverList, &psrv->node) < 0) { + fprintf(stderr, "dbUnregisterServer: '%s' not registered.\n", + psrv->name); + return -1; + } + ellDelete(&serverList, &psrv->node); + return 0; +} + void dbsr(unsigned level) { dbServer *psrv = (dbServer *)ellFirst(&serverList); diff --git a/src/ioc/db/dbServer.h b/src/ioc/db/dbServer.h index 1d184329c..ce5244e11 100644 --- a/src/ioc/db/dbServer.h +++ b/src/ioc/db/dbServer.h @@ -119,6 +119,13 @@ typedef struct dbServer { */ epicsShareFunc int dbRegisterServer(dbServer *psrv); +/** @brief Unregister a server layer + * + * This should only be called when the servers are inactive. + * @param psrv Server information structure for the server + */ +epicsShareFunc int dbUnregisterServer(dbServer *psrv); + /** @brief Print dbServer Reports. * * Calls the report methods of all registered servers. diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index e9c6556c5..5aba92770 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -110,6 +110,12 @@ dbServer illegal = { disInit, NULL, NULL, NULL }; +dbServer toolate = { + ELLNODE_INIT, "toolate", + NULL, NULL, NULL, + disInit, NULL, NULL, NULL +}; + MAIN(dbServerTest) { @@ -117,7 +123,7 @@ MAIN(dbServerTest) char *theName = "The One"; int status; - testPlan(14); + testPlan(18); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); @@ -139,15 +145,19 @@ MAIN(dbServerTest) dbInitServers(); testOk(oneState == INIT_CALLED, "dbInitServers"); testOk(disInitialized == 0, "Disabled server not initialized"); + testOk(dbRegisterServer(&toolate) != 0, "No registration while active"); dbRunServers(); testOk(oneState == RUN_CALLED, "dbRunServers"); + testOk(dbUnregisterServer(&one) != 0, "No unregistration while active"); dbPauseServers(); testOk(oneState == PAUSE_CALLED, "dbPauseServers"); dbStopServers(); testOk(oneState == STOP_CALLED, "dbStopServers"); + testOk(dbUnregisterServer(&toolate) != 0, "No unregistration if not reg'ed"); + testOk(dbUnregisterServer(&no_routines) == 0, "Unregistered 'no-routines'"); testDiag("Printing server report"); dbsr(0); From 2b15bc5f70afc46ae04a60988166bee31fe879ba Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 18 Oct 2017 19:12:50 -0500 Subject: [PATCH 25/47] Can't dbUnregisterServer() if stopped & no stop() method Unregistration is allowed if we're still registering though. This allows for cases like rsrv, which doesn't have a stop method. However the semantics of restarting servers after they have been stopped hasn't been defined, and rsrv probably wouldn't work if you tried that anyway, so I'm not convinced this is useful. --- src/ioc/db/dbServer.c | 6 ++++++ src/ioc/db/test/dbServerTest.c | 8 +++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index 495156dcf..09917da55 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -76,6 +76,12 @@ int dbUnregisterServer(dbServer *psrv) psrv->name); return -1; } + if (state == stopped && psrv->stop == NULL) { + fprintf(stderr, "dbUnregisterServer: '%s' has no stop() method.\n", + psrv->name); + return -1; + } + ellDelete(&serverList, &psrv->node); return 0; } diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index 5aba92770..325b77644 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -123,7 +123,7 @@ MAIN(dbServerTest) char *theName = "The One"; int status; - testPlan(18); + testPlan(20); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); @@ -137,6 +137,8 @@ MAIN(dbServerTest) testDiag("Registering dbServer 'no-routines'"); testOk(dbRegisterServer(&no_routines) == 0, "Registered 'no-routines'"); + testOk(dbUnregisterServer(&no_routines) == 0, "'no-routines' unreg'd"); + testOk(dbRegisterServer(&no_routines) == 0, "Re-registered 'no-routines'"); epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "disabled nonexistent"); testDiag("Registering dbServer 'disabled'"); @@ -156,8 +158,8 @@ MAIN(dbServerTest) dbStopServers(); testOk(oneState == STOP_CALLED, "dbStopServers"); - testOk(dbUnregisterServer(&toolate) != 0, "No unregistration if not reg'ed"); - testOk(dbUnregisterServer(&no_routines) == 0, "Unregistered 'no-routines'"); + testOk(dbUnregisterServer(&toolate) != 0, "No unreg' if not reg'ed"); + testOk(dbUnregisterServer(&no_routines) != 0, "No unreg' of 'no-routines'"); testDiag("Printing server report"); dbsr(0); From 11d2a64507537f8fd498393f1b0720a79c80df1e Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 18 Oct 2017 20:12:20 -0500 Subject: [PATCH 26/47] Clarify release entry wording --- documentation/RELEASE_NOTES.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 2735e40da..8c9810306 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -19,7 +19,7 @@

Extend the dbServer API with init/run/pause/stop methods

-

This change permits the IOC to be built that omits the CA server (RSRV) by +

This change permits IOCs to be built that omit the CA server (RSRV) by removing its registrar entry which is now provided in the new rsrv.dbd file. Other server layers can be built into the IOC (alongside RSRV or in place of it) by registering them in a similar manner. The dbServer API is documented From 62db42bf356b3186d8caedd6df0c26bbede59830 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 23 Oct 2017 11:27:51 -0500 Subject: [PATCH 27/47] Delete unused variable --- src/libCom/osi/os/Darwin/osdMonotonic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libCom/osi/os/Darwin/osdMonotonic.c b/src/libCom/osi/os/Darwin/osdMonotonic.c index 6f8cb85af..2f81d002b 100644 --- a/src/libCom/osi/os/Darwin/osdMonotonic.c +++ b/src/libCom/osi/os/Darwin/osdMonotonic.c @@ -29,6 +29,6 @@ epicsUInt64 epicsMonotonicResolution(void) epicsUInt64 epicsMonotonicGet(void) { - uint64_t val = mach_absolute_time(), ret; + uint64_t val = mach_absolute_time(); return val * tbinfo.numer / tbinfo.denom; } From a1d2f337f44e840493a5aa6e5bff74c2833529a7 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 23 Oct 2017 11:30:29 -0500 Subject: [PATCH 28/47] Initial VxWorks support code Implemented for both PowerPC and i86. Builds, but not tested yet. --- src/libCom/osi/os/vxWorks/osdMonotonic.c | 92 ++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/libCom/osi/os/vxWorks/osdMonotonic.c diff --git a/src/libCom/osi/os/vxWorks/osdMonotonic.c b/src/libCom/osi/os/vxWorks/osdMonotonic.c new file mode 100644 index 000000000..6a7abadbf --- /dev/null +++ b/src/libCom/osi/os/vxWorks/osdMonotonic.c @@ -0,0 +1,92 @@ +/*************************************************************************\ +* Copyright (c) 2017 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. +\*************************************************************************/ + +#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> + +#include + +#define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE +#include "epicsTypes.h" +#include "epicsTime.h" +#include "epicsFindSymbol.h" + + +#define NS_PER_SEC 1000000000 + +union timebase { + UINT32 u32[2]; /* vxTimeBaseGet() */ + INT64 i64; /* pentiumTscGet64() */ + UINT64 u64; /* epicsMonotonicGet() */ +}; + +static epicsUInt64 ticksPerSec; + + +#if CPU_FAMILY == PPC +#include + +#define TIMEBASEGET(TB) \ + vxTimeBaseGet(&TB.u32[0], &TB.u32[1]) + +typedef epicsUInt64 (*sysTimeBaseFreq_t)(void); + +void osdMonotonicInit(void) +{ + sysTimeBaseFreq_t sysTimeBaseFreq = + (sysTimeBaseFreq_t) epicsFindSymbol("_sysTimeBaseFreq"); + + if (sysTimeBaseFreq) { + ticksPerSec = sysTimeBaseFreq(); + return; + } + + printf("Warning: BSP routine sysTimeBaseFreq() not found,\n" + "can't set up monotonic time source.\n"); + ticksPerSec = 0; +} + + +#elif CPU_FAMILY == I80X86 + +#include +#include + +#define TIMEBASEGET(TB) \ + pentiumTscGet64(&TB.i64) + +void osdMonotonicInit(void) +{ + ticksPerSec = vxCpuIdGetFreq(); + + if (!ticksPerSec) + printf("Warning: Failed to set up monotonic time source.\n"); +} + + +#else + #error CPU Family not supported yet! +#endif + + +epicsUInt64 epicsMonotonicResolution(void) +{ + if (!ticksPerSec) + return 0; + + return NS_PER_SEC / ticksPerSec; +} + +epicsUInt64 epicsMonotonicGet(void) +{ + union timebase tbNow; + + if (!ticksPerSec) + return 0; + + TIMEBASEGET(tbNow); + return tbNow.u64 / (ticksPerSec / NS_PER_SEC); +} From 77de87cfb0e930c8e217a01964b04c9d5c4f0cc7 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 26 Oct 2017 13:02:20 -0500 Subject: [PATCH 29/47] dbServer: Only call client() and report() methods when running Also adjusts the tests to confirm that behaviour. --- src/ioc/db/dbServer.c | 7 +++++-- src/ioc/db/test/dbServerTest.c | 31 ++++++++++++++++++++----------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/ioc/db/dbServer.c b/src/ioc/db/dbServer.c index 09917da55..5374f02ad 100644 --- a/src/ioc/db/dbServer.c +++ b/src/ioc/db/dbServer.c @@ -98,8 +98,8 @@ void dbsr(unsigned level) printf("Server state: %s\n", stateNames[state]); while (psrv) { - printf("Server '%s':\n", psrv->name); - if (psrv->report) + printf("Server '%s'\n", psrv->name); + if (state == running && psrv->report) psrv->report(level); psrv = (dbServer *)ellNext(&psrv->node); } @@ -109,6 +109,9 @@ int dbServerClient(char *pBuf, size_t bufSize) { dbServer *psrv = (dbServer *)ellFirst(&serverList); + if (state != running) + return -1; + while (psrv) { if (psrv->client && psrv->client(pBuf, bufSize) == 0) diff --git a/src/ioc/db/test/dbServerTest.c b/src/ioc/db/test/dbServerTest.c index 325b77644..f0d5bf6ac 100644 --- a/src/ioc/db/test/dbServerTest.c +++ b/src/ioc/db/test/dbServerTest.c @@ -123,7 +123,7 @@ MAIN(dbServerTest) char *theName = "The One"; int status; - testPlan(20); + testPlan(24); /* Prove that we handle substring names properly */ epicsEnvSet("EPICS_IOC_IGNORE_SERVERS", "none ones"); @@ -144,6 +144,7 @@ MAIN(dbServerTest) testDiag("Registering dbServer 'disabled'"); testOk(dbRegisterServer(&disabled) == 0, "Registration accepted"); + testDiag("Changing server state"); dbInitServers(); testOk(oneState == INIT_CALLED, "dbInitServers"); testOk(disInitialized == 0, "Disabled server not initialized"); @@ -153,29 +154,37 @@ MAIN(dbServerTest) testOk(oneState == RUN_CALLED, "dbRunServers"); testOk(dbUnregisterServer(&one) != 0, "No unregistration while active"); - dbPauseServers(); - testOk(oneState == PAUSE_CALLED, "dbPauseServers"); - - dbStopServers(); - testOk(oneState == STOP_CALLED, "dbStopServers"); - testOk(dbUnregisterServer(&toolate) != 0, "No unreg' if not reg'ed"); - testOk(dbUnregisterServer(&no_routines) != 0, "No unreg' of 'no-routines'"); - - testDiag("Printing server report"); + testDiag("Checking server methods called"); dbsr(0); - testOk(oneState == REPORT_CALLED, "dbsr"); + testOk(oneState == REPORT_CALLED, "dbsr called report()"); oneSim = NULL; name[0] = 0; status = dbServerClient(name, sizeof(name)); + testOk(oneState == CLIENT_CALLED_UNKNOWN, "Client unknown"); testOk(status == -1 && name[0] == 0, "dbServerClient mismatch"); oneSim = theName; name[0] = 0; status = dbServerClient(name, sizeof(name)); + testOk(oneState == CLIENT_CALLED_KNOWN, "Client known"); testOk(status == 0 && strcmp(name, theName) == 0, "dbServerClient match"); + dbPauseServers(); + testOk(oneState == PAUSE_CALLED, "dbPauseServers"); + + dbsr(0); + testOk(oneState != REPORT_CALLED, "No call to report() when paused"); + + status = dbServerClient(name, sizeof(name)); + testOk(oneState != CLIENT_CALLED_KNOWN, "No call to client() when paused"); + + dbStopServers(); + testOk(oneState == STOP_CALLED, "dbStopServers"); + testOk(dbUnregisterServer(&toolate) != 0, "No unreg' if not reg'ed"); + testOk(dbUnregisterServer(&no_routines) != 0, "No unreg' of 'no-routines'"); + return testDone(); } From 49d638be979ea7fad86d6d68e98d692ad9e62e1e Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Oct 2017 15:17:38 -0500 Subject: [PATCH 30/47] VxWorks osdMonotonic implementation PowerPC time-base frequencies are 32-bit. Adjust when warnings are printed. Use long double to calculate time from time-base. --- src/libCom/osi/os/vxWorks/osdMonotonic.c | 47 +++++++++++++++++------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/libCom/osi/os/vxWorks/osdMonotonic.c b/src/libCom/osi/os/vxWorks/osdMonotonic.c index 6a7abadbf..e0c6b2278 100644 --- a/src/libCom/osi/os/vxWorks/osdMonotonic.c +++ b/src/libCom/osi/os/vxWorks/osdMonotonic.c @@ -8,11 +8,11 @@ #define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h> #include +#include #define EPICS_EXPOSE_LIBCOM_MONOTONIC_PRIVATE #include "epicsTypes.h" #include "epicsTime.h" -#include "epicsFindSymbol.h" #define NS_PER_SEC 1000000000 @@ -23,30 +23,35 @@ union timebase { UINT64 u64; /* epicsMonotonicGet() */ }; -static epicsUInt64 ticksPerSec; - #if CPU_FAMILY == PPC #include +#include "epicsFindSymbol.h" + +/* On PowerPC the timebase counter runs at a rate related to the + * bus clock and its frequency should always fit into a UINT32. + */ + +static epicsUInt32 ticksPerSec; #define TIMEBASEGET(TB) \ vxTimeBaseGet(&TB.u32[0], &TB.u32[1]) -typedef epicsUInt64 (*sysTimeBaseFreq_t)(void); - void osdMonotonicInit(void) { + typedef epicsUInt32 (*sysTimeBaseFreq_t)(void); sysTimeBaseFreq_t sysTimeBaseFreq = (sysTimeBaseFreq_t) epicsFindSymbol("_sysTimeBaseFreq"); if (sysTimeBaseFreq) { ticksPerSec = sysTimeBaseFreq(); - return; - } - printf("Warning: BSP routine sysTimeBaseFreq() not found,\n" - "can't set up monotonic time source.\n"); - ticksPerSec = 0; + if (!ticksPerSec) + printf("Warning: Failed to set up monotonic time source.\n"); + /* Warn here only if the BSP routine exists but returned 0 */ + } + else + ticksPerSec = 0; /* Warn on first use */ } @@ -55,6 +60,12 @@ void osdMonotonicInit(void) #include #include +/* On Intel the timebase counter frequency is returned by the OS as a + * UINT64. Some CPUs may count at multi-GHz rates so we need 64 bits. + */ + +static epicsUInt64 ticksPerSec; + #define TIMEBASEGET(TB) \ pentiumTscGet64(&TB.i64) @@ -68,7 +79,7 @@ void osdMonotonicInit(void) #else - #error CPU Family not supported yet! + #error This CPU family not supported yet! #endif @@ -84,9 +95,19 @@ epicsUInt64 epicsMonotonicGet(void) { union timebase tbNow; - if (!ticksPerSec) + if (!ticksPerSec) { + static int warned = 0; + + if (!warned) { + printf("Warning: Monotonic time source is not available.\n"); + warned = 1; + } return 0; + } TIMEBASEGET(tbNow); - return tbNow.u64 / (ticksPerSec / NS_PER_SEC); + /* Using a long double for the calculation below to preserve + * as many bits in the mantissa as possible. + */ + return ((long double) tbNow.u64) * NS_PER_SEC / ticksPerSec; } From 924e11a8bc56c51da5880a958afdcf865af03440 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 2 Nov 2017 17:08:42 -0500 Subject: [PATCH 31/47] rsrv: Fix include list for Windows builds For some reason epicsExport.h doesn't work here, we have to explicitly define epicsExportSharedSymbols and them pull in epicsExport.h separately later. Must be something to do with the #undef and redef that goes on in server.h --- src/ioc/rsrv/rsrvIocRegister.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ioc/rsrv/rsrvIocRegister.c b/src/ioc/rsrv/rsrvIocRegister.c index afc0ae85a..83c84c334 100644 --- a/src/ioc/rsrv/rsrvIocRegister.c +++ b/src/ioc/rsrv/rsrvIocRegister.c @@ -10,9 +10,10 @@ #include "osiSock.h" #include "iocsh.h" -#include "epicsExport.h" +#define epicsExportSharedSymbols #include "rsrv.h" #include "server.h" +#include "epicsExport.h" /* casr */ From 5e5e56b39a0996d27d7e256746871d9b9a5b7c09 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 7 Nov 2017 17:54:50 -0600 Subject: [PATCH 32/47] Restore dbVerify() to dbStaticLib --- documentation/RELEASE_NOTES.html | 7 ++ src/ioc/db/test/dbStaticTest.c | 69 +++++++++++- src/ioc/dbStatic/dbStaticLib.c | 178 +++++++++++++++++++++++++++++++ src/ioc/dbStatic/dbStaticLib.h | 2 + 4 files changed, 255 insertions(+), 1 deletion(-) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 1f8c12234..3256e72a3 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -17,6 +17,13 @@ --> +

dbVerify() has been restored to dbStaticLib

+ +

This routine was removed in Base-3.16.1 but has been reimplemented in this +release by special request. Note that the error message strings that it returns +when verification fails have changed, but are still designed for display to the +user.

+

Simulation mode improvements

Records that support simulation mode have two new fields, SSCN diff --git a/src/ioc/db/test/dbStaticTest.c b/src/ioc/db/test/dbStaticTest.c index 3b8467eec..b956ce449 100644 --- a/src/ioc/db/test/dbStaticTest.c +++ b/src/ioc/db/test/dbStaticTest.c @@ -118,11 +118,76 @@ static void testRec2Entry(const char *recname) dbFinishEntry(&entry2); } +static void verify(DBENTRY *pentry, const char *put, const char *exp) +{ + const char *msg; + int result; + + msg = dbVerify(pentry, put); + result = (!msg && !exp) || (msg && exp && strcmp(msg, exp) == 0); + + if (!testOk(result, "dbVerify('%s.%s', '%s') => '%s'", + (char *) pentry->precnode->precord, pentry->pflddes->name, + put, msg ? msg : "OK")) + testDiag("Expected => '%s'", exp ? exp : "OK"); +} + +static void testDbVerify(const char *record) +{ + DBENTRY entry; + + testDiag("# # # # # # # testDbVerify('%s') # # # # # # # #", record); + + dbInitEntry(pdbbase, &entry); + if (dbFindRecord(&entry, record) != 0) + testAbort("Can't find record '%s'", record); + + dbFindField(&entry, "UDF"); + verify(&entry, "0", NULL); + verify(&entry, "255", NULL); + verify(&entry, "256", "Number too large for field type"); + verify(&entry, "0x100", "Number too large for field type"); + + dbFindField(&entry, "PHAS"); + verify(&entry, "0", NULL); + verify(&entry, "-32768", NULL); + verify(&entry, "-32769", "Number too large for field type"); + verify(&entry, "0x7fff", NULL); + verify(&entry, "32768", "Number too large for field type"); + + dbFindField(&entry, "VAL"); + verify(&entry, "0", NULL); + verify(&entry, "-123456789", NULL); + verify(&entry, "123456789", NULL); + verify(&entry, "0x1234FEDC", NULL); + verify(&entry, "0x100000000", "Number too large for field type"); + verify(&entry, "1.2345", "Extraneous characters after number"); + + dbFindField(&entry, "DESC"); + verify(&entry, "", NULL); + verify(&entry, "abcdefghijklmnopqrstuvwxyz", NULL); + verify(&entry, "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", + "String too long, max 40 characters"); + + dbFindField(&entry, "DTYP"); + verify(&entry, "Soft Channel", NULL); + verify(&entry, "zzzz", "Not a valid device type"); + + dbFindField(&entry, "SCAN"); + verify(&entry, "1 second", NULL); + verify(&entry, "zzzz", "Not a valid menu choice"); + + dbFindField(&entry, "FLNK"); + verify(&entry, "Anything works here!", NULL); + + dbFinishEntry(&entry); +} + void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(dbStaticTest) { - testPlan(200); + testPlan(223); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); @@ -159,6 +224,8 @@ MAIN(dbStaticTest) testRec2Entry("testalias2"); testRec2Entry("testalias3"); + testDbVerify("testrec"); + testIocShutdownOk(); testdbCleanup(); diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index bcfa93727..e68c4531a 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -2651,6 +2651,184 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) return(status); } +char * dbVerify(DBENTRY *pdbentry, const char *pstring) +{ + dbFldDes *pflddes = pdbentry->pflddes; + char *message = getpMessage(pdbentry); + long status; + union { + epicsInt8 i8; + epicsUInt8 u8; + epicsInt16 i16; + epicsUInt16 u16; + epicsInt32 i32; + epicsUInt32 u32; + epicsInt64 i64; + epicsUInt64 u64; + epicsFloat32 f32; + epicsFloat64 f64; + } val; + + if (!pflddes) { + strcpy(message, "fldDes not found"); + return message; + } + + if (strstr(pstring,"$(") || strstr(pstring,"${")) + return NULL; + + switch (pflddes->field_type) { + case DBF_STRING: + { + size_t length = strlen(pstring); + + if (length >= pflddes->size) { + sprintf(message, "String too long, max %d characters", + pflddes->size - 1); + return message; + } + + if (pflddes->special == SPC_CALC) { + char rpcl[RPCL_LEN]; + short err; + + status = postfix(pstring, rpcl, &err); + if (status) { + sprintf(message,"%s in CALC expression '%s'", + calcErrorStr(err), pstring); + return message; + } + } + + return NULL; + } + + case DBF_CHAR: + status = epicsParseInt8(pstring, &val.i8, 0, NULL); + break; + + case DBF_UCHAR: + status = epicsParseUInt8(pstring, &val.u8, 0, NULL); + break; + + case DBF_SHORT: + status = epicsParseInt16(pstring, &val.i16, 0, NULL); + break; + + case DBF_ENUM: + case DBF_USHORT: + status = epicsParseUInt16(pstring, &val.u16, 0, NULL); + break; + + case DBF_LONG: + status = epicsParseInt32(pstring, &val.i32, 0, NULL); + break; + + case DBF_ULONG: + status = epicsParseUInt32(pstring, &val.u32, 0, NULL); + break; + + case DBF_INT64: + status = epicsParseInt64(pstring, &val.i64, 0, NULL); + break; + + case DBF_UINT64: + status = epicsParseUInt64(pstring, &val.u64, 0, NULL); + break; + + case DBF_FLOAT: + status = epicsParseFloat32(pstring, &val.f32, NULL); + break; + + case DBF_DOUBLE: + status = epicsParseFloat64(pstring, &val.f64, NULL); + break; + + case DBF_MENU: + { + dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; + int i; + + if (!pdbMenu) + return NULL; + + for (i = 0; i < pdbMenu->nChoice; i++) { + const char *pchoice = pdbMenu->papChoiceValue[i]; + + if (!pchoice) + continue; + + if (strcmp(pchoice, pstring) == 0) { + return NULL; + } + } + } + strcpy(message, "Not a valid menu choice"); + return message; + + case DBF_DEVICE: + { + dbDeviceMenu *pdbDeviceMenu = dbGetDeviceMenu(pdbentry); + int i; + + if (!pdbDeviceMenu || pdbDeviceMenu->nChoice == 0) + return NULL; + + for (i = 0; i < pdbDeviceMenu->nChoice; i++) { + const char *pchoice = pdbDeviceMenu->papChoice[i]; + + if (!pchoice) + continue; + + if (strcmp(pchoice, pstring) == 0) { + return NULL; + } + } + } + strcpy(message, "Not a valid device type"); + return message; + + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: + return NULL; + + default: + strcpy(message, "Not a valid field type"); + return message; + } + + switch (status) { + case 0: + message = NULL; + break; + + case S_stdlib_noConversion: + strcpy(message, "Not a valid integer"); + break; + + case S_stdlib_badBase: + strcpy(message, "Internal error (badBase)"); + break; + + case S_stdlib_overflow: + strcpy(message, "Number too large for field type"); + break; + + case S_stdlib_underflow: + strcpy(message, "Number too small for field type"); + break; + + case S_stdlib_extraneous: + strcpy(message, "Extraneous characters after number"); + break; + + default: + strcpy(message, "Unknown numeric conversion error"); + } + return message; +} + long dbFirstInfo(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; diff --git a/src/ioc/dbStatic/dbStaticLib.h b/src/ioc/dbStatic/dbStaticLib.h index b2f4a02ae..1ee999104 100644 --- a/src/ioc/dbStatic/dbStaticLib.h +++ b/src/ioc/dbStatic/dbStaticLib.h @@ -182,6 +182,8 @@ epicsShareFunc int dbFoundField(DBENTRY *pdbentry); epicsShareFunc char * dbGetString(DBENTRY *pdbentry); epicsShareFunc long dbPutString(DBENTRY *pdbentry, const char *pstring); +epicsShareFunc char * dbVerify(DBENTRY *pdbentry, + const char *pstring); epicsShareFunc int dbIsDefaultValue(DBENTRY *pdbentry); epicsShareFunc long dbFirstInfo(DBENTRY *pdbentry); From 98930eebc442757a81fda2bc92e94132f7dc87a6 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 3 Mar 2018 00:12:59 -0600 Subject: [PATCH 33/47] strcpy() -> strncpy() changes from Bruce Hill Also added some additional static assertions for string field sizes. --- src/std/rec/stringinRecord.c | 5 +++-- src/std/rec/stringoutRecord.c | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/std/rec/stringinRecord.c b/src/std/rec/stringinRecord.c index 19f55866a..db2f626f1 100644 --- a/src/std/rec/stringinRecord.c +++ b/src/std/rec/stringinRecord.c @@ -93,6 +93,7 @@ static long readValue(stringinRecord *); static long init_record(stringinRecord *prec, int pass) { STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val)); + STATIC_ASSERT(sizeof(prec->sval)==sizeof(prec->val)); struct stringindset *pdset; long status; @@ -119,7 +120,7 @@ static long init_record(stringinRecord *prec, int pass) if( pdset->init_record ) { if((status=(*pdset->init_record)(prec))) return(status); } - strcpy(prec->oval,prec->val); + strncpy(prec->oval, prec->val, sizeof(prec->val)); return(0); } @@ -193,7 +194,7 @@ static long readValue(stringinRecord *prec) status=dbGetLink(&(prec->siol),DBR_STRING, prec->sval,0,0); if (status==0) { - strcpy(prec->val,prec->sval); + strncpy(prec->val, prec->sval, sizeof(prec->val)); prec->udf=FALSE; } } else { diff --git a/src/std/rec/stringoutRecord.c b/src/std/rec/stringoutRecord.c index ea9012911..2bca3228c 100644 --- a/src/std/rec/stringoutRecord.c +++ b/src/std/rec/stringoutRecord.c @@ -95,6 +95,7 @@ static long writeValue(stringoutRecord *); static long init_record(stringoutRecord *prec, int pass) { STATIC_ASSERT(sizeof(prec->oval)==sizeof(prec->val)); + STATIC_ASSERT(sizeof(prec->ivov)==sizeof(prec->val)); struct stringoutdset *pdset; long status=0; @@ -121,7 +122,7 @@ static long init_record(stringoutRecord *prec, int pass) if( pdset->init_record ) { if((status=(*pdset->init_record)(prec))) return(status); } - strcpy(prec->oval,prec->val); + strncpy(prec->oval, prec->val, sizeof(prec->val)); return(0); } @@ -159,7 +160,7 @@ static long process(stringoutRecord *prec) break; case (menuIvoaSet_output_to_IVOV) : if(prec->pact == FALSE){ - strcpy(prec->val,prec->ivov); + strncpy(prec->val, prec->ivov, sizeof(prec->val)); } status=writeValue(prec); /* write the new value */ break; From 8eb4eec7d28eaa8950be95b05cfd551897b4b69a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 3 Mar 2018 17:17:34 -0600 Subject: [PATCH 34/47] Corrected fix from Bruce Hill's Github PR#19 --- configure/RULES_BUILD | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 6d2a5cd55..1b7e5dc25 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -170,8 +170,9 @@ ifdef RES @$(RM) *$(RES) endif -$(DIRECTORY_TARGETS) : - $(MKDIR) -p $@ +# Sort mkdir targets to remove duplicates & make parents first +$(DIRECTORY_TARGETS): + $(MKDIR) $(sort $@) # Install LIB_INSTALLS libraries before linking executables $(TESTPRODNAME) $(PRODNAME): | $(INSTALL_LIB_INSTALLS) From 98d9ea45451e43125d3b3eb3da31b48afa1b9cbc Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 4 Mar 2018 15:45:15 -0600 Subject: [PATCH 35/47] ca/client: Catch by reference, missing '&' --- src/ca/client/oldChannelNotify.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ca/client/oldChannelNotify.cpp b/src/ca/client/oldChannelNotify.cpp index 5775bcc6b..701f51fc1 100644 --- a/src/ca/client/oldChannelNotify.cpp +++ b/src/ca/client/oldChannelNotify.cpp @@ -390,7 +390,7 @@ int epicsShareAPI ca_array_get_callback ( chtype type, { caStatus = ECA_ALLOCMEM; } - catch ( cacChannel::msgBodyCacheTooSmall ) { + catch ( cacChannel::msgBodyCacheTooSmall & ) { caStatus = ECA_TOLARGE; } catch ( ... ) From a9764c8f62ca49150c6bc1e68777b011b50e5604 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 4 Mar 2018 15:51:39 -0600 Subject: [PATCH 36/47] tools/caput: Report errors from ca_array_put*() Fixes LP: #1747983 --- src/ca/client/tools/caput.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ca/client/tools/caput.c b/src/ca/client/tools/caput.c index 9c50cd9df..5e4d10e23 100644 --- a/src/ca/client/tools/caput.c +++ b/src/ca/client/tools/caput.c @@ -8,7 +8,7 @@ * Copyright (c) 2002 Berliner Elektronenspeicherringgesellschaft fuer * Synchrotronstrahlung. * 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. \*************************************************************************/ /* @@ -419,7 +419,7 @@ int main (int argc, char *argv[]) if (argc > optind+1) { for (i = optind + 1; i < argc; i++) { strcat(cbuf, " "); - strcat(cbuf, argv[i]); + strcat(cbuf, argv[i]); } } @@ -530,6 +530,11 @@ int main (int argc, char *argv[]) /* Use standard put with defined timeout */ result = ca_array_put (dbrType, count, pvs[0].chid, pbuf); } + if (result != ECA_NORMAL) { + fprintf(stderr, "Error from put operation: %s\n", ca_message(result)); + return 1; + } + result = ca_pend_io(caTimeout); if (result == ECA_TIMEOUT) { fprintf(stderr, "Write operation timed out: Data was not written.\n"); @@ -545,7 +550,7 @@ int main (int argc, char *argv[]) } if (result != ECA_NORMAL) { - fprintf(stderr, "Error occured writing data.\n"); + fprintf(stderr, "Error occured writing data: %s\n", ca_message(result)); return 1; } From 9020c2ce1a5da9bead4146335fabaf11b62eeb5e Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 6 Mar 2018 14:45:06 -0600 Subject: [PATCH 37/47] testdbConvert: Add some minor overflow checks --- src/ioc/db/test/testdbConvert.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ioc/db/test/testdbConvert.c b/src/ioc/db/test/testdbConvert.c index cd0ed0ed2..cc175c0a0 100644 --- a/src/ioc/db/test/testdbConvert.c +++ b/src/ioc/db/test/testdbConvert.c @@ -38,7 +38,7 @@ static void testBasicGet(void) getter(&addr, scratch, 1, s_input_len, 0); - testOk1(scratch[0]==s_input[0]); + testOk1(scratch[0]==s_input[0] && scratch[1]==0); memset(scratch, 0x42, sizeof(s_input)); } @@ -128,7 +128,7 @@ static void testBasicPut(void) putter(&addr, s_input, 1, s_input_len, 0); - testOk1(scratch[0]==s_input[0]); + testOk1(scratch[0]==s_input[0] && scratch[1]==0); memset(scratch, 0x42, sizeof(s_input)); } From 958c81db89c1ad9726570f375ce66faf425e5398 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 6 Mar 2018 17:25:42 -0600 Subject: [PATCH 38/47] Protect dbGetString() if field has overflowed Only return the number of characters that a string field can actually hold, in case it had a buffer overflow. --- src/ioc/dbStatic/dbStaticLib.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 6e9997ad1..963b452f1 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -51,8 +51,11 @@ static char *pNullString = ""; #define messagesize 276 #define RPCL_LEN INFIX_TO_POSTFIX_SIZE(80) -/* must be long enough to hold 32-bit signed integer in base 10 */ -STATIC_ASSERT(messagesize>=11); +/* Must be big enough to hold a 64-bit integer in base 10, but in + * the future when fields hold large JSON objects this fixed size + * allocation will probably have to become variable sized. + */ +STATIC_ASSERT(messagesize >= 21); static char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"}; static char *msstring[4]={" NMS"," MS"," MSI"," MSS"}; @@ -208,11 +211,13 @@ static void zeroDbentry(DBENTRY *pdbentry) static char *getpMessage(DBENTRY *pdbentry) { char *msg = pdbentry->message; + if (!msg) { msg = dbCalloc(1, messagesize); pdbentry->message = msg; } - *msg = '\0'; + else + *msg = '\0'; return msg; } @@ -224,6 +229,17 @@ void dbMsgCpy(DBENTRY *pdbentry, const char *msg) pdbentry->message[messagesize-1] = '\0'; } +static +void dbMsgNCpy(DBENTRY *pdbentry, const char *msg, size_t len) +{ + getpMessage(pdbentry); + if (len >= messagesize) + len = messagesize-1; /* FIXME: Quietly truncates */ + + strncpy(pdbentry->message, msg, len); + pdbentry->message[len] = '\0'; +} + static void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) { @@ -1888,7 +1904,8 @@ char * dbGetString(DBENTRY *pdbentry) switch (pflddes->field_type) { case DBF_STRING: - dbMsgCpy(pdbentry, (char *)pfield); + /* Protect against a missing nil-terminator */ + dbMsgNCpy(pdbentry, (char *)pfield, pflddes->size); break; case DBF_CHAR: case DBF_UCHAR: From c05101bb3f8d9bf2ed984e85fde8bcb27402054a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Tue, 6 Mar 2018 18:23:34 -0600 Subject: [PATCH 39/47] Make dbGetString() fetch link fields properly before iocInit --- src/ioc/dbStatic/dbStaticLib.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 963b452f1..69a4a6d1b 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -1928,6 +1928,8 @@ char * dbGetString(DBENTRY *pdbentry) case CONSTANT: if (plink->value.constantStr) { dbMsgCpy(pdbentry, plink->value.constantStr); + } else if (plink->text) { + dbMsgCpy(pdbentry, plink->text); } else { dbMsgCpy(pdbentry, ""); } @@ -2028,7 +2030,13 @@ char * dbGetString(DBENTRY *pdbentry) switch(plink->type) { case CONSTANT: - dbMsgCpy(pdbentry, "0"); + if (plink->value.constantStr) { + dbMsgCpy(pdbentry, plink->value.constantStr); + } else if (plink->text) { + dbMsgCpy(pdbentry, plink->text); + } else { + dbMsgCpy(pdbentry, ""); + } break; case MACRO_LINK: if (plink->value.macro_link.macroStr) { From d7e416e76a1c0d404c4dbd32b8333d3f6acfd627 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 7 Mar 2018 00:02:45 -0600 Subject: [PATCH 40/47] Now dbGetString() works before iocInit, fix asTest to match Previously dbGetString() of a link field would return an empty string until iocInit was run. I fixed that earlier today, so asTest started failing because it was checking for the old behavior. --- src/std/rec/test/asTestLib.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/std/rec/test/asTestLib.c b/src/std/rec/test/asTestLib.c index 18139233f..2043ed770 100644 --- a/src/std/rec/test/asTestLib.c +++ b/src/std/rec/test/asTestLib.c @@ -109,11 +109,7 @@ static void hookPass0(initHookState state) else testFail("Wrong link type: %d", (int)prec->out.type); - /* note that dbGetString() reads an empty string before links are initialized - * should probably be considered a bug, but has been the case for so long - * we call it a 'feature'. - */ - checkGetString(&entry, ""); + checkGetString(&entry, "rec0.DISV"); testOk1(dbPutString(&entry, "rec0.SEVR")==0); } else{ From 6cc623a7b40e255fa4b08918aec4d0a6a5fd4ca2 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Fri, 10 Nov 2017 17:36:59 -0800 Subject: [PATCH 41/47] Fixed camonitor server side relative timestamps bug tsFirst needs to get initialized from the first server side timestamp instead of the client side tsNow. --- src/catools/tool_lib.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/catools/tool_lib.c b/src/catools/tool_lib.c index a29d7efaf..ce7a049a1 100644 --- a/src/catools/tool_lib.c +++ b/src/catools/tool_lib.c @@ -416,6 +416,12 @@ char *dbr2str (const void *value, unsigned type) ptsNewS = &((struct TYPE *)value)->stamp; \ ptsNewC = &tsNow; \ \ + if (!tsInitS) \ + { \ + tsFirst = *ptsNewS; \ + tsInitS = 1; \ + } \ + \ switch (tsType) { \ case relative: \ ptsRefC = &tsStart; \ @@ -508,12 +514,6 @@ void print_time_val_sts (pv* pv, unsigned long reqElems) epicsTimeGetCurrent(&tsNow); epicsTimeToStrftime(timeText, TIMETEXTLEN, timeFormatStr, &tsNow); - if (!tsInitS) - { - tsFirst = tsNow; - tsInitS = 1; - } - if (pv->nElems <= 1 && fieldSeparator == ' ') printf("%-30s", pv->name); else printf("%s", pv->name); printf("%c", fieldSeparator); From be8f35d78278ce51efa65b139ea7ff414bb1cbf8 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 9 Mar 2018 12:46:46 -0600 Subject: [PATCH 42/47] Fix for lp: #1754298 Cleaned up and reformatted since Dirk's original. --- src/ioc/dbStatic/dbStaticLib.c | 38 ++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 96868eded..a4ac17e20 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -1635,19 +1635,22 @@ int dbIsVisibleRecord(DBENTRY *pdbentry) long dbCreateAlias(DBENTRY *pdbentry, const char *alias) { - dbRecordType *precordType = pdbentry->precordType; - dbRecordNode *precnode = pdbentry->precnode; - dbRecordNode *pnewnode; - PVDENTRY *ppvd; - ELLLIST *preclist = NULL; + dbRecordType *precordType = pdbentry->precordType; + dbRecordNode *precnode = pdbentry->precnode; + dbRecordNode *pnewnode; + DBENTRY tempEntry; + PVDENTRY *ppvd; + + if (!precordType) + return S_dbLib_recordTypeNotFound; + if (!precnode) + return S_dbLib_recNotFound; + + dbInitEntry(pdbentry->pdbbase, &tempEntry); + if (!dbFindRecord(&tempEntry, alias)) + return S_dbLib_recExists; + dbFinishEntry(&tempEntry); - if (!precordType) return S_dbLib_recordTypeNotFound; - if (!precnode) return S_dbLib_recNotFound; - zeroDbentry(pdbentry); - if (!dbFindRecord(pdbentry, alias)) return S_dbLib_recExists; - zeroDbentry(pdbentry); - pdbentry->precordType = precordType; - preclist = &precordType->recList; pnewnode = dbCalloc(1, sizeof(dbRecordNode)); pnewnode->recordname = epicsStrDup(alias); pnewnode->precord = precnode->precord; @@ -1655,11 +1658,16 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias) if (!(precnode->flags & DBRN_FLAGS_ISALIAS)) precnode->flags |= DBRN_FLAGS_HASALIAS; ellInit(&pnewnode->infoList); - ellAdd(preclist, &pnewnode->node); + + ellAdd(&precordType->recList, &pnewnode->node); precordType->no_aliases++; - pdbentry->precnode = pnewnode; + ppvd = dbPvdAdd(pdbentry->pdbbase, precordType, pnewnode); - if (!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);} + if (!ppvd) { + errMessage(-1, "dbCreateAlias: Add to PVD failed"); + return -1; + } + return 0; } From 2307e94d1c5eefa40de318c40bad4365ef2110cf Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 12 Mar 2018 16:34:54 -0500 Subject: [PATCH 43/47] Rules for building bootable TESTPROD_RTEMS targets Previously if you wanted a .boot file it had to be installed. --- configure/RULES_BUILD | 11 ++++++++--- configure/os/CONFIG.Common.RTEMS | 7 +++++++ configure/os/CONFIG.Common.RTEMS-beatnik | 1 - configure/os/CONFIG.Common.RTEMS-mvme2100 | 1 - configure/os/CONFIG.Common.RTEMS-mvme2700 | 1 - configure/os/CONFIG.Common.RTEMS-mvme3100 | 1 - configure/os/CONFIG.Common.RTEMS-mvme5500 | 1 - configure/os/CONFIG.Common.RTEMS-pc386 | 1 - configure/os/CONFIG.Common.RTEMS-uC5282 | 1 - 9 files changed, 15 insertions(+), 10 deletions(-) diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 1b7e5dc25..cec33eca7 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -102,6 +102,7 @@ endif # Products and Object libraries # PRODTARGETS += $(PRODNAME) $(MUNCHNAME) $(CTDT_SRCS) $(CTDT_OBJS) $(NMS) +TESTPRODTARGETS += $(TESTPRODNAME) $(TESTMUNCHNAME) #--------------------------------------------------------------- # Test specifications and test result files @@ -140,7 +141,7 @@ rebuild: clean install build: inc -build: $(OBJSNAME) $(LIBTARGETS) $(PRODTARGETS) $(TESTPRODNAME) \ +build: $(OBJSNAME) $(LIBTARGETS) $(PRODTARGETS) $(TESTPRODTARGETS) \ $(TARGETS) $(TESTSCRIPTS) $(INSTALL_LIB_INSTALLS) inc : $(COMMON_INC) $(INSTALL_INC) $(INSTALL_CONFIGS) @@ -158,13 +159,13 @@ clean: build_clean build_clean: $(ECHO) "Cleaning" - @$(RM) *.i *$(OBJ) *.a $(TESTPRODNAME) \ + @$(RM) *.i *$(OBJ) *.a \ $(LIBNAME) $(TESTLIBNAME) $(SHRLIBNAME) $(TESTSHRLIBNAME) \ $(DLLSTUB_LIBNAME) $(TESTDLLSTUB_LIBNAME) \ $(LOADABLE_SHRLIBNAME) \ $(INC) $(TARGETS) $(TDS) $(CLEANS) \ *.out MakefileInclude *.manifest *.exp \ - $(COMMON_INC) $(HDEPENDS_FILES) $(PRODTARGETS) \ + $(COMMON_INC) $(HDEPENDS_FILES) $(PRODTARGETS) $(TESTPRODTARGETS) \ $(TESTSCRIPTS) $(TAPFILES) $(JUNITFILES) ifdef RES @$(RM) *$(RES) @@ -322,6 +323,10 @@ $(MUNCHNAME): %$(MUNCH_SUFFIX): $(MUNCH_DEPENDS) %$(EXE) @$(RM) $@ $(MUNCH_CMD) +$(TESTMUNCHNAME): %$(MUNCH_SUFFIX): $(MUNCH_DEPENDS) %$(EXE) + @$(RM) $@ + $(MUNCH_CMD) + #--------------------------------------------------------------- # GeSys modules for RTEMS $(MODNAME): %$(MODEXT): %$(EXE) diff --git a/configure/os/CONFIG.Common.RTEMS b/configure/os/CONFIG.Common.RTEMS index 9650ae255..c22e37a66 100644 --- a/configure/os/CONFIG.Common.RTEMS +++ b/configure/os/CONFIG.Common.RTEMS @@ -136,6 +136,13 @@ MOD_LDFLAGS = $(OPT_LDFLAGS) $(TARGET_LDFLAGS) $(USR_LDFLAGS) $(POSIX_LDFLAGS) \ LINK.mod = $(CCC) -o $@ $(PRODDIR_LDFLAGS) $(MOD_LDFLAGS) LINK.mod += $(PROD_LDFLAGS) $(PROD_LD_OBJS) $(PROD_LD_RESS) $(MOD_LDLIBS) +#-------------------------------------------------- +# Here munching means creating a bootable object binary +ifdef MUNCH_SUFFIX + MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) + TESTMUNCHNAME = $(TESTPRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) +endif + #-------------------------------------------------- # RTEMS has neither shared libraries nor dynamic loading STATIC_BUILD=YES diff --git a/configure/os/CONFIG.Common.RTEMS-beatnik b/configure/os/CONFIG.Common.RTEMS-beatnik index 00080e7fd..aaf611638 100644 --- a/configure/os/CONFIG.Common.RTEMS-beatnik +++ b/configure/os/CONFIG.Common.RTEMS-beatnik @@ -15,7 +15,6 @@ ARCH_DEP_CFLAGS += -DRTEMS_NETWORK_CONFIG_CLUSTER_SPACE=5120 OP_SYS_LDLIBS += -lbspExt MUNCH_SUFFIX = .boot -MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $< $@ endef diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2100 b/configure/os/CONFIG.Common.RTEMS-mvme2100 index 687af2374..0ae64c791 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme2100 +++ b/configure/os/CONFIG.Common.RTEMS-mvme2100 @@ -13,7 +13,6 @@ ARCH_DEP_CFLAGS += -DHAVE_PPCBUG OP_SYS_LDLIBS += -lbspExt MUNCH_SUFFIX = .boot -MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< rtems gzip -f9 rtems diff --git a/configure/os/CONFIG.Common.RTEMS-mvme2700 b/configure/os/CONFIG.Common.RTEMS-mvme2700 index f45e321c4..899fab17f 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme2700 +++ b/configure/os/CONFIG.Common.RTEMS-mvme2700 @@ -7,7 +7,6 @@ ARCH_DEP_CFLAGS += -DHAVE_PPCBUG ARCH_DEP_CFLAGS += -DNVRAM_INDIRECT MUNCH_SUFFIX = .boot -MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< rtems gzip -f9 rtems diff --git a/configure/os/CONFIG.Common.RTEMS-mvme3100 b/configure/os/CONFIG.Common.RTEMS-mvme3100 index cd9416ce7..e94d46211 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme3100 +++ b/configure/os/CONFIG.Common.RTEMS-mvme3100 @@ -15,7 +15,6 @@ ARCH_DEP_CFLAGS += -DRTEMS_NETWORK_CONFIG_CLUSTER_SPACE=5120 OP_SYS_LDLIBS += -lbspExt MUNCH_SUFFIX = .boot -MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $< $@ endef diff --git a/configure/os/CONFIG.Common.RTEMS-mvme5500 b/configure/os/CONFIG.Common.RTEMS-mvme5500 index 0c05b76a8..44ef7ea3e 100644 --- a/configure/os/CONFIG.Common.RTEMS-mvme5500 +++ b/configure/os/CONFIG.Common.RTEMS-mvme5500 @@ -16,7 +16,6 @@ ARCH_DEP_CFLAGS += -DBSP_NVRAM_BASE_ADDR=0xf1110000 OP_SYS_LDLIBS += -lbspExt MUNCH_SUFFIX = .boot -MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary $< $@ endef diff --git a/configure/os/CONFIG.Common.RTEMS-pc386 b/configure/os/CONFIG.Common.RTEMS-pc386 index b3150cc66..92ef4ac22 100644 --- a/configure/os/CONFIG.Common.RTEMS-pc386 +++ b/configure/os/CONFIG.Common.RTEMS-pc386 @@ -8,7 +8,6 @@ RTEMS_TARGET_CPU=i386 MUNCH_SUFFIX = .boot -MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< temp.bin $(BIN2BOOT) $@ 0x00097E00 \ diff --git a/configure/os/CONFIG.Common.RTEMS-uC5282 b/configure/os/CONFIG.Common.RTEMS-uC5282 index 9c6a58d33..6b0903e07 100644 --- a/configure/os/CONFIG.Common.RTEMS-uC5282 +++ b/configure/os/CONFIG.Common.RTEMS-uC5282 @@ -9,7 +9,6 @@ RTEMS_TARGET_CPU = m68k ARCH_DEP_CFLAGS += -DMY_DO_BOOTP=NULL MUNCH_SUFFIX = .boot -MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX)) define MUNCH_CMD $(RTEMS_TOOLS)/bin/$(OBJCOPY_FOR_TARGET) -O binary -R .comment -S $< $@ endef From 12bb8969adac77c1d9b899d43b4f940e2ac53b6f Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Mon, 27 Nov 2017 18:43:41 -0800 Subject: [PATCH 44/47] Rework module dependency conflict error message for easier debugging. Now a simply grep for asyn in the build log will show you line by line which modules depend on which asyn versions. --- src/tools/convertRelease.pl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/convertRelease.pl b/src/tools/convertRelease.pl index 5900612f3..eab9395b3 100644 --- a/src/tools/convertRelease.pl +++ b/src/tools/convertRelease.pl @@ -231,10 +231,10 @@ sub checkRelease { AbsPath($macros{$parent}) ne AbsPath($ppath)) { print "\n" unless ($status); print "Definition of $parent conflicts with $app support.\n"; - print "In this application a RELEASE file defines\n"; - print "\t$parent = $macros{$parent}\n"; - print "but $app at $path defines\n"; - print "\t$parent = $ppath\n"; + print "In this application or module, a RELEASE file\n"; + print "conflicts with $app at $path\n"; + print " This application: $parent = $macros{$parent}\n"; + print " $app: $parent = $ppath\n"; $status = 1; } } From ceaff61c09cc45af511884a3e99804955682c692 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 14 Mar 2018 14:12:12 -0500 Subject: [PATCH 45/47] Pull in the podToHtml.pl script and rules from 3.15 This lets src/cap5 build with Perl installations that lack Perl's podchecker and pod2html scripts (e.g. Fedora 27). --- configure/RULES.Db | 14 +++++++++++++ configure/RULES_BUILD | 4 ++++ src/cap5/Makefile | 4 ---- src/tools/Makefile | 1 + src/tools/podToHtml.pl | 45 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 src/tools/podToHtml.pl diff --git a/configure/RULES.Db b/configure/RULES.Db index fcb7737f4..615fb2b5a 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -301,6 +301,20 @@ $(foreach file, $(DBD_INSTALLS), $(eval $(call DBD_INSTALLS_template, $(file)))) .PRECIOUS: $(COMMON_DBDS) $(COMMON_DIR)/%Include.dbd +##################################################### HTML files + +$(COMMON_DIR)/%.html: %.pm $(TOOLS)/podToHtml.pl + @$(RM) $(notdir $@) + $(PERL) $(TOOLS)/podToHtml.pl -o $(notdir $@) $< + @$(MV) $(notdir $@) $@ + +$(COMMON_DIR)/%.html: ../%.pm $(TOOLS)/podToHtml.pl + @$(RM) $(notdir $@) + $(PERL) $(TOOLS)/podToHtml.pl -o $(notdir $@) $< + @$(MV) $(notdir $@) $@ + +.PRECIOUS: $(COMMON_DIR)/%.html %.html + ##################################################### DB files $(COMMON_DIR)/%.db$(RAW): $(COMMON_DIR)/%.edf diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index 2c8de45c5..fb04b9e4c 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -500,6 +500,10 @@ $(INSTALL_HTML)/$(HTMLS_DIR)/%: ../% $(ECHO) "Installing html $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) +$(INSTALL_HTML)/$(HTMLS_DIR)/%: $(COMMON_DIR)/% + $(ECHO) "Installing generated html $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + $(INSTALL_TEMPLATES_SUBDIR)/%: ../% $(ECHO) "Installing $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) diff --git a/src/cap5/Makefile b/src/cap5/Makefile index f749864c5..cde47ce8e 100644 --- a/src/cap5/Makefile +++ b/src/cap5/Makefile @@ -66,10 +66,6 @@ ifdef T_A $(RM) $@ $@_new $(PERL) $(XSUBPP) -typemap $(EXTUTILS)/typemap $< > $@_new && $(MV) $@_new $@ - %.html: ../%.pm - $(RM) $@ - podchecker $< && pod2html --infile=$< --outfile=$@ - $(INSTALL_PERL_MODULES)/$(PERL_ARCHPATH)/%: % $(ECHO) "Installing loadable shared library $@" @$(INSTALL_LIBRARY) -d -m $(LIB_PERMISSIONS) $< $(INSTALL_PERL_MODULES)/$(PERL_ARCHPATH) diff --git a/src/tools/Makefile b/src/tools/Makefile index 672944958..bcf12700d 100644 --- a/src/tools/Makefile +++ b/src/tools/Makefile @@ -28,6 +28,7 @@ PERL_SCRIPTS += makeMakefile.pl PERL_SCRIPTS += makeTestfile.pl PERL_SCRIPTS += mkmf.pl PERL_SCRIPTS += munch.pl +PERL_SCRIPTS += podToHtml.pl PERL_SCRIPTS += replaceVAR.pl PERL_SCRIPTS += tap-to-junit-xml.pl PERL_SCRIPTS += useManifestTool.pl diff --git a/src/tools/podToHtml.pl b/src/tools/podToHtml.pl new file mode 100644 index 000000000..99ee5b426 --- /dev/null +++ b/src/tools/podToHtml.pl @@ -0,0 +1,45 @@ +#!/usr/bin/env perl +#************************************************************************* +# Copyright (c) 2013 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. +#************************************************************************* + +use strict; +use warnings; + +use Getopt::Std; +use Pod::Simple::HTML; + +our ($opt_o); + +$Getopt::Std::OUTPUT_HELP_VERSION = 1; +&HELP_MESSAGE if !getopts('o:') || @ARGV != 1; + +my $infile = shift @ARGV; + +if (!$opt_o) { + ($opt_o = $infile) =~ s/\. \w+ $/.html/x; + $opt_o =~ s/^.*\///; +} + +open my $out, '>', $opt_o or + die "Can't create $opt_o: $!\n"; + +my $podHtml = Pod::Simple::HTML->new(); + +$podHtml->html_css('style.css'); +$podHtml->perldoc_url_prefix(''); +$podHtml->perldoc_url_postfix('.html'); +$podHtml->set_source($infile); +$podHtml->output_string(\my $html); +$podHtml->run; + +print $out $html; +close $out; + +sub HELP_MESSAGE { + print STDERR "Usage: podToHtml.pl [-o file.html] file.pod\n"; + exit 2; +} From c8a7e1597ddf19301260bc8f2d42427fed0067cf Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 15 Mar 2018 10:57:55 -0500 Subject: [PATCH 46/47] VxWorks Timezone updates Remove 2017; fix hour in MET settings --- configure/CONFIG_SITE_ENV | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/configure/CONFIG_SITE_ENV b/configure/CONFIG_SITE_ENV index 1decb05c8..49331f9a4 100644 --- a/configure/CONFIG_SITE_ENV +++ b/configure/CONFIG_SITE_ENV @@ -34,35 +34,30 @@ # The future dates below assume the rules don't get changed; # see http://www.timeanddate.com/time/dst/2018.html to check. # -# DST for 2017 US: Mar 12 - Nov 05 -# EU: Mar 26 - Oct 29 -EPICS_TIMEZONE = CUS::360:031202:110502 -#EPICS_TIMEZONE = MET::-60:032602:102902 -# # DST for 2018 US: Mar 11 - Nov 04 # EU: Mar 25 - Oct 28 -#EPICS_TIMEZONE = CUS::360:031102:110402 -#EPICS_TIMEZONE = MET::-60:032502:102802 +EPICS_TIMEZONE = CUS::360:031102:110402 +#EPICS_TIMEZONE = MET::-60:032502:102803 # # DST for 2019 US: Mar 10 - Nov 03 # EU: Mar 31 - Oct 27 #EPICS_TIMEZONE = CUS::360:031002:110302 -#EPICS_TIMEZONE = MET::-60:033102:102702 +#EPICS_TIMEZONE = MET::-60:033102:102703 # # DST for 2020 US: Mar 08 - Nov 01 # EU: Mar 29 - Oct 25 #EPICS_TIMEZONE = CUS::360:030802:110102 -#EPICS_TIMEZONE = MET::-60:032902:102502 +#EPICS_TIMEZONE = MET::-60:032902:102503 # # DST for 2021 US: Mar 14 - Nov 07 # EU: Mar 28 - Oct 31 #EPICS_TIMEZONE = CUS::360:031402:110702 -#EPICS_TIMEZONE = MET::-60:032802:103102 +#EPICS_TIMEZONE = MET::-60:032802:103103 # # DST for 2022 US: Mar 13 - Nov 06 # EU: Mar 27 - Oct 30 #EPICS_TIMEZONE = CUS::360:031302:110602 -#EPICS_TIMEZONE = MET::-60:032702:103002 +#EPICS_TIMEZONE = MET::-60:032702:103003 # EPICS_TS_NTP_INET # NTP time server ip address for VxWorks and RTEMS. From 58dc1ced9b05bc19866a3ca49e0f8b4bd109faf0 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 15 Mar 2018 17:52:52 -0500 Subject: [PATCH 47/47] Fix softTest synchronization bug Local CA output links do *not* trigger processing in the same thread; need to wait for the dest record in Group 3, but not in Group 4. --- src/std/rec/test/softTest.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/std/rec/test/softTest.c b/src/std/rec/test/softTest.c index b29c955f9..e9cb319a0 100644 --- a/src/std/rec/test/softTest.c +++ b/src/std/rec/test/softTest.c @@ -12,6 +12,7 @@ #include "dbTest.h" #include "dbUnitTest.h" #include "epicsThread.h" +#include "epicsEvent.h" #include "errlog.h" #include "registryFunction.h" #include "subRecord.h" @@ -134,24 +135,25 @@ void testGroup2(void) int dest; +epicsEventId destEvent; static long destSubr(subRecord *prec) { dest = prec->val; prec->val = -1; + epicsEventMustTrigger(destEvent); return 0; } static -void checkOutput(const char *rec, int value) +void checkOutput3(const char *rec, int value) { testDiag("Checking record '%s'", rec); testdbPutFieldOk(rec, DBR_LONG, value); - /* Even with a local CA link, the dest record gets processed in - * the context of this thread (i.e. immediately). TPRO confirms. - */ + + epicsEventMustWait(destEvent); testOk(dest == value, "value %d output -> %d", value, dest); } @@ -170,22 +172,33 @@ void testGroup3(void) NULL, }; + destEvent = epicsEventMustCreate(epicsEventEmpty); + testDiag("============ Starting %s ============", EPICS_FUNCTION); for (rec = records; *rec; rec++) { - checkOutput(*rec, 1); + checkOutput3(*rec, 1); checkDtyp(*rec); } - checkOutput("do3.B0", 1); + checkOutput3("do3.B0", 1); checkDtyp("do3"); - checkOutput("do3c.B0", 1); + checkOutput3("do3c.B0", 1); checkDtyp("do3c"); for (rec = records; *rec; rec++) { - checkOutput(*rec, 0); + checkOutput3(*rec, 0); } - checkOutput("do3.B0", 0); - checkOutput("do3c.B0", 0); + checkOutput3("do3.B0", 0); + checkOutput3("do3c.B0", 0); +} + + +static +void checkOutput4(const char *rec, int value) +{ + testDiag("Checking record '%s'", rec); + + testdbPutFieldOk(rec, DBR_LONG, value); } /* Group 4 are all soft-channel output records with OUT being empty @@ -202,7 +215,7 @@ void testGroup4(void) testDiag("============ Starting %s ============", EPICS_FUNCTION); for (rec = records; *rec; rec++) { - checkOutput(*rec, 0); + checkOutput4(*rec, 0); } } @@ -211,7 +224,7 @@ void recTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(softTest) { - testPlan(266); + testPlan(258); testdbPrepare(); testdbReadDatabase("recTestIoc.dbd", NULL, NULL);