diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index c3638c711..2be4b338a 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -105,13 +105,17 @@ endif PRODTARGETS += $(PRODNAME) $(MUNCHNAME) $(CTDT_SRCS) $(CTDT_OBJS) $(NMS) #--------------------------------------------------------------- -# Generate a test specification if any tests are defined. +# Test specifications and test result files # ifneq (,$(strip $(TESTS))) TARGETS += testspec endif +# Enable testing if this host can run tests on the current target +ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS))) +RUNTESTS_ENABLED = YES TAPFILES += $(TESTSCRIPTS:.t=.tap) +endif #--------------------------------------------------------------- # Libraries @@ -324,7 +328,7 @@ $(MODNAME): %$(MODEXT): %$(EXE) # Automated testing runtests: $(TESTSCRIPTS) -ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS))) +ifdef RUNTESTS_ENABLED -$(PERL) -MTest::Harness -e 'runtests @ARGV if @ARGV;' $^ endif @@ -340,7 +344,7 @@ tapfiles: $(TESTSCRIPTS) $(TAPFILES) # A .tap file is the output from running the associated test script %.tap: %.t -ifneq (,$(findstring $(T_A),$(EPICS_HOST_ARCH) $(CROSS_COMPILER_RUNTEST_ARCHS))) +ifdef RUNTESTS_ENABLED -$(PERL) $< -tap > $@ endif diff --git a/configure/os/CONFIG.win32-x86.win32-x86 b/configure/os/CONFIG.win32-x86.win32-x86 index f9bb0b9cb..6c6f21a61 100644 --- a/configure/os/CONFIG.win32-x86.win32-x86 +++ b/configure/os/CONFIG.win32-x86.win32-x86 @@ -10,6 +10,8 @@ VALID_BUILDS = Host Ioc CMPLR_CLASS = msvc +OPT_WHOLE_PROGRAM = YES + #------------------------------------------------------- WINLINK = link @@ -42,11 +44,13 @@ WARN_CFLAGS_NO = -W1 # -Ox maximum optimizations # -GL whole program optimization # -Oy- re-enable creation of frame pointers -OPT_CFLAGS_YES = -Ox -GL -Oy- +OPT_CFLAGS_YES_YES = -Ox -GL -Oy- +OPT_CFLAGS_YES_NO = -Ox -Oy- +OPT_CFLAGS_YES = $(OPT_CFLAGS_YES_$(OPT_WHOLE_PROGRAM)) # # -Zi generate program database for debugging information -# -RTCsu catch bugs occuring only inoptimized code +# -RTCsu enable run-time error checks OPT_CFLAGS_NO = -Zi -RTCsu # specify object file name and location @@ -96,17 +100,19 @@ CODE_CPPFLAGS += -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE # -w44355 set "'this' used in the base initializer list" to be level 4 # -w44344 "behavior change: use of explicit template arguments results in ..." WARN_CXXFLAGS_YES = -W3 -w44355 -w44344 -WARN_CXXFLAGS_NO = -W1 +WARN_CXXFLAGS_NO = -W1 # # -Ox maximum optimizations # -GL whole program optimization # -Oy- re-enable creation of frame pointers -OPT_CXXFLAGS_YES = -Ox -GL -Oy- +OPT_CXXFLAGS_YES_YES = -Ox -GL -Oy- +OPT_CXXFLAGS_YES_NO = -Ox -Oy- +OPT_CXXFLAGS_YES = $(OPT_CXXFLAGS_YES_$(OPT_WHOLE_PROGRAM)) # # -Zi generate program database for debugging information -# -RTCsu catch bugs occurring only in optimized code +# -RTCsu enable run-time error checks OPT_CXXFLAGS_NO = -RTCsu -Zi # specify object file name and location @@ -136,11 +142,14 @@ RANLIB= # -fixed:no generate relocatable code # -version:. - only 2 components allowed, 0-65535 each # -debug generate debugging info -LINK_OPT_FLAGS_YES = -LTCG -incremental:no -opt:ref \ - -release $(PROD_VERSION:%=-version:%) +LINK_OPT_FLAGS_WHOLE_YES = -LTCG +LINK_OPT_FLAGS_YES = $(LINK_OPT_FLAGS_WHOLE_$(OPT_WHOLE_PROGRAM)) +LINK_OPT_FLAGS_YES += -incremental:no -opt:ref +LINK_OPT_FLAGS_YES += -release $(PROD_VERSION:%=-version:%) LINK_OPT_FLAGS_NO = -debug -incremental:no -fixed:no OPT_LDFLAGS = $(LINK_OPT_FLAGS_$(HOST_OPT)) -LIB_OPT_FLAGS_YES = -LTCG + +LIB_OPT_FLAGS_YES = $(LINK_OPT_FLAGS_WHOLE_$(OPT_WHOLE_PROGRAM)) LIB_OPT_LDFLAGS = $(LIB_OPT_FLAGS_$(HOST_OPT)) ARCH_DEP_CFLAGS= diff --git a/configure/os/CONFIG_SITE.Common.win32-x86-static b/configure/os/CONFIG_SITE.Common.win32-x86-static new file mode 100644 index 000000000..de4e61760 --- /dev/null +++ b/configure/os/CONFIG_SITE.Common.win32-x86-static @@ -0,0 +1,8 @@ +# CONFIG_SITE.Common.win32-x86-static +# +# Site-specific settings for the win32-x86-static target + +# Whole-program optimization doesn't work with Visual Studio 2010 when +# building static binaries. Newer versions of Visual Studio than 2010 +# may work though, comment out or set this to YES to try. +OPT_WHOLE_PROGRAM = NO diff --git a/configure/os/CONFIG_SITE.Common.windows-x64-static b/configure/os/CONFIG_SITE.Common.windows-x64-static index f2f059751..5db619b11 100644 --- a/configure/os/CONFIG_SITE.Common.windows-x64-static +++ b/configure/os/CONFIG_SITE.Common.windows-x64-static @@ -1,9 +1,8 @@ # CONFIG_SITE.Common.windows-x64-static # -# Site Specific definitions for windows-x64-static target -# Only the local epics system manager should modify this file - -# 64-bit Visual Studio 2010 builds fail when built optimized. -# If you are using a newer version you can try removing this: -HOST_OPT = NO +# Site-specific settings for the windows-x64-static target +# Whole-program optimization doesn't work with Visual Studio 2010 when +# building static binaries. Newer versions of Visual Studio than 2010 +# may work though, comment out or set this to YES to try. +OPT_WHOLE_PROGRAM = NO diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index a65400c7a..027889c54 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -32,6 +32,29 @@ of the dbStatic library that is not being built anymore.

Changes from the 3.14 branch since 3.15.4

+

Add dynamic (variable length) array support to PCAS

+ +

Dynamic array sizing support was added to the IOC server (RSRV) in the +Base-3.14.12 release, but has not until now been supported in the Portable +Channel Access Server (PCAS). Channel Access server applications using the +PCAS may not need to be modified at all; if they already push monitors with +different gdd array lengths, those variable sizes will be forwarded to any CA +clients who have requested variable length updates. The example CAS server +application has been modified to demonstrate this feature.

+ +

In implementing the above, the gdd method gdd::put(const gdd *) now +copies the full-sized array from the source gdd if the destination gdd is of +type array, has no allocated memory and a boundary size of 0.

+ +

Additional epicsTime conversion

+ +

The EPICS timestamp library (epicsTime) inside libCom's OSI layer has +been extended by routines that convert from struct tm to the EPICS +internal epicsTime type, assuming UTC - i.e. without going through +the timezone mechanism. This solves issues with converting from the structured +type to the EPICS timestamp at driver level from multiple threads at a high +repetition rate, where the timezone mechanism was blocking on file access.

+

MinGW Cross-builds from Linux

The build configuration files that allow cross-building of the 32-bit diff --git a/src/ca/client/cac.cpp b/src/ca/client/cac.cpp index d66cf9dad..d1afe6785 100644 --- a/src/ca/client/cac.cpp +++ b/src/ca/client/cac.cpp @@ -149,9 +149,9 @@ cac::cac ( iiuExistenceCount ( 0u ), cacShutdownInProgress ( false ) { - if ( ! osiSockAttach () ) { - throwWithLocation ( caErrorCode (ECA_INTERNAL) ); - } + if ( ! osiSockAttach () ) { + throwWithLocation ( udpiiu :: noSocket () ); + } try { long status; diff --git a/src/ca/client/iocinf.h b/src/ca/client/iocinf.h index c0c33d6c2..0d3b57db1 100644 --- a/src/ca/client/iocinf.h +++ b/src/ca/client/iocinf.h @@ -61,11 +61,6 @@ static const double CA_CONN_VERIFY_PERIOD = 30.0; /* (sec) how often to request */ static const unsigned contiguousMsgCountWhichTriggersFlowControl = 10u; -class caErrorCode { -public: - caErrorCode ( int ) {}; -}; - /* * CA internal functions */ diff --git a/src/ca/client/tcpiiu.cpp b/src/ca/client/tcpiiu.cpp index 58b1b0c0f..7fd848b20 100644 --- a/src/ca/client/tcpiiu.cpp +++ b/src/ca/client/tcpiiu.cpp @@ -44,12 +44,7 @@ using namespace std; -#if 0 -const unsigned mSecPerSec = 1000u; -const unsigned uSecPerSec = 1000u * mSecPerSec; -#endif - -tcpSendThread::tcpSendThread ( +tcpSendThread::tcpSendThread ( class tcpiiu & iiuIn, const char * pName, unsigned stackSize, unsigned priority ) : thread ( *this, pName, stackSize, priority ), iiu ( iiuIn ) @@ -807,28 +802,6 @@ tcpiiu::tcpiiu ( } } -# if 0 - // - // windows has a really strange implementation of thess options - // and we can avoid the need for this by using pthread_kill on unix - // - { - struct timeval timeout; - double pollInterval = connectionTimeout / 8.0; - timeout.tv_sec = static_cast < long > ( pollInterval ); - timeout.tv_usec = static_cast < long > - ( ( pollInterval - timeout.tv_sec ) * uSecPerSec ); - // intentionally ignore status as we dont expect that all systems - // will accept this request - setsockopt ( this->sock, SOL_SOCKET, SO_SNDTIMEO, - ( char * ) & timeout, sizeof ( timeout ) ); - // intentionally ignore status as we dont expect that all systems - // will accept this request - setsockopt ( this->sock, SOL_SOCKET, SO_RCVTIMEO, - ( char * ) & timeout, sizeof ( timeout ) ); - } -# endif - if ( isNameService() ) { pSearchDest->setCircuit ( this ); } diff --git a/src/ca/legacy/gdd/gdd.cc b/src/ca/legacy/gdd/gdd.cc index fd2b662c9..acb53da4e 100644 --- a/src/ca/legacy/gdd/gdd.cc +++ b/src/ca/legacy/gdd/gdd.cc @@ -1355,8 +1355,9 @@ gddStatus gdd::put ( const gdd * dd ) } aitUint32 srcAvailSize = srcElemCount - unusedSrcBelow; - if ( srcAvailSize > this->getBounds()->size() ) { - srcCopySize = this->getBounds()->size(); + aitUint32 destSize = this->getBounds()->size(); + if ( destSize > 0 && srcAvailSize > destSize ) { + srcCopySize = destSize; } else { srcCopySize = srcAvailSize; diff --git a/src/ca/legacy/pcas/generic/caHdrLargeArray.h b/src/ca/legacy/pcas/generic/caHdrLargeArray.h index 89b0d6d4b..7bda002e9 100644 --- a/src/ca/legacy/pcas/generic/caHdrLargeArray.h +++ b/src/ca/legacy/pcas/generic/caHdrLargeArray.h @@ -30,7 +30,7 @@ # include "shareLib.h" #endif -static const unsigned char CA_MINOR_PROTOCOL_REVISION = 12; +static const unsigned char CA_MINOR_PROTOCOL_REVISION = 13; typedef ca_uint32_t caResId; diff --git a/src/ca/legacy/pcas/generic/caServerI.h b/src/ca/legacy/pcas/generic/caServerI.h index 344f6393b..012233693 100644 --- a/src/ca/legacy/pcas/generic/caServerI.h +++ b/src/ca/legacy/pcas/generic/caServerI.h @@ -40,8 +40,13 @@ class casIntfOS; class casMonitor; class casChannelI; -caStatus convertContainerMemberToAtomic ( class gdd & dd, - aitUint32 appType, aitUint32 elemCount ); +caStatus convertContainerMemberToAtomic (class gdd & dd, + aitUint32 appType, aitUint32 requestedCount, aitUint32 nativeCount); + +// Keep the old signature for backward compatibility +inline caStatus convertContainerMemberToAtomic (class gdd & dd, + aitUint32 appType, aitUint32 elemCount) +{ return convertContainerMemberToAtomic(dd, appType, elemCount, elemCount); } class caServerI : public caServerIO, diff --git a/src/ca/legacy/pcas/generic/casCtx.h b/src/ca/legacy/pcas/generic/casCtx.h index 706376e77..eab644685 100644 --- a/src/ca/legacy/pcas/generic/casCtx.h +++ b/src/ca/legacy/pcas/generic/casCtx.h @@ -18,6 +18,8 @@ #include "caHdrLargeArray.h" +class casStrmClient; + class casCtx { public: casCtx(); @@ -41,6 +43,7 @@ private: casChannelI * pChannel; casPVI * pPV; unsigned nAsyncIO; // checks for improper use of async io + friend class casStrmClient; }; inline const caHdrLargeArray * casCtx::getMsg() const diff --git a/src/ca/legacy/pcas/generic/casStrmClient.cc b/src/ca/legacy/pcas/generic/casStrmClient.cc index 6b1de3629..8db94adb3 100644 --- a/src/ca/legacy/pcas/generic/casStrmClient.cc +++ b/src/ca/legacy/pcas/generic/casStrmClient.cc @@ -388,14 +388,12 @@ caStatus casStrmClient::echoAction ( epicsGuard < casClientMutex > & ) // // casStrmClient::verifyRequest() // -caStatus casStrmClient::verifyRequest ( casChannelI * & pChan ) +caStatus casStrmClient::verifyRequest (casChannelI * & pChan , bool allowdyn) { - const caHdrLargeArray * mp = this->ctx.getMsg(); - // // channel exists for this resource id ? // - chronIntId tmpId ( mp->m_cid ); + chronIntId tmpId ( ctx.msg.m_cid ); pChan = this->chanTable.lookup ( tmpId ); if ( ! pChan ) { return ECA_BADCHID; @@ -404,14 +402,15 @@ caStatus casStrmClient::verifyRequest ( casChannelI * & pChan ) // // data type out of range ? // - if ( mp->m_dataType > ((unsigned)LAST_BUFFER_TYPE) ) { + if ( ctx.msg.m_dataType > ((unsigned)LAST_BUFFER_TYPE) ) { return ECA_BADTYPE; } // // element count out of range ? // - if ( mp->m_count > pChan->getPVI().nativeCount() || mp->m_count == 0u ) { + if ( ctx.msg.m_count > pChan->getPVI().nativeCount() || + ( !allowdyn && ctx.msg.m_count == 0u ) ) { return ECA_BADCOUNT; } @@ -444,7 +443,7 @@ caStatus casStrmClient::readAction ( epicsGuard < casClientMutex > & guard ) casChannelI * pChan; { - caStatus status = this->verifyRequest ( pChan ); + caStatus status = this->verifyRequest ( pChan, CA_V413 ( this->minor_version_number ) ); if ( status != ECA_NORMAL ) { if ( pChan ) { return this->sendErr ( guard, mp, pChan->getCID(), @@ -531,11 +530,15 @@ caStatus casStrmClient::readResponse ( epicsGuard < casClientMutex > & guard, pChan->getCID(), status, ECA_GETFAIL ); } + ca_uint32_t count = (msg.m_count == 0) ? + (ca_uint32_t)desc.getDataSizeElements() : + msg.m_count; + void * pPayload; { - unsigned payloadSize = dbr_size_n ( msg.m_dataType, msg.m_count ); + unsigned payloadSize = dbr_size_n ( msg.m_dataType, count ); caStatus localStatus = this->out.copyInHeader ( msg.m_cmmd, payloadSize, - msg.m_dataType, msg.m_count, pChan->getCID (), + msg.m_dataType, count, pChan->getCID (), msg.m_available, & pPayload ); if ( localStatus ) { if ( localStatus==S_cas_hugeRequest ) { @@ -551,21 +554,21 @@ caStatus casStrmClient::readResponse ( epicsGuard < casClientMutex > & guard, // (places the data in network format) // int mapDBRStatus = gddMapDbr[msg.m_dataType].conv_dbr( - pPayload, msg.m_count, desc, pChan->enumStringTable() ); + pPayload, count, desc, pChan->enumStringTable() ); if ( mapDBRStatus < 0 ) { desc.dump (); errPrintf ( S_cas_badBounds, __FILE__, __LINE__, "- get with PV=%s type=%u count=%u", - pChan->getPVI().getName(), msg.m_dataType, msg.m_count ); + pChan->getPVI().getName(), msg.m_dataType, count ); return this->sendErrWithEpicsStatus ( guard, & msg, pChan->getCID(), S_cas_badBounds, ECA_GETFAIL ); } int cacStatus = caNetConvert ( - msg.m_dataType, pPayload, pPayload, true, msg.m_count ); + msg.m_dataType, pPayload, pPayload, true, count ); if ( cacStatus != ECA_NORMAL ) { return this->sendErrWithEpicsStatus ( guard, & msg, pChan->getCID(), S_cas_internal, cacStatus ); } - if ( msg.m_dataType == DBR_STRING && msg.m_count == 1u ) { + if ( msg.m_dataType == DBR_STRING && count == 1u ) { unsigned reducedPayloadSize = strlen ( static_cast < char * > ( pPayload ) ) + 1u; this->out.commitMsg ( reducedPayloadSize ); } @@ -585,7 +588,7 @@ caStatus casStrmClient::readNotifyAction ( epicsGuard < casClientMutex > & guard casChannelI * pChan; { - caStatus status = this->verifyRequest ( pChan ); + caStatus status = this->verifyRequest ( pChan, CA_V413 ( this->minor_version_number ) ); if ( status != ECA_NORMAL ) { return this->readNotifyFailureResponse ( guard, * mp, status ); } @@ -656,11 +659,15 @@ caStatus casStrmClient::readNotifyResponse ( epicsGuard < casClientMutex > & gua return ecaStatus; } + ca_uint32_t count = (msg.m_count == 0) ? + (ca_uint32_t)desc.getDataSizeElements() : + msg.m_count; + void *pPayload; { - unsigned size = dbr_size_n ( msg.m_dataType, msg.m_count ); + unsigned size = dbr_size_n ( msg.m_dataType, count ); caStatus status = this->out.copyInHeader ( msg.m_cmmd, size, - msg.m_dataType, msg.m_count, ECA_NORMAL, + msg.m_dataType, count, ECA_NORMAL, msg.m_available, & pPayload ); if ( status ) { if ( status == S_cas_hugeRequest ) { @@ -675,23 +682,23 @@ caStatus casStrmClient::readNotifyResponse ( epicsGuard < casClientMutex > & gua // convert gdd to db_access type // int mapDBRStatus = gddMapDbr[msg.m_dataType].conv_dbr ( pPayload, - msg.m_count, desc, pChan->enumStringTable() ); + count, desc, pChan->enumStringTable() ); if ( mapDBRStatus < 0 ) { desc.dump(); errPrintf ( S_cas_badBounds, __FILE__, __LINE__, "- get notify with PV=%s type=%u count=%u", - pChan->getPVI().getName(), msg.m_dataType, msg.m_count ); + pChan->getPVI().getName(), msg.m_dataType, count ); return this->readNotifyFailureResponse ( guard, msg, ECA_NOCONVERT ); } int cacStatus = caNetConvert ( - msg.m_dataType, pPayload, pPayload, true, msg.m_count ); + msg.m_dataType, pPayload, pPayload, true, count ); if ( cacStatus != ECA_NORMAL ) { return this->sendErrWithEpicsStatus ( guard, & msg, pChan->getCID(), S_cas_internal, cacStatus ); } - if ( msg.m_dataType == DBR_STRING && msg.m_count == 1u ) { + if ( msg.m_dataType == DBR_STRING && count == 1u ) { unsigned reducedPayloadSize = strlen ( static_cast < char * > ( pPayload ) ) + 1u; this->out.commitMsg ( reducedPayloadSize ); } @@ -727,8 +734,8 @@ caStatus casStrmClient::readNotifyFailureResponse ( // to be more efficent if it discovers that the source has less data // than the destination) // -caStatus convertContainerMemberToAtomic ( gdd & dd, - aitUint32 appType, aitUint32 elemCount ) +caStatus convertContainerMemberToAtomic ( gdd & dd, + aitUint32 appType, aitUint32 requestedCount, aitUint32 nativeCount ) { gdd * pVal; if ( dd.isContainer() ) { @@ -755,13 +762,13 @@ caStatus convertContainerMemberToAtomic ( gdd & dd, return S_cas_badType; } - if ( elemCount <= 1 ) { + if ( nativeCount <= 1 ) { return S_cas_success; } // convert to atomic gddBounds bds; - bds.setSize ( elemCount ); + bds.setSize ( requestedCount ); bds.setFirst ( 0u ); pVal->setDimension ( 1u, & bds ); return S_cas_success; @@ -770,9 +777,9 @@ caStatus convertContainerMemberToAtomic ( gdd & dd, // // createDBRDD () // -static caStatus createDBRDD ( unsigned dbrType, - unsigned elemCount, gdd * & pDD ) -{ +static caStatus createDBRDD ( unsigned dbrType, + unsigned requestedCount, unsigned nativeCount, gdd * & pDD ) +{ /* * DBR type has already been checked, but it is possible * that "gddDbrToAit" will not track with changes in @@ -799,7 +806,7 @@ static caStatus createDBRDD ( unsigned dbrType, // fix the value element count caStatus status = convertContainerMemberToAtomic ( - *pDescRet, gddAppType_value, elemCount ); + *pDescRet, gddAppType_value, requestedCount, nativeCount ); if ( status != S_cas_success ) { pDescRet->unreference (); return status; @@ -849,11 +856,15 @@ caStatus casStrmClient::monitorResponse ( casChannelI & chan, const caHdrLargeArray & msg, const gdd & desc, const caStatus completionStatus ) { + ca_uint32_t count = (msg.m_count == 0) ? + (ca_uint32_t)desc.getDataSizeElements() : + msg.m_count; + void * pPayload = 0; { - ca_uint32_t size = dbr_size_n ( msg.m_dataType, msg.m_count ); + ca_uint32_t size = dbr_size_n ( msg.m_dataType, count ); caStatus status = out.copyInHeader ( msg.m_cmmd, size, - msg.m_dataType, msg.m_count, ECA_NORMAL, + msg.m_dataType, count, ECA_NORMAL, msg.m_available, & pPayload ); if ( status ) { if ( status == S_cas_hugeRequest ) { @@ -871,7 +882,8 @@ caStatus casStrmClient::monitorResponse ( gdd * pDBRDD = 0; if ( completionStatus == S_cas_success ) { - caStatus status = createDBRDD ( msg.m_dataType, msg.m_count, pDBRDD ); + caStatus status = createDBRDD ( msg.m_dataType, count, + chan.getPVI().nativeCount(), pDBRDD ); if ( status != S_cas_success ) { caStatus ecaStatus; if ( status == S_cas_badType ) { @@ -892,7 +904,7 @@ caStatus casStrmClient::monitorResponse ( pDBRDD->unreference (); errPrintf ( S_cas_noConvert, __FILE__, __LINE__, "no conversion between event app type=%d and DBR type=%d Element count=%d", - desc.applicationType (), msg.m_dataType, msg.m_count); + desc.applicationType (), msg.m_dataType, count); return monitorFailureResponse ( guard, msg, ECA_NOCONVERT ); } } @@ -915,14 +927,14 @@ caStatus casStrmClient::monitorResponse ( } int mapDBRStatus = gddMapDbr[msg.m_dataType].conv_dbr ( - pPayload, msg.m_count, *pDBRDD, chan.enumStringTable() ); + pPayload, count, *pDBRDD, chan.enumStringTable() ); if ( mapDBRStatus < 0 ) { pDBRDD->unreference (); return monitorFailureResponse ( guard, msg, ECA_NOCONVERT ); } int cacStatus = caNetConvert ( - msg.m_dataType, pPayload, pPayload, true, msg.m_count ); + msg.m_dataType, pPayload, pPayload, true, count ); if ( cacStatus != ECA_NORMAL ) { pDBRDD->unreference (); return this->sendErrWithEpicsStatus ( @@ -932,7 +944,7 @@ caStatus casStrmClient::monitorResponse ( // // force string message size to be the true size // - if ( msg.m_dataType == DBR_STRING && msg.m_count == 1u ) { + if ( msg.m_dataType == DBR_STRING && count == 1u ) { ca_uint32_t reducedPayloadSize = strlen ( static_cast < char * > ( pPayload ) ) + 1u; this->out.commitMsg ( reducedPayloadSize ); } @@ -1940,7 +1952,7 @@ caStatus casStrmClient::eventAddAction ( casChannelI *pciu; { - caStatus status = casStrmClient::verifyRequest ( pciu ); + caStatus status = casStrmClient::verifyRequest ( pciu, CA_V413 ( this->minor_version_number ) ); if ( status != ECA_NORMAL ) { if ( pciu ) { return this->sendErr ( guard, mp, @@ -2601,8 +2613,8 @@ caStatus casStrmClient::read () { gdd * pDD = 0; - caStatus status = createDBRDD ( pHdr->m_dataType, - pHdr->m_count, pDD ); + caStatus status = createDBRDD ( pHdr->m_dataType, pHdr->m_count, + this->ctx.getChannel()->getPVI().nativeCount(), pDD ); if ( status != S_cas_success ) { return status; } diff --git a/src/ca/legacy/pcas/generic/casStrmClient.h b/src/ca/legacy/pcas/generic/casStrmClient.h index 3f6c0a51b..0fdd36bb4 100644 --- a/src/ca/legacy/pcas/generic/casStrmClient.h +++ b/src/ca/legacy/pcas/generic/casStrmClient.h @@ -69,7 +69,7 @@ private: bool responseIsPending; caStatus createChannel ( const char * pName ); - caStatus verifyRequest ( casChannelI * & pChan ); + caStatus verifyRequest ( casChannelI * & pChan, bool allowdyn = false ); typedef caStatus ( casStrmClient :: * pCASMsgHandler ) ( epicsGuard < casClientMutex > & ); static pCASMsgHandler const msgHandlers[CA_PROTO_LAST_CMMD+1u]; diff --git a/src/libCom/osi/epicsTime.cpp b/src/libCom/osi/epicsTime.cpp index b85b67b8e..af4fae25d 100644 --- a/src/libCom/osi/epicsTime.cpp +++ b/src/libCom/osi/epicsTime.cpp @@ -60,11 +60,13 @@ static const unsigned long NTP_TIME_AT_EPICS_EPOCH = // // epicsTime (const unsigned long secIn, const unsigned long nSecIn) // -inline epicsTime::epicsTime (const unsigned long secIn, - const unsigned long nSecIn) : - secPastEpoch ( nSecIn / nSecPerSec + secIn ), - nSec ( nSecIn % nSecPerSec ) +inline epicsTime::epicsTime (const unsigned long secIn, const unsigned long nSecIn) : + secPastEpoch ( secIn ), nSec ( nSecIn ) { + if (nSecIn >= nSecPerSec) { + this->secPastEpoch += nSecIn / nSecPerSec; + this->nSec = nSecIn % nSecPerSec; + } } // @@ -113,47 +115,26 @@ epicsTimeLoadTimeInit::epicsTimeLoadTimeInit () } // -// epicsTime::addNanoSec () +// private epicsTime::addNanoSec () // -// The nano-second field of several of the the UNIX time stamp formats -// field is stored in the C type "long". +// Most formats keep the nSec value as an unsigned long, so are +ve. +// struct timeval's tv_usec may be -1, but I think that means error, +// so this private method never needs to handle -ve offsets. // void epicsTime :: addNanoSec ( long nSecAdj ) { - // - // After optimizing this function we now have a larger code which - // uses only unsigned integer, and not floating point, arithmetic. - // This change benefits embedded CPU's lacking a floating point - // co-processor at the expense of some additional code to maintain. - // - // We hope that all CPU's we run on provide at least an integer - // divide instruction which should enable this implementation - // to be more efficient than implementations based on branching; - // this is presuming that we will run on pipelined architectures. - // - // Overflow and underflow is expected; in the future we might - // operate close to, the modulo of, the EPICS epic. - // - // We are depending on the normalize operation in the private - // constructor used below. - // - // joh 11-04-2012 - // - if ( nSecAdj >= 0 ) { - const unsigned long nSecPlus = - static_cast ( nSecAdj ); - const unsigned long nSecPlusAdj = nSecPlus % nSecPerSec; - const unsigned long secPlusAdj = nSecPlus / nSecPerSec; - *this = epicsTime ( this->secPastEpoch+secPlusAdj, - this->nSec+nSecPlusAdj ); + if (nSecAdj <= 0) + return; + + if (static_cast(nSecAdj) >= nSecPerSec) { + this->secPastEpoch += nSecAdj / nSecPerSec; + nSecAdj %= nSecPerSec; } - else { - const unsigned long nSecMinus = - static_cast ( -nSecAdj ); - const unsigned long nSecMinusAdj = nSecMinus % nSecPerSec; - const unsigned long secMinusAdj = nSecMinus / nSecPerSec; - *this = epicsTime ( this->secPastEpoch - secMinusAdj - 1u, - this->nSec + nSecPerSec - nSecMinusAdj ); + + this->nSec += nSecAdj; // Can't overflow + if (this->nSec >= nSecPerSec) { + this->secPastEpoch++; + this->nSec -= nSecPerSec; } } @@ -317,20 +298,82 @@ epicsTime::operator gm_tm_nano_sec () const // epicsTime::epicsTime (const local_tm_nano_sec &tm) { - static const time_t mktimeFailure = static_cast (-1); - time_t_wrapper ansiTimeTicks; struct tm tmp = tm.ansi_tm; + time_t_wrapper ansiTimeTicks = { mktime (&tmp) }; - ansiTimeTicks.ts = mktime (&tmp); - if (ansiTimeTicks.ts == mktimeFailure) { + static const time_t mktimeError = static_cast (-1); + if (ansiTimeTicks.ts == mktimeError) { throwWithLocation ( formatProblemWithStructTM () ); } - *this = epicsTime (ansiTimeTicks); + *this = epicsTime(ansiTimeTicks); + this->addNanoSec(tm.nSec); +} - unsigned long nSecAdj = tm.nSec % nSecPerSec; - unsigned long secAdj = tm.nSec / nSecPerSec; - *this = epicsTime ( this->secPastEpoch+secAdj, this->nSec+nSecAdj ); +// +// epicsTime (const gm_tm_nano_sec &tm) +// + +// do conversion avoiding the timezone mechanism +static inline int is_leap(int year) +{ + if (year % 400 == 0) + return 1; + if (year % 100 == 0) + return 0; + if (year % 4 == 0) + return 1; + return 0; +} + +static inline int days_from_0(int year) +{ + year--; + return 365 * year + (year / 400) - (year / 100) + (year / 4); +} + +static inline int days_from_1970(int year) +{ + static const int days_from_0_to_1970 = days_from_0(1970); + return days_from_0(year) - days_from_0_to_1970; +} + +static inline int days_from_1jan(int year, int month, int day) +{ + static const int days[2][12] = + { + { 0,31,59,90,120,151,181,212,243,273,304,334}, + { 0,31,60,91,121,152,182,213,244,274,305,335} + }; + return days[is_leap(year)][month-1] + day - 1; +} + +epicsTime::epicsTime (const gm_tm_nano_sec &tm) +{ + int year = tm.ansi_tm.tm_year + 1900; + int month = tm.ansi_tm.tm_mon; + if (month > 11) { + year += month / 12; + month %= 12; + } else if (month < 0) { + int years_diff = (-month + 11) / 12; + year -= years_diff; + month += 12 * years_diff; + } + month++; + + int day = tm.ansi_tm.tm_mday; + int day_of_year = days_from_1jan(year, month, day); + int days_since_epoch = days_from_1970(year) + day_of_year; + + time_t_wrapper ansiTimeTicks; + ansiTimeTicks.ts = ((days_since_epoch + * 24 + tm.ansi_tm.tm_hour) + * 60 + tm.ansi_tm.tm_min) + * 60 + tm.ansi_tm.tm_sec; + + *this = epicsTime(ansiTimeTicks); + this->addNanoSec(tm.nSec); } // @@ -729,7 +772,7 @@ double epicsTime::operator - (const epicsTime &rhs) const // so the unsigned to signed conversion is ok // if (this->nSec>=rhs.nSec) { - nSecRes = this->nSec - rhs.nSec; + nSecRes = this->nSec - rhs.nSec; } else { nSecRes = rhs.nSec - this->nSec; @@ -944,6 +987,19 @@ extern "C" { } return epicsTimeOK; } + epicsShareFunc int epicsShareAPI epicsTimeFromGMTM (epicsTimeStamp *pDest, const struct tm *pSrc, unsigned long nSecSrc) + { + try { + gm_tm_nano_sec tmns; + tmns.ansi_tm = *pSrc; + tmns.nSec = nSecSrc; + *pDest = epicsTime (tmns); + } + catch (...) { + return epicsTimeERROR; + } + return epicsTimeOK; + } epicsShareFunc int epicsShareAPI epicsTimeToTimespec (struct timespec *pDest, const epicsTimeStamp *pSrc) { try { @@ -1075,4 +1131,3 @@ extern "C" { } } } - diff --git a/src/libCom/osi/epicsTime.h b/src/libCom/osi/epicsTime.h index a36d6afe3..1cb3733bc 100644 --- a/src/libCom/osi/epicsTime.h +++ b/src/libCom/osi/epicsTime.h @@ -105,10 +105,12 @@ public: epicsTime & operator = ( const local_tm_nano_sec & ); /* - * convert to ANSI Cs "struct tm" (with nano seconds) + * convert to and from ANSI Cs "struct tm" (with nano seconds) * adjusted for GM time (UTC) */ operator gm_tm_nano_sec () const; + epicsTime ( const gm_tm_nano_sec & ); + epicsTime & operator = ( const gm_tm_nano_sec & ); /* convert to and from POSIX RTs "struct timespec" */ operator struct timespec () const; @@ -194,13 +196,15 @@ epicsShareFunc int epicsShareAPI epicsTimeToTime_t ( epicsShareFunc int epicsShareAPI epicsTimeFromTime_t ( epicsTimeStamp * pDest, time_t src ); -/*convert to and from ANSI C's "struct tm" with nano seconds */ +/* convert to and from ANSI C's "struct tm" with nano seconds */ epicsShareFunc int epicsShareAPI epicsTimeToTM ( struct tm * pDest, unsigned long * pNSecDest, const epicsTimeStamp * pSrc ); epicsShareFunc int epicsShareAPI epicsTimeToGMTM ( struct tm * pDest, unsigned long * pNSecDest, const epicsTimeStamp * pSrc ); epicsShareFunc int epicsShareAPI epicsTimeFromTM ( epicsTimeStamp * pDest, const struct tm * pSrc, unsigned long nSecSrc ); +epicsShareFunc int epicsShareAPI epicsTimeFromGMTM ( + epicsTimeStamp * pDest, const struct tm * pSrc, unsigned long nSecSrc ); /* convert to and from POSIX RT's "struct timespec" */ epicsShareFunc int epicsShareAPI epicsTimeToTimespec ( @@ -312,6 +316,11 @@ inline epicsTime & epicsTime::operator = ( const local_tm_nano_sec & rhs ) return *this = epicsTime ( rhs ); } +inline epicsTime & epicsTime::operator = ( const gm_tm_nano_sec & rhs ) +{ + return *this = epicsTime ( rhs ); +} + inline epicsTime & epicsTime::operator = ( const struct timespec & rhs ) { *this = epicsTime ( rhs ); diff --git a/src/libCom/test/epicsTimeTest.cpp b/src/libCom/test/epicsTimeTest.cpp index 970258801..ec5da4c78 100644 --- a/src/libCom/test/epicsTimeTest.cpp +++ b/src/libCom/test/epicsTimeTest.cpp @@ -43,7 +43,7 @@ MAIN(epicsTimeTest) const int wasteTime = 100000; const int nTimes = 10; - testPlan(15 + nTimes * 18); + testPlan(15 + nTimes * 19); try { const epicsTimeStamp epochTS = {0, 0}; @@ -200,6 +200,11 @@ MAIN(epicsTimeTest) epicsTime beginANSI = ansiDate; testOk1(beginANSI + diff == now); + // test struct gmtm round-trip conversion + gm_tm_nano_sec ansiGmDate = begin; + epicsTime beginGMANSI = ansiGmDate; + testOk1(beginGMANSI + diff == now); + // test struct timespec round-trip conversion struct timespec ts = begin; epicsTime beginTS = ts; diff --git a/src/template/base/top/caServerApp/exAsyncPV.cc b/src/template/base/top/caServerApp/exAsyncPV.cc index 7e0758c13..b5bff9181 100644 --- a/src/template/base/top/caServerApp/exAsyncPV.cc +++ b/src/template/base/top/caServerApp/exAsyncPV.cc @@ -3,9 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ // @@ -30,23 +29,23 @@ exAsyncPV::exAsyncPV ( exServer & cas, pvInfo & setup, // caStatus exAsyncPV::read (const casCtx &ctx, gdd &valueIn) { - exAsyncReadIO *pIO; - - if ( this->simultAsychReadIOCount >= this->cas.maxSimultAsyncIO () ) { - return S_casApp_postponeAsyncIO; - } + exAsyncReadIO *pIO; - pIO = new exAsyncReadIO ( this->cas, ctx, - *this, valueIn, this->asyncDelay ); - if ( ! pIO ) { + if ( this->simultAsychReadIOCount >= this->cas.maxSimultAsyncIO () ) { + return S_casApp_postponeAsyncIO; + } + + pIO = new exAsyncReadIO ( this->cas, ctx, + *this, valueIn, this->asyncDelay ); + if ( ! pIO ) { if ( this->simultAsychReadIOCount > 0 ) { return S_casApp_postponeAsyncIO; } else { - return S_casApp_noMemory; + return S_casApp_noMemory; } - } - this->simultAsychReadIOCount++; + } + this->simultAsychReadIOCount++; return S_casApp_asyncCompletion; } @@ -54,24 +53,24 @@ caStatus exAsyncPV::read (const casCtx &ctx, gdd &valueIn) // exAsyncPV::writeNotify() // caStatus exAsyncPV::writeNotify ( const casCtx &ctx, const gdd &valueIn ) -{ - if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) { - return S_casApp_postponeAsyncIO; - } +{ + if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) { + return S_casApp_postponeAsyncIO; + } - exAsyncWriteIO * pIO = new + exAsyncWriteIO * pIO = new exAsyncWriteIO ( this->cas, ctx, *this, - valueIn, this->asyncDelay ); - if ( ! pIO ) { + valueIn, this->asyncDelay ); + if ( ! pIO ) { if ( this->simultAsychReadIOCount > 0 ) { return S_casApp_postponeAsyncIO; } else { - return S_casApp_noMemory; + return S_casApp_noMemory; } } - this->simultAsychWriteIOCount++; - return S_casApp_asyncCompletion; + this->simultAsychWriteIOCount++; + return S_casApp_asyncCompletion; } // @@ -79,24 +78,24 @@ caStatus exAsyncPV::writeNotify ( const casCtx &ctx, const gdd &valueIn ) // caStatus exAsyncPV::write ( const casCtx &ctx, const gdd &valueIn ) { - // implement the discard intermediate values, but last value + // implement the discard intermediate values, but last value // sent always applied behavior that IOCs provide excepting // that we will alow N requests to pend instead of a limit // of only one imposed in the IOC - if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) { + if ( this->simultAsychWriteIOCount >= this->cas.maxSimultAsyncIO() ) { pStandbyValue.set ( & valueIn ); - return S_casApp_success; - } - - exAsyncWriteIO * pIO = new - exAsyncWriteIO ( this->cas, ctx, *this, - valueIn, this->asyncDelay ); - if ( ! pIO ) { - pStandbyValue.set ( & valueIn ); - return S_casApp_success; + return S_casApp_success; } - this->simultAsychWriteIOCount++; - return S_casApp_asyncCompletion; + + exAsyncWriteIO * pIO = new + exAsyncWriteIO ( this->cas, ctx, *this, + valueIn, this->asyncDelay ); + if ( ! pIO ) { + pStandbyValue.set ( & valueIn ); + return S_casApp_success; + } + this->simultAsychWriteIOCount++; + return S_casApp_asyncCompletion; } // Implementing a specialized update for exAsyncPV @@ -150,7 +149,7 @@ void exAsyncPV::removeWriteIO () exAsyncWriteIO::exAsyncWriteIO ( exServer & cas, const casCtx & ctxIn, exAsyncPV & pvIn, const gdd & valueIn, double asyncDelay ) : - casAsyncWriteIO ( ctxIn ), pv ( pvIn ), + casAsyncWriteIO ( ctxIn ), pv ( pvIn ), timer ( cas.createTimer () ), pValue(valueIn) { this->timer.start ( *this, asyncDelay ); @@ -168,7 +167,7 @@ exAsyncWriteIO::~exAsyncWriteIO() if ( this->pValue.valid () ) { this->pv.updateFromAsyncWrite ( *this->pValue ); } - this->pv.removeWriteIO(); + this->pv.removeWriteIO(); } // @@ -179,9 +178,9 @@ epicsTimerNotify::expireStatus exAsyncWriteIO:: expire ( const epicsTime & /* currentTime */ ) { assert ( this->pValue.valid () ); - caStatus status = this->pv.updateFromAsyncWrite ( *this->pValue ); - this->pValue.set ( 0 ); - this->postIOCompletion ( status ); + caStatus status = this->pv.updateFromAsyncWrite ( *this->pValue ); + this->pValue.set ( 0 ); + this->postIOCompletion ( status ); return noRestart; } @@ -191,7 +190,7 @@ epicsTimerNotify::expireStatus exAsyncWriteIO:: exAsyncReadIO::exAsyncReadIO ( exServer & cas, const casCtx & ctxIn, exAsyncPV & pvIn, gdd & protoIn, double asyncDelay ) : - casAsyncReadIO ( ctxIn ), pv ( pvIn ), + casAsyncReadIO ( ctxIn ), pv ( pvIn ), timer ( cas.createTimer() ), pProto ( protoIn ) { this->timer.start ( *this, asyncDelay ); @@ -202,7 +201,7 @@ exAsyncReadIO::exAsyncReadIO ( exServer & cas, const casCtx & ctxIn, // exAsyncReadIO::~exAsyncReadIO() { - this->pv.removeReadIO (); + this->pv.removeReadIO (); this->timer.destroy (); } @@ -213,16 +212,16 @@ exAsyncReadIO::~exAsyncReadIO() epicsTimerNotify::expireStatus exAsyncReadIO::expire ( const epicsTime & /* currentTime */ ) { - // - // map between the prototype in and the - // current value - // - caStatus status = this->pv.exPV::readNoCtx ( this->pProto ); + // + // map between the prototype in and the + // current value + // + caStatus status = this->pv.exPV::readNoCtx ( this->pProto ); - // - // post IO completion - // - this->postIOCompletion ( status, *this->pProto ); + // + // post IO completion + // + this->postIOCompletion ( status, *this->pProto ); return noRestart; } diff --git a/src/template/base/top/caServerApp/exPV.cc b/src/template/base/top/caServerApp/exPV.cc index af99c4e6a..97430c000 100644 --- a/src/template/base/top/caServerApp/exPV.cc +++ b/src/template/base/top/caServerApp/exPV.cc @@ -3,9 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ // // Example EPICS CA server @@ -42,7 +41,7 @@ exPV::exPV ( exServer & casIn, pvInfo & setup, // // no dataless PV allowed // - assert (this->info.getElementCount()>=1u); + assert (this->info.getCapacity()>=1u); // // start a very slow background scan diff --git a/src/template/base/top/caServerApp/exServer.cc b/src/template/base/top/caServerApp/exServer.cc index 49ea802dc..f934f3608 100644 --- a/src/template/base/top/caServerApp/exServer.cc +++ b/src/template/base/top/caServerApp/exServer.cc @@ -3,9 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ // // fileDescriptorManager.process(delay); @@ -304,7 +303,7 @@ exPV *pvInfo::createPV ( exServer & cas, bool preCreateFlag, // depending on the io type and the number // of elements // - if (this->elementCount==1u) { + if (this->capacity==1u) { switch (this->ioType){ case excasIoSync: pNewPV = new exScalarPV ( cas, *this, preCreateFlag, scanOn ); diff --git a/src/template/base/top/caServerApp/exServer.h b/src/template/base/top/caServerApp/exServer.h index 59ba11309..22aac2b72 100644 --- a/src/template/base/top/caServerApp/exServer.h +++ b/src/template/base/top/caServerApp/exServer.h @@ -3,9 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ // // Example EPICS CA server @@ -76,8 +75,10 @@ public: double getLopr () const; aitEnum getType () const; excasIoType getIOType () const; - unsigned getElementCount () const; - void unlinkPV (); + unsigned getCapacity () const; + unsigned getElementCount () const; + void setElementCount (unsigned); + void unlinkPV (); exPV *createPV ( exServer & exCAS, bool preCreateFlag, bool scanOn, double asyncDelay ); void deletePV (); @@ -88,7 +89,8 @@ private: const double lopr; aitEnum type; const excasIoType ioType; - const unsigned elementCount; + const unsigned capacity; + unsigned elementCount; exPV * pPV; pvInfo & operator = ( const pvInfo & ); }; @@ -441,8 +443,8 @@ inline pvInfo::pvInfo ( double scanPeriodIn, const char *pNameIn, scanPeriod ( scanPeriodIn ), pName ( pNameIn ), hopr ( hoprIn ), lopr ( loprIn ), type ( typeIn ), - ioType ( ioTypeIn ), elementCount ( countIn ), - pPV ( 0 ) + ioType ( ioTypeIn ), capacity ( countIn ), + elementCount ( 0 ), pPV ( 0 ) { } @@ -454,8 +456,8 @@ inline pvInfo::pvInfo ( const pvInfo & copyIn ) : scanPeriod ( copyIn.scanPeriod ), pName ( copyIn.pName ), hopr ( copyIn.hopr ), lopr ( copyIn.lopr ), type ( copyIn.type ), - ioType ( copyIn.ioType ), elementCount ( copyIn.elementCount ), - pPV ( copyIn.pPV ) + ioType ( copyIn.ioType ), capacity ( copyIn.capacity ), + elementCount ( copyIn.elementCount ), pPV ( copyIn.pPV ) { } @@ -509,12 +511,22 @@ inline excasIoType pvInfo::getIOType () const return this->ioType; } -inline unsigned pvInfo::getElementCount () const -{ - return this->elementCount; +inline unsigned pvInfo::getCapacity () const +{ + return this->capacity; } -inline void pvInfo::unlinkPV () +inline unsigned pvInfo::getElementCount () const +{ + return this->elementCount; +} + +inline void pvInfo::setElementCount (unsigned newCount) +{ + this->elementCount = newCount; +} + +inline void pvInfo::unlinkPV () { this->pPV = NULL; } diff --git a/src/template/base/top/caServerApp/exVectorPV.cc b/src/template/base/top/caServerApp/exVectorPV.cc index 963b59082..e14f65ae0 100644 --- a/src/template/base/top/caServerApp/exVectorPV.cc +++ b/src/template/base/top/caServerApp/exVectorPV.cc @@ -3,9 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ #include "exServer.h" @@ -48,7 +47,7 @@ unsigned exVectorPV::maxDimension() const aitIndex exVectorPV::maxBound (unsigned dimension) const { if (dimension==0u) { - return this->info.getElementCount(); + return this->info.getCapacity(); } else { return 0u; @@ -60,26 +59,21 @@ aitIndex exVectorPV::maxBound (unsigned dimension) const // void exVectorPV::scan() { - caStatus status; - double radians; - smartGDDPointer pDD; - aitFloat32 *pF, *pFE; - const aitFloat32 *pCF; - float newValue; - float limit; - exVecDestructor *pDest; - int gddStatus; + static epicsTime startTime = epicsTime::getCurrent(); - // - // update current time (so we are not required to do - // this every time that we write the PV which impacts - // throughput under sunos4 because gettimeofday() is - // slow) + // update current time // this->currentTime = epicsTime::getCurrent(); - - pDD = new gddAtomic (gddAppType_value, aitEnumFloat64, - 1u, this->info.getElementCount()); + + // demonstrate a changing array size + unsigned ramp = 15 & (unsigned) (this->currentTime - startTime); + unsigned newSize = this->info.getCapacity(); + if (newSize > ramp) { + newSize -= ramp; + } + + smartGDDPointer pDD = new gddAtomic (gddAppType_value, aitEnumFloat64, + 1u, newSize); if ( ! pDD.valid () ) { return; } @@ -87,18 +81,18 @@ void exVectorPV::scan() // // smart pointer class manages reference count after this point // - gddStatus = pDD->unreference(); - assert (!gddStatus); + gddStatus gdds = pDD->unreference(); + assert(!gdds); // // allocate array buffer // - pF = new aitFloat32 [this->info.getElementCount()]; + aitFloat64 * pF = new aitFloat64 [newSize]; if (!pF) { return; } - pDest = new exVecDestructor; + exVecDestructor * pDest = new exVecDestructor; if (!pDest) { delete [] pF; return; @@ -114,37 +108,39 @@ void exVectorPV::scan() // double check for reasonable bounds on the // current value // - pCF=NULL; - if ( this->pValue.valid () ) { - if (this->pValue->dimension()==1u) { - const gddBounds *pB = this->pValue->getBounds(); - if (pB[0u].size()==this->info.getElementCount()) { - pCF = *this->pValue; - } - } + const aitFloat64 *pCF = NULL, *pCFE = NULL; + if (this->pValue.valid () && + this->pValue->dimension() == 1u) { + const gddBounds *pB = this->pValue->getBounds(); + + pCF = *this->pValue; + pCFE = &pCF[pB->size()]; } - pFE = &pF[this->info.getElementCount()]; - while (pFinfo.getHopr(); + newValue += (sin (radians) / 10.0); + double limit = this->info.getHopr(); newValue = tsMin (newValue, limit); - limit = (float) this->info.getLopr(); + limit = this->info.getLopr(); newValue = tsMax (newValue, limit); - *(pF++) = newValue; + *pF++ = newValue; } aitTimeStamp gddts = this->currentTime; pDD->setTimeStamp ( & gddts ); - status = this->update ( *pDD ); + caStatus status = this->update ( *pDD ); + this->info.setElementCount(newSize); + if ( status != S_casApp_success ) { errMessage (status, "vector scan update failed\n"); } @@ -166,7 +162,7 @@ void exVectorPV::scan() // caStatus exVectorPV::updateValue ( const gdd & value ) { - + aitUint32 newSize = 0; // // Check bounds of incoming request // (and see if we are replacing all elements - @@ -183,7 +179,9 @@ caStatus exVectorPV::updateValue ( const gdd & value ) if ( pb[0u].first() != 0u ) { return S_casApp_outOfBounds; } - else if ( pb[0u].size() > this->info.getElementCount() ) { + + newSize = pb[0u].size(); + if ( newSize > this->info.getCapacity() ) { return S_casApp_outOfBounds; } } @@ -193,14 +191,14 @@ caStatus exVectorPV::updateValue ( const gdd & value ) // return S_casApp_outOfBounds; } - + // // Create a new array data descriptor // (so that old values that may be referenced on the // event queue are not replaced) // - smartGDDPointer pNewValue ( new gddAtomic ( gddAppType_value, aitEnumFloat64, - 1u, this->info.getElementCount() ) ); + smartGDDPointer pNewValue ( new gddAtomic ( gddAppType_value, aitEnumFloat64, + 1u, newSize ) ); if ( ! pNewValue.valid() ) { return S_casApp_noMemory; } @@ -211,21 +209,20 @@ caStatus exVectorPV::updateValue ( const gdd & value ) // gddStatus gdds = pNewValue->unreference( ); assert ( ! gdds ); - + // // allocate array buffer // - aitFloat64 * pF = new aitFloat64 [this->info.getElementCount()]; + aitFloat64 * pF = new aitFloat64 [newSize]; if (!pF) { return S_casApp_noMemory; } - + // // Install (and initialize) array buffer // if no old values exist // - unsigned count = this->info.getElementCount(); - for ( unsigned i = 0u; i < count; i++ ) { + for ( unsigned i = 0u; i < newSize; i++ ) { pF[i] = 0.0f; } @@ -240,7 +237,7 @@ caStatus exVectorPV::updateValue ( const gdd & value ) // (do this before we increment pF) // pNewValue->putRef ( pF, pDest ); - + // // copy in the values that they are writing // @@ -248,9 +245,10 @@ caStatus exVectorPV::updateValue ( const gdd & value ) if ( gdds ) { return S_cas_noConvert; } - + this->pValue = pNewValue; - + this->info.setElementCount(newSize); + return S_casApp_success; } @@ -261,6 +259,6 @@ caStatus exVectorPV::updateValue ( const gdd & value ) // void exVecDestructor::run ( void *pUntyped ) { - aitFloat32 * pf = reinterpret_cast < aitFloat32 * > ( pUntyped ); + aitFloat64 * pf = reinterpret_cast < aitFloat64 * > ( pUntyped ); delete [] pf; }