diff --git a/Doxyfile b/Doxyfile index 24768b1..3caa0af 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1078,7 +1078,7 @@ HTML_EXTRA_STYLESHEET = # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_FILES = documentation/pvDatabaseCPP.html +HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the stylesheet and background images according to diff --git a/Makefile b/Makefile index 70d34e5..c7236b5 100644 --- a/Makefile +++ b/Makefile @@ -1,27 +1,12 @@ #Makefile at top of application tree TOP = . include $(TOP)/configure/CONFIG -DIRS := $(DIRS) $(filter-out $(DIRS), configure) -DIRS := $(DIRS) $(filter-out $(DIRS), src) -DIRS := $(DIRS) $(filter-out $(DIRS), test) -DIRS := $(DIRS) $(filter-out $(DIRS), arrayPerformance) -DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard example*)) +DIRS += configure -EMBEDDED_TOPS := $(EMBEDDED_TOPS) $(filter-out $(EMBEDDED_TOPS), test) -EMBEDDED_TOPS := $(EMBEDDED_TOPS) $(filter-out $(EMBEDDED_TOPS), arrayPerformance) -EMBEDDED_TOPS := $(EMBEDDED_TOPS) $(filter-out $(EMBEDDED_TOPS), $(wildcard example*)) +DIRS += src +src_DEPEND_DIRS = configure -define DIR_template - $(1)_DEPEND_DIRS = configure -endef -$(foreach dir, $(filter-out configure,$(DIRS)),$(eval $(call DIR_template,$(dir)))) - -define EMB_template - $(1)_DEPEND_DIRS = src -endef -$(foreach dir, $(EMBEDDED_TOPS),$(eval $(call EMB_template,$(dir)))) - -#exampleDatabase_DEPEND_DIRS += test -#examplePowerSupply_DEPEND_DIRS += test +DIRS += test +test_DEPEND_DIRS = src include $(TOP)/configure/RULES_TOP diff --git a/README.md b/README.md index b8ac347..ee6a6c5 100644 --- a/README.md +++ b/README.md @@ -17,18 +17,18 @@ then just type: make -If RELEASE.local does not exist then look at configure/RELEASE -for directions for how to build. +It can also be built by: + + cp configure/ExampleRELEASE.local configure/RELEASE.local + edit configure/RELEASE.local + make + + Examples ------------ -The examples require the database in pvaDatabaseTestCPP. -For example: - - mrk> pwd - /home/epicsv4/pvaDatabaseTestCPP/database/iocBoot/exampleDatabase - mrk> ../../bin/linux-x86_64/exampleDatabase st.cmd +Examples are available in exampleCPP. Status ------ diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 78f1bf3..1efca70 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,14 @@ + +EPICS V4 release 4.6 +==================== + +The examples are moved to exampleCPP + +The test is now a regression test the can be ran via + + make runtests + + EPICS V4 release 4.5 ==================== diff --git a/arrayPerformance/Makefile b/arrayPerformance/Makefile deleted file mode 100644 index 999631d..0000000 --- a/arrayPerformance/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -#Makefile at top of application tree -TOP = . -include $(TOP)/configure/CONFIG -DIRS += configure - -DIRS += src -src_DEPEND_DIRS = configure - -include $(TOP)/configure/RULES_TOP - - diff --git a/arrayPerformance/configure/CONFIG b/arrayPerformance/configure/CONFIG deleted file mode 100644 index c1a4703..0000000 --- a/arrayPerformance/configure/CONFIG +++ /dev/null @@ -1,29 +0,0 @@ -# CONFIG - Load build configuration data -# -# Do not make changes to this file! - -# Allow user to override where the build rules come from -RULES = $(EPICS_BASE) - -# RELEASE files point to other application tops -include $(TOP)/configure/RELEASE --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common -ifdef T_A --include $(TOP)/configure/RELEASE.Common.$(T_A) --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) -endif - -CONFIG = $(RULES)/configure -include $(CONFIG)/CONFIG - -# Override the Base definition: -INSTALL_LOCATION = $(TOP) - -# CONFIG_SITE files contain other build configuration settings -include $(TOP)/configure/CONFIG_SITE --include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common -ifdef T_A - -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A) - -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) -endif - diff --git a/arrayPerformance/configure/CONFIG_SITE b/arrayPerformance/configure/CONFIG_SITE deleted file mode 100644 index 0165b88..0000000 --- a/arrayPerformance/configure/CONFIG_SITE +++ /dev/null @@ -1,39 +0,0 @@ -# CONFIG_SITE - -# Make any application-specific changes to the EPICS build -# configuration variables in this file. -# -# Host/target specific settings can be specified in files named -# CONFIG_SITE.$(EPICS_HOST_ARCH).Common -# CONFIG_SITE.Common.$(T_A) -# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) - -# CHECK_RELEASE controls the consistency checking of the support -# applications pointed to by the RELEASE* files. -# Normally CHECK_RELEASE should be set to YES. -# Set CHECK_RELEASE to NO to disable checking completely. -# Set CHECK_RELEASE to WARN to perform consistency checking but -# continue building anyway if conflicts are found. -#CHECK_RELEASE = YES - -# Set this when you only want to compile this application -# for a subset of the cross-compiled target architectures -# that Base is built for. -#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 - -# To install files into a location other than $(TOP) define -# INSTALL_LOCATION here. -#INSTALL_LOCATION= - -# Set this when your IOC and the host use different paths -# to access the application. This will be needed to boot -# from a Microsoft FTP server or with some NFS mounts. -# You must rebuild in the iocBoot directory for this to -# take effect. -#IOCS_APPL_TOP = - -INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv -USR_INCLUDES += -I $(INSTALL_LOCATION)/include - --include $(TOP)/../../CONFIG_SITE.local --include $(TOP)/../configure/CONFIG_SITE.local diff --git a/arrayPerformance/configure/ExampleRELEASE.local b/arrayPerformance/configure/ExampleRELEASE.local deleted file mode 100644 index 95e3783..0000000 --- a/arrayPerformance/configure/ExampleRELEASE.local +++ /dev/null @@ -1,8 +0,0 @@ -EPICS_BASE=/home/install/epics/base -TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top -EPICSV4HOME=/home/hg -PVCOMMON=${EPICSV4HOME}/pvCommonCPP -PVDATA=${EPICSV4HOME}/pvDataCPP -PVACCESS=${EPICSV4HOME}/pvAccessCPP -PVDATABASE=${EPICSV4HOME}/pvDatabaseCPP -PVASRV=${EPICSV4HOME}/pvaSrv diff --git a/arrayPerformance/configure/Makefile b/arrayPerformance/configure/Makefile deleted file mode 100644 index 9254309..0000000 --- a/arrayPerformance/configure/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -TOP=.. - -include $(TOP)/configure/CONFIG - -TARGETS = $(CONFIG_TARGETS) -CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS))) - -include $(TOP)/configure/RULES diff --git a/arrayPerformance/configure/RELEASE b/arrayPerformance/configure/RELEASE deleted file mode 100644 index eca1885..0000000 --- a/arrayPerformance/configure/RELEASE +++ /dev/null @@ -1,40 +0,0 @@ -# pvDatabaseCPP/arrayPerformance RELEASE - Location of external support modules -# -# IF YOU CHANGE this file or any file it includes you must -# subsequently do a "gnumake rebuild" in the application's -# top level directory. -# -# The build process does not check dependencies against files -# that are outside this application, thus you should also do a -# "gnumake rebuild" in the top level directory after EPICS_BASE -# or any other external module pointed to below is rebuilt. -# -# Host- or target-specific settings can be given in files named -# RELEASE.$(EPICS_HOST_ARCH).Common -# RELEASE.Common.$(T_A) -# RELEASE.$(EPICS_HOST_ARCH).$(T_A) - -# EPICS V4 Developers: Do not edit the locations in this file! -# -# Create a file RELEASE.local pointing to your places -# for the dependencies, e.g. -# PVACCESS = /path/to/epics/pvAccessCPP -# PVDATA = /path/to/epics/pvDataCPP -# PVCOMMON = /path/to/epics/pvCommonCPP -# EPICS_BASE = /path/to/epics/base - -# If this example is built in a directory under pvDatabaseCPP, -# use the following definitions: - -PVDATABASE = $(TOP)/.. --include $(TOP)/../../RELEASE.local --include $(TOP)/../configure/RELEASE.local - -# If you copied this example from pvDatabaseCPP to be built as a -# standalone TOP, define -# PVDATABASE = /path/to/epics/pvDatabaseCPP -# in the appropriate RELEASE[.local], -# and use the following definitions instead: - -#-include $(TOP)/../RELEASE.local -#-include $(TOP)/configure/RELEASE.local diff --git a/arrayPerformance/configure/RULES b/arrayPerformance/configure/RULES deleted file mode 100644 index 6d56e14..0000000 --- a/arrayPerformance/configure/RULES +++ /dev/null @@ -1,6 +0,0 @@ -# RULES - -include $(CONFIG)/RULES - -# Library should be rebuilt because LIBOBJS may have changed. -$(LIBNAME): ../Makefile diff --git a/arrayPerformance/configure/RULES.ioc b/arrayPerformance/configure/RULES.ioc deleted file mode 100644 index 901987c..0000000 --- a/arrayPerformance/configure/RULES.ioc +++ /dev/null @@ -1,2 +0,0 @@ -#RULES.ioc -include $(CONFIG)/RULES.ioc diff --git a/arrayPerformance/configure/RULES_DIRS b/arrayPerformance/configure/RULES_DIRS deleted file mode 100644 index 3ba269d..0000000 --- a/arrayPerformance/configure/RULES_DIRS +++ /dev/null @@ -1,2 +0,0 @@ -#RULES_DIRS -include $(CONFIG)/RULES_DIRS diff --git a/arrayPerformance/configure/RULES_TOP b/arrayPerformance/configure/RULES_TOP deleted file mode 100644 index 2b8cbc6..0000000 --- a/arrayPerformance/configure/RULES_TOP +++ /dev/null @@ -1,2 +0,0 @@ -#RULES_TOP -include $(CONFIG)/RULES_TOP diff --git a/arrayPerformance/src/Makefile b/arrayPerformance/src/Makefile deleted file mode 100644 index 362d0c6..0000000 --- a/arrayPerformance/src/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -TOP = .. -include $(TOP)/configure/CONFIG - -LIBRARY_IOC += pvDatabaseExample -pvDatabaseExample_LIBS += pvDatabase pvAccess pvData Com -pvDatabaseExample_LIBS += $(EPICS_BASE_IOC_LIBS) - - -INC+= arrayPerformance.h -LIBSRCS += arrayPerformance.cpp -INC+= longArrayMonitor.h -LIBSRCS += longArrayMonitor.cpp -INC+= longArrayGet.h -LIBSRCS += longArrayGet.cpp -INC+= longArrayPut.h -LIBSRCS += longArrayPut.cpp - -PROD_HOST += arrayPerformanceMain -arrayPerformanceMain_SRCS += arrayPerformanceMain.cpp -arrayPerformanceMain_LIBS += pvDatabase pvAccess pvData Com -arrayPerformanceMain_LIBS += pvDatabaseExample - -PROD_HOST += longArrayMonitorMain -longArrayMonitorMain_SRCS += longArrayMonitorMain.cpp -longArrayMonitorMain_LIBS += pvDatabase pvAccess pvData Com -longArrayMonitorMain_LIBS += pvDatabaseExample - -PROD_HOST += longArrayGetMain -longArrayGetMain_SRCS += longArrayGetMain.cpp -longArrayGetMain_LIBS += pvDatabase pvAccess pvData Com -longArrayGetMain_LIBS += pvDatabaseExample - -PROD_HOST += longArrayPutMain -longArrayPutMain_SRCS += longArrayPutMain.cpp -longArrayPutMain_LIBS += pvDatabase pvAccess pvData Com -longArrayPutMain_LIBS += pvDatabaseExample - -PROD_HOST += vectorPerformanceMain -vectorPerformanceMain_SRCS += vectorPerformanceMain.cpp -vectorPerformanceMain_LIBS += pvDatabase pvAccess pvData Com -vectorPerformanceMain_LIBS += pvDatabaseExample - - -include $(TOP)/configure/RULES - diff --git a/arrayPerformance/src/arrayPerformance.cpp b/arrayPerformance/src/arrayPerformance.cpp deleted file mode 100644 index efe2e3f..0000000 --- a/arrayPerformance/src/arrayPerformance.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* arrayPerformance.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.08 - */ - -#include - -#define epicsExportSharedSymbols -#include - - - -namespace epics { namespace pvDatabase { - -using namespace epics::pvData; -using std::tr1::static_pointer_cast; -using std::tr1::dynamic_pointer_cast; -using std::cout; -using std::endl; -using std::ostringstream; - -ArrayPerformancePtr ArrayPerformance::create( - std::string const & recordName, - size_t size, - double delay) -{ - epics::pvData::PVStructurePtr pvStructure = - epics::pvData::getStandardPVField()->scalarArray(epics::pvData::pvLong,"timeStamp,alarm"); - ArrayPerformancePtr pvRecord( - new ArrayPerformance(recordName,pvStructure,size,delay)); - if(!pvRecord->init()) pvRecord.reset(); - return pvRecord; -} - -ArrayPerformance::ArrayPerformance( - std::string const & recordName, - epics::pvData::PVStructurePtr const & pvStructure, - size_t size, - double delay) -: PVRecord(recordName,pvStructure), - size(size), - delay(delay), - isDestroyed(false) -{ - pvTimeStamp.attach(pvStructure->getSubField("timeStamp")); -} - -ArrayPerformance::~ArrayPerformance() -{ -} - -bool ArrayPerformance::init() -{ - - initPVRecord(); - PVLongArrayPtr pvLongArray = getPVStructure()->getSubField("value"); - if(!pvLongArray) return false; - pvValue = pvLongArray; - ArrayPerformancePtr xxx = dynamic_pointer_cast(getPtrSelf()); - arrayPerformanceThread = ArrayPerformanceThreadPtr(new ArrayPerformanceThread(xxx)); - arrayPerformanceThread->init(); - return true; -} - -void ArrayPerformance::start() -{ - arrayPerformanceThread->start(); -} - -void ArrayPerformance::process() -{ - timeStamp.getCurrent(); - pvTimeStamp.set(timeStamp); -} - -void ArrayPerformance::destroy() -{ - if(isDestroyed) return; - isDestroyed = true; - arrayPerformanceThread->destroy(); - arrayPerformanceThread.reset(); - PVRecord::destroy(); -} - -ArrayPerformanceThread::ArrayPerformanceThread(ArrayPerformancePtr const & arrayPerformance) -: - arrayPerformance(arrayPerformance), - isDestroyed(false), - runReturned(false), - threadName("arrayPerformance"), - value(0) -{} - -void ArrayPerformanceThread::init() -{ - thread = std::auto_ptr(new epicsThread( - *this, - threadName.c_str(), - epicsThreadGetStackSize(epicsThreadStackSmall), - epicsThreadPriorityHigh)); -} - -void ArrayPerformanceThread::start() -{ - thread->start(); -} - -void ArrayPerformanceThread::destroy() -{ - Lock lock(mutex); - if(isDestroyed) return; - isDestroyed = true; - while(true) { - if(runReturned) break; - lock.unlock(); - epicsThreadSleep(.01); - lock.lock(); - } - thread->exitWait(); - thread.reset(); - arrayPerformance.reset(); -} - -void ArrayPerformanceThread::run() -{ - TimeStamp timeStamp; - TimeStamp timeStampLast; - timeStampLast.getCurrent(); - int nSinceLastReport = 0; - while(true) { - if(arrayPerformance->delay>0.0) epicsThreadSleep(arrayPerformance->delay); - { - Lock lock(mutex); - if(isDestroyed) { - runReturned = true; - return; - } - } - timeStamp.getCurrent(); - double diff = TimeStamp::diff(timeStamp,timeStampLast); - if(diff>=1.0) { - ostringstream out; - out << "arrayPerformance value " << value; - out << " time " << diff ; - double iterations = nSinceLastReport; - iterations /= diff; - if(iterations>10.0e9) { - iterations /= 1e9; - out << " gigaIterations/sec " << iterations; - } else if(iterations>10.0e6) { - iterations /= 1e6; - out << " megaIterations/sec " << iterations; - } else if(iterations>10.0e3) { - iterations /= 1e3; - out << " kiloIterations/sec " << iterations; - } else { - out << " Iterations/sec " << iterations; - } - double elementSize = arrayPerformance->size; - double elementsPerSecond = elementSize*nSinceLastReport; - elementsPerSecond /= diff; - if(elementsPerSecond>10.0e9) { - elementsPerSecond /= 1e9; - out << " gigaElements/sec " << elementsPerSecond; - } else if(elementsPerSecond>10.0e6) { - elementsPerSecond /= 1e6; - out << " megaElements/sec " << elementsPerSecond; - } else if(elementsPerSecond>10.0e3) { - elementsPerSecond /= 1e3; - out << " kiloElements/sec " << elementsPerSecond; - } else { - out << " Elements/sec " << elementsPerSecond; - } - cout << out.str() << endl; - timeStampLast = timeStamp; - nSinceLastReport = 0; - } - ++nSinceLastReport; - arrayPerformance->lock(); - try { - if(arrayPerformance->getTraceLevel()>1) { - cout << "arrayPerformance size " << arrayPerformance->size; - cout << " value " << value +1 << endl; - } - shared_vector xxx(arrayPerformance->size,value++); - shared_vector data(freeze(xxx)); - arrayPerformance->beginGroupPut(); - arrayPerformance->pvValue->replace(data); - arrayPerformance->process(); - arrayPerformance->endGroupPut(); - } catch(...) { - arrayPerformance->unlock(); - throw; - } - arrayPerformance->unlock(); - } -} - -}} - diff --git a/arrayPerformance/src/arrayPerformance.h b/arrayPerformance/src/arrayPerformance.h deleted file mode 100644 index 60e8210..0000000 --- a/arrayPerformance/src/arrayPerformance.h +++ /dev/null @@ -1,92 +0,0 @@ -/* arrayPerformance.h */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.08 - */ -#ifndef ARRAYPERFORMANCE_H -#define ARRAYPERFORMANCE_H - - -#ifdef epicsExportSharedSymbols -# define arrayperformanceEpicsExportSharedSymbols -# undef epicsExportSharedSymbols -#endif - -#include -#include -#include -#include -#include - -#ifdef arrayperformanceEpicsExportSharedSymbols -# define epicsExportSharedSymbols -# undef arrayperformanceEpicsExportSharedSymbols -# include -#endif - -namespace epics { namespace pvDatabase { - -class ArrayPerformance; -typedef std::tr1::shared_ptr ArrayPerformancePtr; - -class ArrayPerformanceThread; -typedef std::tr1::shared_ptr ArrayPerformanceThreadPtr; - -class epicsShareClass ArrayPerformance : - public PVRecord -{ -public: - POINTER_DEFINITIONS(ArrayPerformance); - static ArrayPerformancePtr create( - std::string const & recordName, - size_t size, - double delay); - virtual ~ArrayPerformance(); - virtual bool init(); - virtual void start(); - virtual void process(); - virtual void destroy(); -private: - ArrayPerformance(std::string const & recordName, - epics::pvData::PVStructurePtr const & pvStructure, - size_t size, - double delay); - size_t size; - double delay; - bool isDestroyed; - epics::pvData::PVLongArrayPtr pvValue; - epics::pvData::PVTimeStamp pvTimeStamp; - epics::pvData::TimeStamp timeStamp; - ArrayPerformanceThreadPtr arrayPerformanceThread; - friend class ArrayPerformanceThread; -}; - -class epicsShareClass ArrayPerformanceThread : - public epicsThreadRunable -{ -public: - ArrayPerformanceThread(ArrayPerformancePtr const & arrayPerformance); - virtual ~ArrayPerformanceThread(){}; - void init(); - void start(); - virtual void run(); - void destroy(); -private: - ArrayPerformancePtr arrayPerformance; - bool isDestroyed; - bool runReturned; - std::string threadName; - epics::pvData::Mutex mutex; - epics::pvData::int64 value; - std::auto_ptr thread; -}; - - -}} - -#endif /* ARRAYPERFORMANCE_H */ diff --git a/arrayPerformance/src/arrayPerformanceMain.cpp b/arrayPerformance/src/arrayPerformanceMain.cpp deleted file mode 100644 index 9540a26..0000000 --- a/arrayPerformance/src/arrayPerformanceMain.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/*arrayPerformanceMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.08 - */ - -/* Author: Marty Kraimer */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace std; -using std::tr1::static_pointer_cast; -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; - -int main(int argc,char *argv[]) -{ - bool result(false); - string recordName; - recordName = "arrayPerformance"; - size_t size = 10000000; - double delay = .0001; - string providerName("local"); - size_t nMonitor = 1; - int queueSize = 2; - double waitTime = 0.0; - if(argc==2 && string(argv[1])==string("-help")) { - cout << "arrayPerformanceMain recordName size"; - cout << " delay providerName nMonitor queueSize waitTime" << endl; - cout << "default" << endl; - cout << "arrayPerformance "; - cout << recordName << " "; - cout << size << " "; - cout << delay << " "; - cout << providerName << " "; - cout << nMonitor << " "; - cout << queueSize << " "; - cout << "0.0" << endl; - return 0; - } - if(argc>1) recordName = argv[1]; - if(argc>2) size = strtoul(argv[2],0,0); - if(argc>3) delay = atof(argv[3]); - if(argc>4) providerName = argv[4]; - if(argc>5) nMonitor = strtoul(argv[5],0,0); - if(argc>6) queueSize = strtol(argv[6],0,0); - if(argc>7) waitTime = atof(argv[7]); - cout << "arrayPerformance "; - cout << recordName << " "; - cout << size << " "; - cout << delay << " "; - cout << providerName << " "; - cout << nMonitor << " "; - cout << queueSize << " "; - cout << waitTime << endl; - ClientFactory::start(); - PVDatabasePtr master = PVDatabase::getMaster(); - ChannelProviderLocalPtr channelProvider = getChannelProviderLocal(); - PVRecordPtr pvRecord; - pvRecord = ArrayPerformance::create(recordName,size,delay); - result = master->addRecord(pvRecord); - PVRecordPtr arrayPreformance = pvRecord; - arrayPreformance->setTraceLevel(0); - pvRecord = TraceRecord::create("traceRecordPGRPC"); - result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; - pvRecord.reset(); - ServerContext::shared_pointer pvaServer = - startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true); - std::vector longArrayMonitor(nMonitor); - for(size_t i=0; istart(); - cout << "arrayPerformance\n"; - string str; - while(true) { - cout << "Type exit to stop: \n"; - getline(cin,str); - if(str.compare("exit")==0) break; - - } - arrayPreformance.reset(); - for(size_t i=0; istop(); - for(size_t i=0; idestroy(); - pvaServer->shutdown(); - epicsThreadSleep(1.0); - pvaServer->destroy(); - ClientFactory::stop(); - epicsThreadSleep(1.0); - channelProvider->destroy(); - return 0; -} - diff --git a/arrayPerformance/src/longArrayGet.cpp b/arrayPerformance/src/longArrayGet.cpp deleted file mode 100644 index 778af61..0000000 --- a/arrayPerformance/src/longArrayGet.cpp +++ /dev/null @@ -1,543 +0,0 @@ -/* longArrayGet.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.09 - */ - -#include -#include - -#define epicsExportSharedSymbols -#include - -namespace epics { namespace pvDatabase { - -using namespace epics::pvData; -using namespace epics::pvAccess; -using std::tr1::static_pointer_cast; -using std::tr1::dynamic_pointer_cast; -using std::cout; -using std::endl; -using std::ostringstream; -using std::string; - -static string requesterName("longArrayGet"); -static string request("value,timeStamp,alarm"); -static epics::pvData::Mutex printMutex; - -class LongArrayChannelRequester; -typedef std::tr1::shared_ptr LongArrayChannelRequesterPtr; -class LongArrayChannelGetRequester; -typedef std::tr1::shared_ptr LongArrayChannelGetRequesterPtr; - -class LongArrayChannelRequester : - virtual public ChannelRequester, - public std::tr1::enable_shared_from_this -{ -public: - LongArrayChannelRequester( - LongArrayChannelGetPtr const & longArrayChannelGet) - : longArrayChannelGet(longArrayChannelGet), - isDestroyed(false) - {} - virtual ~LongArrayChannelRequester(){} - virtual void destroy() - { - Lock guard(mutex); - if(isDestroyed) return; - isDestroyed = true; - longArrayChannelGet.reset(); - } - virtual string getRequesterName() { return requesterName;} - virtual void message(string const & message, MessageType messageType) - { - Lock guard(printMutex); - cout << requesterName << " message " << message << endl; - } - virtual void channelCreated( - const Status& status, - Channel::shared_pointer const & channel); - virtual void channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState); -private: - LongArrayChannelRequesterPtr getPtrSelf() - { - return shared_from_this(); - } - LongArrayChannelGetPtr longArrayChannelGet; - bool isDestroyed; - Mutex mutex; -}; - - -class LongArrayChannelGetRequester : - virtual public ChannelGetRequester, - public std::tr1::enable_shared_from_this -{ -public: - LongArrayChannelGetRequester( - LongArrayChannelGetPtr const & longArrayChannelGet) - : longArrayChannelGet(longArrayChannelGet), - isDestroyed(false) - {} - virtual ~LongArrayChannelGetRequester(){} - virtual void destroy() - { - Lock guard(mutex); - if(isDestroyed) return; - isDestroyed = true; - longArrayChannelGet.reset(); - } - virtual string getRequesterName() { return requesterName;} - virtual void message(string const & message, MessageType messageType) - { - Lock guard(printMutex); - cout << requesterName << " message " << message << endl; - } - virtual void channelGetConnect( - Status const & status, - ChannelGet::shared_pointer const & channelGet, - StructureConstPtr const &structure); - virtual void getDone( - Status const & status, - ChannelGet::shared_pointer const & channelGet, - PVStructurePtr const &pvStructure, - BitSetPtr const & bitSet); -private: - LongArrayChannelGetRequesterPtr getPtrSelf() - { - return shared_from_this(); - } - LongArrayChannelGetPtr longArrayChannelGet; - bool isDestroyed; - Mutex mutex; -}; - - - -class LongArrayChannelGet : - public std::tr1::enable_shared_from_this, - public epicsThreadRunable -{ -public: - LongArrayChannelGet( - string providerName, - string channelName, - int iterBetweenCreateChannel, - int iterBetweenCreateChannelGet, - double delayTime) - : providerName(providerName), - channelName(channelName), - iterBetweenCreateChannel(iterBetweenCreateChannel), - iterBetweenCreateChannelGet(iterBetweenCreateChannelGet), - delayTime(delayTime), - isDestroyed(false), - runReturned(false), - threadName("longArrayGet") - {} - virtual ~LongArrayChannelGet(){} - bool init(); - virtual void destroy(); - virtual void run(); - void message(string const & message, MessageType messageType) - { - Lock guard(printMutex); - cout << requesterName << " message " << message << endl; - } - virtual void channelCreated( - const Status& status, - Channel::shared_pointer const & channel); - virtual void channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState); - virtual void channelGetConnect( - Status const & status, - ChannelGet::shared_pointer const & channelGet, - StructureConstPtr const &structure); - virtual void getDone( - Status const & status, - ChannelGet::shared_pointer channelGet, - PVStructurePtr const &pvStructure, - BitSetPtr const & bitSet); -private: - LongArrayChannelGetPtr getPtrSelf() - { - return shared_from_this(); - } - size_t checkResult(); - string providerName; - string channelName; - int iterBetweenCreateChannel; - int iterBetweenCreateChannelGet; - double delayTime; - bool isDestroyed; - bool runReturned; - std::string threadName; - Status status; - Event event; - Mutex mutex; - std::auto_ptr thread; - Channel::shared_pointer channel; - ChannelGet::shared_pointer channelGet; - PVStructurePtr pvStructure; - BitSetPtr bitSet; - LongArrayChannelRequesterPtr longArrayChannelRequester; - LongArrayChannelGetRequesterPtr longArrayChannelGetRequester; -}; - -void LongArrayChannelRequester::channelCreated( - const Status& status, - Channel::shared_pointer const & channel) -{ - Lock guard(mutex); - if(isDestroyed) return; - longArrayChannelGet->channelCreated(status,channel); -} - -void LongArrayChannelRequester::channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState) -{ - string mess(Channel::ConnectionStateNames[connectionState]); - message(mess,infoMessage); - Lock guard(mutex); - if(isDestroyed) return; - longArrayChannelGet->channelStateChange(channel,connectionState); -} - -void LongArrayChannelGetRequester::channelGetConnect( - Status const & status, - ChannelGet::shared_pointer const & channelGet, - StructureConstPtr const &structure) -{ - Lock guard(mutex); - if(isDestroyed) return; - longArrayChannelGet->channelGetConnect( - status,channelGet,structure); -} - -void LongArrayChannelGetRequester::getDone( - Status const & status, - ChannelGet::shared_pointer const & channelGet, - PVStructurePtr const &pvStructure, - BitSetPtr const & bitSet) -{ - Lock guard(mutex); - if(isDestroyed) return; - longArrayChannelGet->getDone(status,channelGet,pvStructure,bitSet); -} - -void LongArrayChannelGet::channelCreated( - const Status& status, - Channel::shared_pointer const & channel) -{ - if(!status.isOK()) message(status.getMessage(),errorMessage); - this->status = status; - this->channel = channel; - event.signal(); -} - -void LongArrayChannelGet::channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState) -{ - MessageType messageType = - (connectionState==Channel::CONNECTED ? infoMessage : errorMessage); - message("channelStateChange",messageType); -} -void LongArrayChannelGet::channelGetConnect( - Status const & status, - ChannelGet::shared_pointer const & channelGet, - StructureConstPtr const &structure) -{ - this->status = status; - if(!status.isOK()) { - message(status.getMessage(),errorMessage); - event.signal(); - return; - } - this->channelGet = channelGet; - bool structureOK(true); - PVStructurePtr pvStructure = getPVDataCreate()->createPVStructure(structure); - PVFieldPtr pvField = pvStructure->getSubField("timeStamp"); - if(!pvField) structureOK = false; - pvField = pvStructure->getSubField("value"); - if(!pvField) { - structureOK = false; - } else { - FieldConstPtr field = pvField->getField(); - if(field->getType()!=scalarArray) { - structureOK = false; - } else { - ScalarArrayConstPtr scalarArray = dynamic_pointer_cast(field); - if(scalarArray->getElementType()!=pvLong) structureOK = false; - } - } - if(!structureOK) { - string mess("channelGetConnect: illegal structure"); - message(mess,errorMessage); - this->status = Status(Status::STATUSTYPE_ERROR,mess); - } - event.signal(); -} - - -bool LongArrayChannelGet::init() -{ - ChannelProvider::shared_pointer channelProvider = - getChannelProviderRegistry()->getProvider(providerName); - if(!channelProvider) { - cout << "provider " << providerName << " not found" << endl; - return false; - } - longArrayChannelRequester.reset(new LongArrayChannelRequester(getPtrSelf())); - channel = channelProvider->createChannel( - channelName, - longArrayChannelRequester); - event.wait(); - channelProvider.reset(); - if(!status.isOK()) return false; - CreateRequest::shared_pointer createRequest = CreateRequest::create(); - PVStructurePtr pvRequest = createRequest->createRequest(request); - if(!pvRequest) { - cout << "request logic error " << createRequest->getMessage() << endl; - return false; - } - longArrayChannelGetRequester.reset(new LongArrayChannelGetRequester(getPtrSelf())); - channelGet = channel->createChannelGet( - longArrayChannelGetRequester, - pvRequest); - event.wait(); - if(!status.isOK()) return false; - thread = std::auto_ptr(new epicsThread( - *this, - threadName.c_str(), - epicsThreadGetStackSize(epicsThreadStackSmall), - epicsThreadPriorityLow)); - thread->start(); - event.signal(); - return true; -} - -void LongArrayChannelGet::destroy() -{ - if(isDestroyed) return; - isDestroyed = true; - event.signal(); - while(true) { - if(runReturned) break; - epicsThreadSleep(.01); - } - if(longArrayChannelRequester) { - longArrayChannelRequester->destroy(); - } - if(longArrayChannelGetRequester) { - longArrayChannelGetRequester->destroy(); - } - thread->exitWait(); - channel->destroy(); - channelGet.reset(); - channel.reset(); -} - - -void LongArrayChannelGet::run() -{ - while(true) { - event.wait(); - if(isDestroyed) { - runReturned = true; - return; - } - TimeStamp timeStamp; - TimeStamp timeStampLast; - timeStampLast.getCurrent(); - int numChannelGet = 0; - int numChannelCreate = 0; - size_t nElements = 0; - while(true) { - channelGet->get(); - event.wait(); - if(isDestroyed) { - runReturned = true; - return; - } - size_t latestSize = checkResult(); - nElements += latestSize; - timeStamp.getCurrent(); - double diff = TimeStamp::diff(timeStamp,timeStampLast); - if(diff>=1.0) { - ostringstream out; - out << "get"; - double elementsPerSec = nElements; - elementsPerSec /= diff; - if(elementsPerSec>10.0e9) { - elementsPerSec /= 1e9; - out << " gigaElements/sec " << elementsPerSec; - } else if(elementsPerSec>10.0e6) { - elementsPerSec /= 1e6; - out << " megaElements/sec " << elementsPerSec; - } else if(elementsPerSec>10.0e3) { - elementsPerSec /= 1e3; - out << " kiloElements/sec " << elementsPerSec; - } else { - out << " Elements/sec " << elementsPerSec; - } - cout << out.str() << endl; - timeStampLast = timeStamp; - nElements = 0; - } - if(delayTime>0.0) epicsThreadSleep(delayTime); - if(isDestroyed) { - runReturned = true; - return; - } - ++numChannelCreate; - bool createGet = false; - if(iterBetweenCreateChannel!=0) { - if(numChannelCreate>=iterBetweenCreateChannel) { - longArrayChannelRequester->destroy(); - channel->destroy(); - ChannelProvider::shared_pointer channelProvider = - getChannelProviderRegistry()->getProvider(providerName); - longArrayChannelRequester.reset(new LongArrayChannelRequester(getPtrSelf())); - channel = channelProvider->createChannel( - channelName, - longArrayChannelRequester); - event.wait(); - channelProvider.reset(); - if(!status.isOK()) { - message(status.getMessage(),errorMessage); - return; - } - cout<< "createChannel success" << endl; - createGet = true; - numChannelCreate = 0; - } - } - ++numChannelGet; - if(iterBetweenCreateChannelGet!=0) { - if(numChannelGet>=iterBetweenCreateChannelGet) createGet = true; - } - if(createGet) { - numChannelGet = 0; - longArrayChannelGetRequester->destroy(); - channelGet->destroy(); - CreateRequest::shared_pointer createRequest = - CreateRequest::create(); - PVStructurePtr pvRequest = - createRequest->createRequest(request); - if(!pvRequest) { - cout << "request logic error " << createRequest->getMessage() << endl; - return ; - } - longArrayChannelGetRequester.reset(new LongArrayChannelGetRequester(getPtrSelf())); - channelGet = channel->createChannelGet( - longArrayChannelGetRequester, - pvRequest); - event.wait(); - if(!status.isOK()) { - message(status.getMessage(),errorMessage); - return; - } - cout<< "createChannelGet success" << endl; - } - } - } -} - -void LongArrayChannelGet::getDone( - Status const & status, - ChannelGet::shared_pointer channelGet, - PVStructurePtr const &pvStructure, - BitSetPtr const & bitSet) -{ - this->pvStructure = pvStructure; - this->bitSet = bitSet; - event.signal(); -} - -size_t LongArrayChannelGet::checkResult() -{ - PVLongArrayPtr pvValue; - if(!status.isOK()) { - message(status.getMessage(),errorMessage); - return 0; - } - pvValue = dynamic_pointer_cast(pvStructure->getSubField("value")); - if(!bitSet->get(pvValue->getFieldOffset())) { - return 0; - } - bitSet->clear(); - shared_vector data = pvValue->view(); - if(data.size()>0) { - int64 first = data[0]; - int64 last = data[data.size()-1]; - if(first!=last) { - cout << "error first=" << first << " last=" << last << endl; - } - } - return data.size(); -} - - -LongArrayGetPtr LongArrayGet::create( - string const &providerName, - string const & channelName, - int iterBetweenCreateChannel, - int iterBetweenCreateChannelGet, - double delayTime) -{ - LongArrayGetPtr longArrayGet( - new LongArrayGet( - providerName, - channelName, - iterBetweenCreateChannel, - iterBetweenCreateChannelGet, - delayTime)); - if(!longArrayGet->init()) longArrayGet.reset(); - return longArrayGet; -} - -LongArrayGet::LongArrayGet( - string const &providerName, - string const & channelName, - int iterBetweenCreateChannel, - int iterBetweenCreateChannelGet, - double delayTime) -: providerName(providerName), - channelName(channelName), - iterBetweenCreateChannel(iterBetweenCreateChannel), - iterBetweenCreateChannelGet(iterBetweenCreateChannelGet), - delayTime(delayTime) -{} - - -LongArrayGet::~LongArrayGet() {} - -bool LongArrayGet::init() -{ - longArrayChannelGet = LongArrayChannelGetPtr(new LongArrayChannelGet( - providerName, - channelName, - iterBetweenCreateChannel, - iterBetweenCreateChannelGet, - delayTime)); - return longArrayChannelGet->init(); -} - -void LongArrayGet::destroy() -{ - longArrayChannelGet->destroy(); - longArrayChannelGet.reset(); -} - -}} - - diff --git a/arrayPerformance/src/longArrayGet.h b/arrayPerformance/src/longArrayGet.h deleted file mode 100644 index 85b00be..0000000 --- a/arrayPerformance/src/longArrayGet.h +++ /dev/null @@ -1,81 +0,0 @@ -/* longArrayGet.h */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.09 - */ -#ifndef LONGARRAYGET_H -#define LONGARRAYGET_H - - -#ifdef epicsExportSharedSymbols -# define longarraygetEpicsExportSharedSymbols -# undef epicsExportSharedSymbols -#endif - -#include -#include -#include -#include -#include -#include - - -#ifdef longarraygetEpicsExportSharedSymbols -# define epicsExportSharedSymbols -# undef longarraygetEpicsExportSharedSymbols -# include -#endif - -namespace epics { namespace pvDatabase { - - -class LongArrayGet; -typedef std::tr1::shared_ptr LongArrayGetPtr; - - -class LongArrayChannelGet; -typedef std::tr1::shared_ptr LongArrayChannelGetPtr; - -class epicsShareClass LongArrayGet : - public std::tr1::enable_shared_from_this -{ -public: - POINTER_DEFINITIONS(LongArrayGet); - static LongArrayGetPtr create( - std::string const & providerName, - std::string const & channelName, - int iterBetweenCreateChannel = 0, - int iterBetweenCreateChannelGet = 0, - double delayTime = 0.0); - ~LongArrayGet(); - void destroy(); -private: - LongArrayGetPtr getPtrSelf() - { - return shared_from_this(); - } - LongArrayGet( - std::string const & providerName, - std::string const & channelName, - int iterBetweenCreateChannel = 0, - int iterBetweenCreateChannelGet = 0, - double delayTime = 0.0); - bool init(); - - std::string providerName; - std::string channelName; - int iterBetweenCreateChannel; - int iterBetweenCreateChannelGet; - double delayTime; - LongArrayChannelGetPtr longArrayChannelGet; -}; - - -}} - -#endif /* LONGARRAYGET_H */ diff --git a/arrayPerformance/src/longArrayGetMain.cpp b/arrayPerformance/src/longArrayGetMain.cpp deleted file mode 100644 index 39b41bb..0000000 --- a/arrayPerformance/src/longArrayGetMain.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/*longArrayGetMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.10 - */ - -/* Author: Marty Kraimer */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace std; -using std::tr1::static_pointer_cast; -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; - -int main(int argc,char *argv[]) -{ - string channelName("arrayPerformance"); - int iterBetweenCreateChannel = 0; - int iterBetweenCreateChannelGet = 0; - double delayTime = 1.0; - if(argc==2 && string(argv[1])==string("-help")) { - cout << "longArrayGetMain channelName "; - cout << "iterBetweenCreateChannel iterBetweenCreateChannelGet delayTime" << endl; - cout << "default" << endl; - cout << "longArrayGetMain " << channelName << " "; - cout << iterBetweenCreateChannel << " "; - cout << iterBetweenCreateChannelGet << " "; - cout << delayTime << endl; - return 0; - } - ClientFactory::start(); - if(argc>1) channelName = argv[1]; - if(argc>2) iterBetweenCreateChannel = strtol(argv[2],0,0); - if(argc>3) iterBetweenCreateChannelGet = strtol(argv[3],0,0); - if(argc>4) delayTime = atof(argv[4]); - cout << "longArrayGetMain " << channelName << " "; - cout << iterBetweenCreateChannel << " "; - cout << iterBetweenCreateChannelGet << " "; - cout << delayTime << endl; - LongArrayGetPtr longArrayGet - = LongArrayGet::create( - "pvAccess", - channelName, - iterBetweenCreateChannel, - iterBetweenCreateChannelGet, - delayTime); - cout << "longArrayGet\n"; - string str; - while(true) { - cout << "Type exit to stop: \n"; - getline(cin,str); - if(str.compare("exit")==0) break; - - } - longArrayGet->destroy(); - longArrayGet.reset(); - double xxx = 1.0; - if(xxx -#include - -#define epicsExportSharedSymbols -#include - - -namespace epics { namespace pvDatabase { - -using namespace epics::pvData; -using namespace epics::pvAccess; -using std::tr1::static_pointer_cast; -using std::tr1::dynamic_pointer_cast; -using std::cout; -using std::endl; -using std::string; -using std::ostringstream; - -static string requesterName("longArrayMonitor"); - -static void messagePvt(string const & message, MessageType messageType) -{ - cout << requesterName << " message " << message << endl; -} - -class LAMChannelRequester : - public ChannelRequester -{ -public: - LAMChannelRequester(LongArrayMonitorPtr const &longArrayMonitor) - : longArrayMonitor(longArrayMonitor) - {} - virtual ~LAMChannelRequester(){} - virtual void destroy(){longArrayMonitor.reset();} - virtual string getRequesterName() { return requesterName;} - virtual void message(string const & message, MessageType messageType) - { messagePvt(message,messageType);} - virtual void channelCreated(const Status& status, Channel::shared_pointer const & channel); - virtual void channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState); -private: - LongArrayMonitorPtr longArrayMonitor; -}; - -void LAMChannelRequester::channelCreated(const Status& status, Channel::shared_pointer const & channel) -{ - if(!status.isOK()) messagePvt(status.getMessage(),errorMessage); - longArrayMonitor->status = status; - longArrayMonitor->channel = channel; - longArrayMonitor->event.signal(); -} - -void LAMChannelRequester::channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) -{ - MessageType messageType = (connectionState==Channel::CONNECTED ? infoMessage : errorMessage); - messagePvt("channelStateChange",messageType); -} - - -class LAMMonitorRequester : - public MonitorRequester, - public epicsThreadRunable -{ -public: - LAMMonitorRequester(LongArrayMonitorPtr const &longArrayMonitor,double waitTime) - : longArrayMonitor(longArrayMonitor), - waitTime(waitTime), - isDestroyed(false), - runReturned(false), - threadName("longArrayMonitor") - {} - virtual ~LAMMonitorRequester(){} - void init(); - virtual void destroy(); - virtual void run(); - virtual string getRequesterName() { return requesterName;} - virtual void message(string const & message, MessageType messageType) - { messagePvt(message,messageType);} - virtual void monitorConnect(Status const & status, - MonitorPtr const & monitor, StructureConstPtr const & structure); - virtual void monitorEvent(MonitorPtr const & monitor); - virtual void unlisten(MonitorPtr const & monitor); -private: - LongArrayMonitorPtr longArrayMonitor; - double waitTime; - bool isDestroyed; - bool runReturned; - std::string threadName; - Event event; - Mutex mutex; - std::auto_ptr thread; -}; - -void LAMMonitorRequester::init() -{ - thread = std::auto_ptr(new epicsThread( - *this, - threadName.c_str(), - epicsThreadGetStackSize(epicsThreadStackSmall), - epicsThreadPriorityLow)); - thread->start(); -} - -void LAMMonitorRequester::destroy() -{ - if(isDestroyed) return; - isDestroyed = true; - event.signal(); - while(true) { - if(runReturned) break; - epicsThreadSleep(.01); - } - thread->exitWait(); - longArrayMonitor.reset(); -} - - -void LAMMonitorRequester::monitorConnect(Status const & status, - MonitorPtr const & monitor, StructureConstPtr const & structure) -{ - longArrayMonitor->status = status; - longArrayMonitor->monitor = monitor; - if(!status.isOK()) { - messagePvt(status.getMessage(),errorMessage); - longArrayMonitor->event.signal(); - return; - } - bool structureOK(true); - FieldConstPtr field = structure->getField("timeStamp"); - if(!field) structureOK = false; - field = structure->getField("value"); - if(!field) { - structureOK = false; - } else { - if(field->getType()!=scalarArray) { - structureOK = false; - } else { - ScalarArrayConstPtr scalarArray = dynamic_pointer_cast(field); - if(scalarArray->getElementType()!=pvLong) structureOK = false; - } - } - if(!structureOK) { - string message("monitorConnect: illegal structure"); - messagePvt(message,errorMessage); - longArrayMonitor->status = Status(Status::STATUSTYPE_ERROR,message); - } - longArrayMonitor->event.signal(); -} - -void LAMMonitorRequester::run() -{ - PVLongArrayPtr pvValue; - PVTimeStamp pvTimeStamp; - TimeStamp timeStamp; - TimeStamp timeStampLast; - timeStampLast.getCurrent(); - size_t nElements = 0; - int nSinceLastReport = 0; - while(true) { - event.wait(); - if(isDestroyed) { - runReturned = true; - return; - } - while(true) { - MonitorElementPtr monitorElement; - PVStructurePtr pvStructure; - { - Lock xx(mutex); - monitorElement = longArrayMonitor->monitor->poll(); - if(monitorElement) pvStructure = monitorElement->pvStructurePtr; - } - if(!monitorElement) break; - if(waitTime>0.0) epicsThreadSleep(waitTime); - pvTimeStamp.attach(pvStructure->getSubField("timeStamp")); - pvTimeStamp.get(timeStamp); - pvValue = dynamic_pointer_cast(pvStructure->getSubField("value")); - shared_vector data = pvValue->view(); - if(data.size()>0) { - nElements += data.size(); - int64 first = data[0]; - int64 last = data[data.size()-1]; - if(first!=last) { - cout << "error first=" << first << " last=" << last << endl; - } - double diff = TimeStamp::diff(timeStamp,timeStampLast); - if(diff>=1.0) { - ostringstream out; - out << " monitors/sec " << nSinceLastReport << " "; - out << "first " << first << " last " << last ; - BitSetPtr changed = monitorElement->changedBitSet; - BitSetPtr overrun = monitorElement->overrunBitSet; - out << " changed " << *changed; - out << " overrun " << *overrun; - double elementsPerSec = nElements; - elementsPerSec /= diff; - if(elementsPerSec>10.0e9) { - elementsPerSec /= 1e9; - out << " gigaElements/sec " << elementsPerSec; - } else if(elementsPerSec>10.0e6) { - elementsPerSec /= 1e6; - out << " megaElements/sec " << elementsPerSec; - } else if(elementsPerSec>10.0e3) { - elementsPerSec /= 1e3; - out << " kiloElements/sec " << elementsPerSec; - } else { - out << " Elements/sec " << elementsPerSec; - } - cout << out.str() << endl; - timeStampLast = timeStamp; - nSinceLastReport = 0; - nElements = 0; - } - ++nSinceLastReport; - } else { - cout << "size = 0" << endl; - } - longArrayMonitor->monitor->release(monitorElement); - } - } -} - -void LAMMonitorRequester::monitorEvent(MonitorPtr const & monitor) -{ - event.signal(); -} - -void LAMMonitorRequester::unlisten(MonitorPtr const & monitor) -{ - messagePvt("unlisten called",errorMessage); -} - - -LongArrayMonitorPtr LongArrayMonitor::create( - string const &providerName, - string const & channelName, - int queueSize, - double waitTime) -{ - LongArrayMonitorPtr longArrayMonitor(new LongArrayMonitor()); - if(!longArrayMonitor->init(providerName,channelName,queueSize,waitTime)) longArrayMonitor.reset(); - return longArrayMonitor; -} - -LongArrayMonitor::LongArrayMonitor() {} - -LongArrayMonitor::~LongArrayMonitor() {} - -bool LongArrayMonitor::init( - string const &providerName, - string const &channelName, - int queueSize, - double waitTime) -{ - channelRequester = LAMChannelRequesterPtr(new LAMChannelRequester(getPtrSelf())); - monitorRequester = LAMMonitorRequesterPtr(new LAMMonitorRequester(getPtrSelf(),waitTime)); - monitorRequester->init(); - ChannelProvider::shared_pointer channelProvider = - getChannelProviderRegistry()->getProvider(providerName); - if(!channelProvider) { - cout << "provider " << providerName << " not found" << endl; - return false; - } - channel = channelProvider->createChannel(channelName,channelRequester,0); - event.wait(); - if(!status.isOK()) return false; - string request("record[queueSize="); - char buff[20]; - sprintf(buff,"%d",queueSize); - request += buff; - request += "]field(value,timeStamp,alarm)"; - CreateRequest::shared_pointer createRequest = CreateRequest::create(); - PVStructurePtr pvRequest = createRequest->createRequest(request); - if(!pvRequest) { - cout << "request logic error " << createRequest->getMessage() << endl; - return false; - } - monitor = channel->createMonitor(monitorRequester,pvRequest); - event.wait(); - if(!status.isOK()) return false; - return true; -} - -void LongArrayMonitor::start() -{ - monitor->start(); -} - -void LongArrayMonitor::stop() -{ - monitor->stop(); -} - -void LongArrayMonitor::destroy() -{ - monitorRequester->destroy(); - monitorRequester.reset(); - monitor->destroy(); - monitor.reset(); - channel->destroy(); - channel.reset(); - channelRequester->destroy(); - channelRequester.reset(); -} - -}} - - diff --git a/arrayPerformance/src/longArrayMonitor.h b/arrayPerformance/src/longArrayMonitor.h deleted file mode 100644 index c84d31f..0000000 --- a/arrayPerformance/src/longArrayMonitor.h +++ /dev/null @@ -1,85 +0,0 @@ -/* longArrayMonitor.h */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.09 - */ -#ifndef LONGARRAYMONITOR_H -#define LONGARRAYMONITOR_H - -#ifdef epicsExportSharedSymbols -# define longarraymonitorEpicsExportSharedSymbols -# undef epicsExportSharedSymbols -#endif - -#include -#include -#include -#include -#include -#include - -#ifdef longarraymonitorEpicsExportSharedSymbols -# define epicsExportSharedSymbols -# undef longarraymonitorEpicsExportSharedSymbols -# include -#endif - - -namespace epics { namespace pvDatabase { - - -class LongArrayMonitor; -typedef std::tr1::shared_ptr LongArrayMonitorPtr; - -class LAMChannelRequester; -typedef std::tr1::shared_ptr LAMChannelRequesterPtr; - -class LAMMonitorRequester; -typedef std::tr1::shared_ptr LAMMonitorRequesterPtr; - -class epicsShareClass LongArrayMonitor : - public std::tr1::enable_shared_from_this -{ -public: - POINTER_DEFINITIONS(LongArrayMonitor); - static LongArrayMonitorPtr create( - std::string const & providerName, - std::string const & channelName, - int queueSize = 1, - double waitTime = 0.0); - ~LongArrayMonitor(); - void start(); - void stop(); - void destroy(); -private: - static epics::pvData::Mutex printMutex; - bool init( - std::string const & providerName, - std::string const & channelName, - int queueSize, - double waitTime); - LongArrayMonitorPtr getPtrSelf() - { - return shared_from_this(); - } - LongArrayMonitor(); - - LAMChannelRequesterPtr channelRequester; - LAMMonitorRequesterPtr monitorRequester; - epics::pvAccess::Channel::shared_pointer channel; - epics::pvData::Monitor::shared_pointer monitor; - epics::pvData::Event event; - epics::pvData::Status status; - friend class LAMChannelRequester; - friend class LAMMonitorRequester; -}; - - -}} - -#endif /* LONGARRAYMONITOR_H */ diff --git a/arrayPerformance/src/longArrayMonitorMain.cpp b/arrayPerformance/src/longArrayMonitorMain.cpp deleted file mode 100644 index 74a041f..0000000 --- a/arrayPerformance/src/longArrayMonitorMain.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/*longArrayMonitorMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.10 - */ - -/* Author: Marty Kraimer */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace std; -using std::tr1::static_pointer_cast; -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; - -int main(int argc,char *argv[]) -{ - string channelName("arrayPerformance"); - int queueSize = 2; - double waitTime = 0.0; - if(argc==2 && string(argv[1])==string("-help")) { - cout << "longArrayMonitorMain channelName queueSize waitTime" << endl; - cout << "default" << endl; - cout << "longArrayMonitorMain " << channelName << " "; - cout << queueSize << " "; - cout << "0.0" << endl; - return 0; - } - ClientFactory::start(); - if(argc>1) channelName = argv[1]; - if(argc>2) queueSize = strtol(argv[2],0,0); - if(argc>3) waitTime = atof(argv[3]); - cout << "longArrayMonitorMain " << channelName << " "; - cout << queueSize << " "; - cout << waitTime << endl; - LongArrayMonitorPtr longArrayMonitor - = LongArrayMonitor::create("pvAccess",channelName,queueSize,waitTime); - longArrayMonitor->start(); - cout << "longArrayMonitor\n"; - string str; - while(true) { - cout << "Type exit to stop: \n"; - getline(cin,str); - if(str.compare("exit")==0) break; - - } - longArrayMonitor->destroy(); - longArrayMonitor.reset(); - ClientFactory::stop(); - epicsThreadSleep(1.0); - return 0; -} - diff --git a/arrayPerformance/src/longArrayPut.cpp b/arrayPerformance/src/longArrayPut.cpp deleted file mode 100644 index 4aca1bb..0000000 --- a/arrayPerformance/src/longArrayPut.cpp +++ /dev/null @@ -1,383 +0,0 @@ -/* longArrayPut.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.09 - */ - -#include -#include - -#define epicsExportSharedSymbols -#include - -namespace epics { namespace pvDatabase { - -using namespace epics::pvData; -using namespace epics::pvAccess; -using std::tr1::static_pointer_cast; -using std::tr1::dynamic_pointer_cast; -using std::cout; -using std::endl; -using std::ostringstream; -using std::string; - -static string requesterName("longArrayPut"); -static string request("value"); -static epics::pvData::Mutex printMutex; - -class LongArrayChannelPut : - virtual public ChannelRequester, - virtual public ChannelPutRequester, - public std::tr1::enable_shared_from_this, - public epicsThreadRunable -{ -public: - LongArrayChannelPut( - string providerName, - string channelName, - size_t arraySize, - int iterBetweenCreateChannel, - int iterBetweenCreateChannelPut, - double delayTime) - : providerName(providerName), - channelName(channelName), - arraySize(arraySize), - iterBetweenCreateChannel(iterBetweenCreateChannel), - iterBetweenCreateChannelPut(iterBetweenCreateChannelPut), - delayTime(delayTime), - isDestroyed(false), - runReturned(false), - threadName("longArrayPut") - {} - virtual ~LongArrayChannelPut(){} - bool init(); - virtual void destroy(); - virtual void run(); - virtual string getRequesterName() { return requesterName;} - virtual void message(string const & message, MessageType messageType) - { - Lock guard(printMutex); - cout << requesterName << " message " << message << endl; - } - virtual void channelCreated( - const Status& status, - Channel::shared_pointer const & channel); - virtual void channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState); - virtual void channelPutConnect( - Status const & status, - ChannelPut::shared_pointer const & channelPut, - StructureConstPtr const &structure); - virtual void putDone( - Status const & status, - ChannelPut::shared_pointer const & channelPut); - virtual void getDone( - Status const & status, - ChannelPut::shared_pointer const & channelPut, - PVStructurePtr const &pvStructure, - BitSetPtr const & bitSet){} -private: - LongArrayChannelPutPtr getPtrSelf() - { - return shared_from_this(); - } - string providerName; - string channelName; - size_t arraySize; - int iterBetweenCreateChannel; - int iterBetweenCreateChannelPut; - double delayTime; - bool isDestroyed; - bool runReturned; - std::string threadName; - Status status; - Event event; - Mutex mutex; - std::auto_ptr thread; - Channel::shared_pointer channel; - ChannelPut::shared_pointer channelPut; - PVStructurePtr pvStructure; - PVLongArrayPtr pvLongArray; - BitSetPtr bitSet; -}; - -bool LongArrayChannelPut::init() -{ - ChannelProvider::shared_pointer channelProvider = getChannelProviderRegistry()->getProvider(providerName); - if(!channelProvider) { - cout << "provider " << providerName << " not found" << endl; - return false; - } - channel = channelProvider->createChannel(channelName,getPtrSelf(),0); - event.wait(); - if(!status.isOK()) return false; - CreateRequest::shared_pointer createRequest = CreateRequest::create(); - PVStructurePtr pvRequest = createRequest->createRequest(request); - if(!pvRequest) { - cout << "request logic error " << createRequest->getMessage() << endl; - return false; - } - channelPut = channel->createChannelPut(getPtrSelf(),pvRequest); - event.wait(); - if(!status.isOK()) return false; - thread = std::auto_ptr(new epicsThread( - *this, - threadName.c_str(), - epicsThreadGetStackSize(epicsThreadStackSmall), - epicsThreadPriorityLow)); - thread->start(); - event.signal(); - return true; -} - -void LongArrayChannelPut::destroy() -{ - if(isDestroyed) return; - isDestroyed = true; - event.signal(); - while(true) { - if(runReturned) break; - epicsThreadSleep(.01); - } - thread->exitWait(); - channel->destroy(); - channelPut.reset(); - channel.reset(); -} - -void LongArrayChannelPut::channelCreated( - const Status& status, - Channel::shared_pointer const & channel) -{ - if(!status.isOK()) message(status.getMessage(),errorMessage); - this->status = status; - this->channel = channel; - event.signal(); -} - -void LongArrayChannelPut::channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState) -{ - MessageType messageType = - (connectionState==Channel::CONNECTED ? infoMessage : errorMessage); - message("channelStateChange",messageType); -} - - -void LongArrayChannelPut::channelPutConnect( - Status const & status, - ChannelPut::shared_pointer const & channelPut, - StructureConstPtr const &structure) -{ - this->status = status; - if(!status.isOK()) { - message(status.getMessage(),errorMessage); - event.signal(); - return; - } - this->channelPut = channelPut; - pvStructure = getPVDataCreate()->createPVStructure(structure); - bitSet = BitSetPtr(new BitSet(pvStructure->getNumberFields())); - bool structureOK(true); - PVFieldPtr pvField = pvStructure->getSubField("value"); - if(!pvField) { - structureOK = false; - } else { - FieldConstPtr field = pvField->getField(); - if(field->getType()!=scalarArray) { - structureOK = false; - } else { - ScalarArrayConstPtr scalarArray = dynamic_pointer_cast(field); - if(scalarArray->getElementType()!=pvLong) structureOK = false; - } - } - if(!structureOK) { - string mess("channelPutConnect: illegal structure"); - message(mess,errorMessage); - this->status = Status(Status::STATUSTYPE_ERROR,mess); - } - pvLongArray = static_pointer_cast(pvField); - event.signal(); -} - -void LongArrayChannelPut::run() -{ - while(true) { - event.wait(); - if(isDestroyed) { - runReturned = true; - return; - } - TimeStamp timeStamp; - TimeStamp timeStampLast; - timeStampLast.getCurrent(); - int numChannelPut = 0; - int numChannelCreate = 0; - size_t nElements = 0; - while(true) { - nElements += sizeof(int64) * arraySize; - shared_vector xxx(arraySize,numChannelPut); - shared_vector data(freeze(xxx)); - pvLongArray->replace(data); - bitSet->set(pvLongArray->getFieldOffset()); - channelPut->put(pvStructure,bitSet); - event.wait(); - if(isDestroyed) { - runReturned = true; - return; - } - if(delayTime>0.0) epicsThreadSleep(delayTime); - if(isDestroyed) { - runReturned = true; - return; - } - timeStamp.getCurrent(); - double diff = TimeStamp::diff(timeStamp,timeStampLast); - if(diff>=1.0) { - ostringstream out; - out << "put numChannelPut " << numChannelPut; - out << " time " << diff ; - double elementsPerSec = nElements; - elementsPerSec /= diff; - if(elementsPerSec>10.0e9) { - elementsPerSec /= 1e9; - out << " gigaElements/sec " << elementsPerSec; - } else if(elementsPerSec>10.0e6) { - elementsPerSec /= 1e6; - out << " megaElements/sec " << elementsPerSec; - } else if(elementsPerSec>10.0e3) { - elementsPerSec /= 1e3; - out << " kiloElements/sec " << elementsPerSec; - } else { - out << " Elements/sec " << elementsPerSec; - } - cout << out.str() << endl; - timeStampLast = timeStamp; - nElements = 0; - } - ++numChannelCreate; - bool createPut = false; - if(iterBetweenCreateChannel!=0) { - if(numChannelCreate>=iterBetweenCreateChannel) { - channel->destroy(); - ChannelProvider::shared_pointer channelProvider = - getChannelProviderRegistry()->getProvider(providerName); - channel = channelProvider->createChannel( - channelName,getPtrSelf(),0); - event.wait(); - if(isDestroyed) { - runReturned = true; - return; - } - if(!status.isOK()) { - message(status.getMessage(),errorMessage); - return; - } - cout<< "createChannel success" << endl; - createPut = true; - numChannelCreate = 0; - } - } - ++numChannelPut; - if(iterBetweenCreateChannelPut!=0) { - if(numChannelPut>=iterBetweenCreateChannelPut) createPut = true; - } - if(createPut) { - numChannelPut = 0; - channelPut->destroy(); - CreateRequest::shared_pointer createRequest = CreateRequest::create(); - PVStructurePtr pvRequest = createRequest->createRequest(request); - if(!pvRequest) { - cout << "request logic error " << createRequest->getMessage() << endl; - return ; - } - channelPut = channel->createChannelPut(getPtrSelf(),pvRequest); - event.wait(); - if(isDestroyed) { - runReturned = true; - return; - } - if(!status.isOK()) { - message(status.getMessage(),errorMessage); - return; - } - cout<< "createChannelPut success" << endl; - } - } - } -} - -void LongArrayChannelPut::putDone( - Status const & status, - ChannelPut::shared_pointer const & channelPut) -{ - event.signal(); -} - - -LongArrayPutPtr LongArrayPut::create( - string const &providerName, - string const & channelName, - size_t arraySize, - int iterBetweenCreateChannel, - int iterBetweenCreateChannelPut, - double delayTime) -{ - LongArrayPutPtr longArrayPut( - new LongArrayPut( - providerName, - channelName, - arraySize, - iterBetweenCreateChannel, - iterBetweenCreateChannelPut, - delayTime)); - if(!longArrayPut->init()) longArrayPut.reset(); - return longArrayPut; -} - -LongArrayPut::LongArrayPut( - string const &providerName, - string const & channelName, - size_t arraySize, - int iterBetweenCreateChannel, - int iterBetweenCreateChannelPut, - double delayTime) -: providerName(providerName), - channelName(channelName), - arraySize(arraySize), - iterBetweenCreateChannel(iterBetweenCreateChannel), - iterBetweenCreateChannelPut(iterBetweenCreateChannelPut), - delayTime(delayTime) -{} - - -LongArrayPut::~LongArrayPut() {} - -bool LongArrayPut::init() -{ - longArrayChannelPut = LongArrayChannelPutPtr(new LongArrayChannelPut( - providerName, - channelName, - arraySize, - iterBetweenCreateChannel, - iterBetweenCreateChannelPut, - delayTime)); - return longArrayChannelPut->init(); -} - -void LongArrayPut::destroy() -{ - longArrayChannelPut->destroy(); - longArrayChannelPut.reset(); -} - -}} - - diff --git a/arrayPerformance/src/longArrayPut.h b/arrayPerformance/src/longArrayPut.h deleted file mode 100644 index efca9bb..0000000 --- a/arrayPerformance/src/longArrayPut.h +++ /dev/null @@ -1,84 +0,0 @@ -/* longArrayPut.h */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.09 - */ -#ifndef LONGARRAYPUT_H -#define LONGARRAYPUT_H - - -#ifdef epicsExportSharedSymbols -# define longarrayputEpicsExportSharedSymbols -# undef epicsExportSharedSymbols -#endif - -#include -#include -#include -#include -#include -#include - - -#ifdef longarrayputEpicsExportSharedSymbols -# define epicsExportSharedSymbols -# undef longarrayputEpicsExportSharedSymbols -# include -#endif - -namespace epics { namespace pvDatabase { - - -class LongArrayPut; -typedef std::tr1::shared_ptr LongArrayPutPtr; - - -class LongArrayChannelPut; -typedef std::tr1::shared_ptr LongArrayChannelPutPtr; - -class epicsShareClass LongArrayPut : - public std::tr1::enable_shared_from_this -{ -public: - POINTER_DEFINITIONS(LongArrayPut); - static LongArrayPutPtr create( - std::string const & providerName, - std::string const & channelName, - size_t arraySize = 100, - int iterBetweenCreateChannel = 0, - int iterBetweenCreateChannelPut = 0, - double delayTime = 0.0); - ~LongArrayPut(); - void destroy(); -private: - LongArrayPutPtr getPtrSelf() - { - return shared_from_this(); - } - LongArrayPut( - std::string const & providerName, - std::string const & channelName, - size_t arraySize, - int iterBetweenCreateChannel, - int iterBetweenCreateChannelPut, - double delayTime); - bool init(); - - std::string providerName; - std::string channelName; - size_t arraySize; - int iterBetweenCreateChannel; - int iterBetweenCreateChannelPut; - double delayTime; - LongArrayChannelPutPtr longArrayChannelPut; -}; - - -}} - -#endif /* LONGARRAYPUT_H */ diff --git a/arrayPerformance/src/longArrayPutMain.cpp b/arrayPerformance/src/longArrayPutMain.cpp deleted file mode 100644 index 1e28df3..0000000 --- a/arrayPerformance/src/longArrayPutMain.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/*longArrayPutMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.10 - */ - -/* Author: Marty Kraimer */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace std; -using std::tr1::static_pointer_cast; -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; - -int main(int argc,char *argv[]) -{ - string channelName("arrayPerformance"); - size_t arraySize = 10; - int iterBetweenCreateChannel = 0; - int iterBetweenCreateChannelPut = 0; - double delayTime = 1.0; - if(argc==2 && string(argv[1])==string("-help")) { - cout << "longArrayPutMain channelName arraySize "; - cout << "iterBetweenCreateChannel iterBetweenCreateChannelPut delayTime" << endl; - cout << "default" << endl; - cout << "longArrayPutMain " << channelName << " "; - cout << arraySize << " "; - cout << iterBetweenCreateChannel << " "; - cout << iterBetweenCreateChannelPut << " "; - cout << delayTime << endl; - return 0; - } - ClientFactory::start(); - if(argc>1) channelName = argv[1]; - if(argc>2) arraySize = strtoul(argv[2],0,0); - if(argc>3) iterBetweenCreateChannel = strtol(argv[3],0,0); - if(argc>4) iterBetweenCreateChannelPut = strtol(argv[4],0,0); - if(argc>5) delayTime = atof(argv[5]); - cout << "longArrayPutMain " << channelName << " "; - cout << arraySize << " "; - cout << iterBetweenCreateChannel << " "; - cout << iterBetweenCreateChannelPut << " "; - cout << delayTime << endl; - LongArrayPutPtr longArrayPut - = LongArrayPut::create( - "pvAccess", - channelName, - arraySize, - iterBetweenCreateChannel, - iterBetweenCreateChannelPut, - delayTime); - cout << "longArrayPut\n"; - string str; - while(true) { - cout << "Type exit to stop: \n"; - getline(cin,str); - if(str.compare("exit")==0) break; - - } - longArrayPut->destroy(); - longArrayPut.reset(); - double xxx = 1.0; - if(xxx -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -using namespace std; -using std::tr1::static_pointer_cast; -using namespace epics::pvData; - -class VectorPerformanceThread; -typedef std::tr1::shared_ptr VectorPerformanceThreadPtr; - -class VectorPerformanceThread : - public epicsThreadRunable -{ -public: - VectorPerformanceThread(int threadNumber,size_t size,double delay); - virtual ~VectorPerformanceThread(){}; - void init(); - void start(); - virtual void run(); - void destroy(); -private: - bool isDestroyed; - bool runReturned; - int threadNumber; - size_t size; - epics::pvData::int64 value; - double delay; - Mutex mutex; - std::vector vector; - std::auto_ptr thread; -}; - -VectorPerformanceThread::VectorPerformanceThread( - int threadNumber,size_t size,double delay) -: - isDestroyed(false), - runReturned(false), - threadNumber(threadNumber), - size(size), - value(0), - delay(delay) -{} - -void VectorPerformanceThread::init() -{ - vector.resize(size); - thread = std::auto_ptr(new epicsThread( - *this, - "vectorPerform", - epicsThreadGetStackSize(epicsThreadStackSmall), - epicsThreadPriorityHigh)); -} - -void VectorPerformanceThread::start() -{ - thread->start(); -} - -void VectorPerformanceThread::destroy() -{ - Lock lock(mutex); - if(isDestroyed) return; - isDestroyed = true; - while(true) { - if(runReturned) break; - lock.unlock(); - epicsThreadSleep(.01); - lock.lock(); - } - thread->exitWait(); - thread.reset(); -} - - -void VectorPerformanceThread::run() -{ - TimeStamp timeStamp; - TimeStamp timeStampLast; - timeStampLast.getCurrent(); - int nSinceLastReport = 0; - while(true) { - if(delay>0.0) epicsThreadSleep(delay); - { - Lock lock(mutex); - if(isDestroyed) { -cout << " found isDestroyed " << threadNumber << endl; - runReturned = true; - return; - } - } - timeStamp.getCurrent(); - double diff = TimeStamp::diff(timeStamp,timeStampLast); - if(diff>=1.0) { - cout << "thread" << threadNumber; - cout << " value " << value; - cout << " time " << diff; - double iterations = nSinceLastReport; - iterations /= diff; - cout << " iterations/sec " << iterations; - double elementSize = size; - double elementsPerSecond = elementSize*nSinceLastReport; - elementsPerSecond /= diff; - elementsPerSecond /= 1e6; - cout << " elements/sec " << elementsPerSecond << "million" << endl; - cout.flush(); - timeStampLast = timeStamp; - nSinceLastReport = 0; - } - ++nSinceLastReport; - ++value; - for(size_t i=0; i1) size = strtoul(argv[1],0,0); - if(argc>2) delay = atof(argv[2]); - if(argc>3) nThread = strtoul(argv[3],0,0); - cout << "vectorPerformance "; - cout << size << " "; - cout << delay << " "; - cout << nThread << " "; - cout << endl; - cout << "vectorPerformance\n"; - std::vector threads; - for(size_t i=0; iinit(); - } - for(size_t i=0; istart(); - } - string str; - while(true) { - cout << "Type exit to stop: \n"; - getline(cin,str); - if(str.compare("exit")==0) break; - - } - for(size_t i=0; idestroy(); - threads[i].reset(); - } - return 0; -} - diff --git a/configure/ExampleRELEASE.local b/configure/ExampleRELEASE.local index 1912f46..3d164df 100644 --- a/configure/ExampleRELEASE.local +++ b/configure/ExampleRELEASE.local @@ -1,10 +1,8 @@ +EPICS4_DIR=/home/epicsv4/master + +PVACCESS=${EPICS4_DIR}/pvAccessCPP +PVDATA=${EPICS4_DIR}/pvDataCPP +PVCOMMON=${EPICS4_DIR}/pvCommonCPP TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top - EPICS_BASE=/home/install/epics/base -V4BASE=/home/epicsv4 -PVCOMMON=${V4BASE}/pvCommonCPP -PVDATA=${V4BASE}/pvDataCPP -NORMATIVETYPES=${V4BASE}/normativeTypesCPP -PVACCESS=${V4BASE}/pvAccessCPP -PVASRV=${V4BASE}/pvaSrv diff --git a/documentation/pvDatabaseCPP.html b/documentation/pvDatabaseCPP.html index e84aff8..1d8e3ff 100644 --- a/documentation/pvDatabaseCPP.html +++ b/documentation/pvDatabaseCPP.html @@ -36,29 +36,8 @@

pvDatabaseCPP

- +

Release 4.2 - 2016.01.12

-

EPICS v4 Working Group, Working Draft, 23-October-2015

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20151023.html -
-
Previous version:
-
pvDatabaseCPP20151002.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -

Abstract

@@ -69,31 +48,33 @@ pvData. The framework includes a complete implementation of ChannelProvider as d The framework can be extended in order to create record instances that implements services. The minimum that an extension must provide is a top level PVStructure and a process method.

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

+ +

For more information about EPICS generally, please refer to the home page of the Experimental Physics and Industrial + Control System.

-

Status of this Document

- -

This is the 23-October-2015 version of of pvDatabaseCPP.

-

This version is a complete implementation of what is described in this manual. -

-

Table of Contents

+

Table of Contents

-
+ + +
+

Introduction

Overview

The main purpose of this project to make it easier to implement services that are accessed via pvAccess. -This project supplies a complete implementation of the server side of pvAccess. -All that a service has to provide is a top level PVStructure and a process method. +

+

+This project supplies a complete implementation of the server side of pvAccess, +which has the provider name local. +

+

+A service must provide a top level PVStructure and a process method. A service can be run as a main process or can be part of a V3 IOC. Thus services can be developed that interact with V3 records, asynDriver, areaDetector, etc. @@ -151,104 +132,10 @@ level PVStructure and the following three methods:

href="./html/index.html">doxygenDoc

Getting started

The first step is to build pvDatabaseCPP as described in the next section.

-

One of the examples is exampleServer. -It can be started either via a main program or as part of a V3 IOC. +

A separate project exampleCPP has examples for pvDatabaseCPP +and for pvaClientCPP. +See it for examples.

-

To start it as a main program do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> bin/linux-x86_64/exampleServerMain 
-
-

You should see something like the following:

-
-result of addRecord exampleServer 1
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-exampleServer
-Type exit to stop: 
-
-

Then in another window execute a pvput and pvget as follows:

-
-mrk> pvput -r "field(argument.value)" exampleServer World
-...
-mrk> pvget -r "record[process=true]field(result.value)" exampleServer
-exampleServer
-structure 
-    string value Hello World
-mrk> 
-
-

To run the example as part of a V3 IOC do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../bin/linux-x86_64/exampleServer st.cmd 
-
-

You will see the following:

-
-> envPaths
-epicsEnvSet("ARCH","linux-x86_64")
-epicsEnvSet("IOC","exampleServer")
-epicsEnvSet("TOP","/home/hg/pvDatabaseCPP/exampleServer")
-epicsEnvSet("EPICS_BASE","/home/install/epics/base")
-epicsEnvSet("EPICSV4HOME","/home/hg")
-cd /home/hg/pvDatabaseCPP/exampleServer
-## Register all support components
-dbLoadDatabase("dbd/exampleServer.dbd")
-exampleServer_registerRecordDeviceDriver(pdbbase)
-## Load record instances
-dbLoadRecords("db/dbScalar.db","name=pvdouble,type=ao")
-dbLoadRecords("db/dbArray.db","name=pvdoubleArray,type=DOUBLE")
-dbLoadRecords("db/dbStringArray.db","name=pvstringArray")
-dbLoadRecords("db/dbEnum.db","name=pvenum")
-dbLoadRecords("db/dbCounter.db","name=pvcounter");
-cd /home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-iocInit()
-Starting iocInit
-############################################################################
-## EPICS R3.14.12.3 $Date: Mon 2012-12-17 14:11:47 -0600$
-## EPICS Base built Dec 21 2013
-############################################################################
-iocRun: All initialization complete
-dbl
-pvdouble
-pvcounter
-pvenum
-pvdoubleArray
-pvstringArray
-epicsThreadSleep(1.0)
-exampleServerCreateRecord pvaServer
-startPVAServer
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : dbPv local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-pvdbl
-pvaServer
-epics> 
-
-

Just like previously you can then execute a pvput and pvget and see Hello World. -

-

The examples, i. e. exampleServer, exampleLink, examplePowerSupply, -and exampleDatabase, are described in separate sections below. -In addition arrayPerformance can be used to measure that performance of big -arrays. It is also described in a later section.

-

Reading section exampleServer and looking at it's code is a good way -to learn how to implement a service.

Features Required for localChannelProvider

@@ -300,9 +187,34 @@ to learn how to implement a service.

Building pvDatabaseCPP

-

To build pvDatabaseCPP You must provide a file RELEASE.local -in directory configure. -Thus do the following:

+

+If a proper RELEASE.local is present one directory level above pvDatabaseCPP. +

+

+Just type: +

+
+make
+
+

+An example of a proper RELEASE.local is: +

+
+EPICS4_DIR=/home/epicsv4/master
+EXAMPLE=${EPICS4_DIR}/exampleCPP
+PVDATABASE=${EPICS4_DIR}/pvDatabaseCPP
+PVACLIENT=${EPICS4_DIR}/pvaClientCPP
+PVASRV=${EPICS4_DIR}/pvaSrv
+PVACCESS=${EPICS4_DIR}/pvAccessCPP
+NORMATIVETYPES=${EPICS4_DIR}/normativeTypesCPP
+PVDATA=${EPICS4_DIR}/pvDataCPP
+PVCOMMON=${EPICS4_DIR}/pvCommonCPP
+
+EPICS_BASE=/home/install/epics/base
+
+ +

pvDatabaseCPP can also be built if a file RELEASE.local exists in directory configure. +To create one do the following:

 mrk> pwd
 /home/hg/pvDatabaseCPP/configure
@@ -314,94 +226,31 @@ Than at the top level just execute make:

 mrk> cd ..
 mrk> pwd
-/home/hg/pvDatabaseCPP
+/home/epicsv4/master/pvDatabaseCPP
 mrk> make
 
-

This builds pvDatabaseCPP and also the tests and all examples.

-

Each example and arrayPerformance is a completely separate top, -but is also built when make is run in pvDatabaseCPP itself.

-

-Each is a separate top for the following reasons:

-
    -
  1. - It is easier to understand each example including how it is built so that - it can be run as a main or as part of a V3 IOC. -
  2. -
  3. - Each example can be copied somewhere else and used as the basis - for creating a new service. -
  4. -
-

-If it is desired to build an example all by itself, -just follow the same instructions as for building pvDatabaseCPP itself. -For example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product the example requires. -Than at the top level of the example just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> make
-
-

This builds the example.

-

iocshell commands

Shell commands are made available via the standard DBD include mechanism provided by iocCore. The following provide EPICS V4 shell commands:

-
-
pvAccessCPP
-
PVAClientRegister.dbd and PVAServerRegister.dbd
-
pvaSrv
-
dbPv.dbd
-
pvDatabaseCPP
-
registerChannelProviderLocal.dbd
-
-

-Look at exampleServer/ioc/src/exampleServerInclude.dbd for an example -of how an application can make the shell commands available. -

-

Commands From pvAccessCPP

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider
-
stopPVAServer
-
Stop the local channel provider
-
+
+pvAccessCPP
+pvaSrv
+pvDatabaseCPP
+
-

Commands implemented by pvDatabaseCPP

-

The following iocsh commands are provided for a V3IOC:

+

pvDatabaseCPP provides the following iocshell command.

+
registerChannelProviderLocal
+
Including registerChannelProviderLocal.dbd as a dbd file automatically starts provider local + and also creates the pvdbl shell command. +
pvdbl
Provides a list of all the pvRecords in database master

In addition any code that implements a PVRecord must implement an ioc command. -Look at any of the examples to see how to implement shell commands.

-

Commands implemented by pvaSrv

-

pvaSrv provides a pvAccess server that provides access to iocCore records.

-

pvaSrv does not provide any shell commands but it can be part of an IOC. -Just make sure your application configures pvaSrv and then include the following file: -

-
-include "dbPv.dbd"
-
+Look at any of the examples in exampleCPP to see how to implement shell commands.

database

src/database

@@ -838,20 +687,21 @@ It uses the copy and monitor facilities from pvDataCPP and connects them to a PVRecord.

The implementation is a complete implementation of channelProvider -and channel except for channelRPC, which is implement by pvAccess as a separate -channel provider.

+and channel.

The following provides a brief description of each channel method that is implemented.

channelProcessLocal

-

Needs to be described.

+

Implements channelProcess.

channelGetLocal

-

Needs to be described.

+

Implements channelGet.

channelPutLocal

-

Needs to be described.

+

Implements channelPut.

channelPutGetLocal

-

Needs to be described.

+

Implements channelPutGet.

channelArrayLocal

-

Needs to be described.

+

Implements channelArray.

+

ChannelRPCLocal

+

Implements channelRPC.

MonitorLocal

This is the code that implements monitors on changes to fields of a PVRecord. Because it is called by pvAccess client (monitor methods) and by @@ -1006,11 +856,8 @@ active element but never modifies the pvStructure.

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet +

This section provides traceRecord which implements a PVRecord that allows a client to set +the trace level of another PVRecord. It follows the pattern of a channelPutGet record:

 traceRecord
@@ -1054,950 +901,6 @@ result = master->addRecord(pvRecord);
 if(!result) cout<< "record " << recordName << " not added" << endl;
 
-

exampleServer

-

Overview

-

The example implements a simple service that has a top level pvStructure: -

-
-structure
-    structure argument
-        string value
-    structure result
-        string value
-        time_t timeStamp
-            long secondsPastEpoch
-            int nanoseconds
-            int userTag
-
-

It is designed to be accessed via a channelPutGet request. -The client sets argument.value -When the record processes it sets result.value to "Hello " -concatenated with argument.value. -Thus if the client sets argument.value equal to "World" -result.value will be "Hello World". -In addition the timeStamp is set to the time when process is called.

-

-The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleService
-mrk> bin/linux-x86_64/exampleService
-
-

Directory Layout

-

-The directory layout is: -

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerInclude.dbd
-       exampleServerMain.cpp
-       exampleServerRegister.cpp
-       exampleServerRegister.dbd
-    ioc
-       Db
-          ...
-       src
-          exampleServerInclude.dbd
-          exampleServerMain.cpp
-   iocBoot
-      exampleServer
-         st.cmd
-         ...
-
-where -
-
ExampleRELEASE.local
-
- If you make a copy of exampleServer and use it - to create a new server, - This is the file that must be copied to RELEASE.local - and edited.
-
exampleServer.h
-
The header file for the service.
-
exampleServer.cpp
-
The service implementation.
-
exampleServerMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. -
-
exampleServerInclude.dbd
-
This has a register command so that the service can be started - on a V3 IOC via iocsh. -
-
exampleServerRegister.cpp
-
This has the code to start the service via the following iocsh - command. -
-
exampleServerRegister.dbd
-
This is the file that is used to create the shell command - exampleServerCreateRecord. -
-exampleServerCreateRecord exampleServer
-
- Multiple commands can be issued to create multiple service records. -
-
ioc
-
This is for building a V3 IOC application.
-
ioc/Db
-
This has template files for creating V3 records.
-
ioc/src
-
The files for running a V3 IOC.
-
iocBoot/exampleServer
-
A place to start exampleServer as part of a V3IOC. - It has a st.cmd file that starts the ioc and also starts pvAccess - and the example.
-
-

If only a main program is desired then the directory layout is:

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerMain.cpp
-
-

Thus if only a main program is required the directory layout is simple.

-

Also many sites will want to build the src directory in an area -separate from where the iocs are build.

-

exampleServer.h

-

The example resides in src -The implementation is in exampleServer.cpp. -

-

The description consists of

-
-class ExampleServer;
-typedef std::tr1::shared_ptr<ExampleServer> ExampleServerPtr;
-
-class ExampleServer :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleServer);
-    static ExampleServerPtr create(
-        std::string const & recordName);
-    virtual ~ExampleServer();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleServer(std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-
-    epics::pvData::PVStringPtr pvArgumentValue;
-    epics::pvData::PVStringPtr pvResultValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleServer
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
-
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleServer
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleServerPtr ExampleServer::create(
-    std::string const & recordName)
-{
-    StandardFieldPtr standardField = getStandardField();
-    FieldCreatePtr fieldCreate = getFieldCreate();
-    PVDataCreatePtr pvDataCreate = getPVDataCreate();
-    StructureConstPtr  topStructure = fieldCreate->createFieldBuilder()->
-        addNestedStructure("argument")->
-            add("value",pvString)->
-            endNested()->
-        addNestedStructure("result") ->
-            add("value",pvString) ->
-            add("timeStamp",standardField->timeStamp()) ->
-            endNested()->
-        createStructure();
-    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
-
-    ExampleServerPtr pvRecord(
-        new ExampleServer(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleServerPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleServer::ExampleServer(
-    std::string const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleServer::~ExampleServer()
-{
-}
-
-void ExampleServer::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleServer::init()
-{
-    initPVRecord();
-    PVFieldPtr pvField;
-    pvArgumentValue = getPVStructure()->getStringField("argument.value");
-    if(pvArgumentValue.get()==NULL) return false;
-    pvResultValue = getPVStructure()->getStringField("result.value");
-    if(pvResultValue.get()==NULL) return false;
-    pvTimeStamp.attach(getPVStructure()->getSubField("result.timeStamp"));
-    return true;
-}
-
-

The implementation of process is:

-
-void ExampleServer::process()
-{
-    pvResultValue->put(String("Hello ") + pvArgumentValue->get());
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It gives a value to result.value and -then sets the timeStamp to the current time. -

src/exampleServerMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleServer");
-    PVRecordPtr pvRecord = ExampleServer::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    recordName = "traceRecordPGRPC";
-    pvRecord = TraceRecord::create(recordName);
-    result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    ServerContext::shared_pointer pvaServer =
-        startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    PVStringArrayPtr pvNames = master->getRecordNames();
-    shared_vector<const string> names = pvNames->view();
-    for(size_t i=0; i<names.size(); ++i) cout << names[i] << endl;
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates record exampleServer
  • -
  • creates record traceRecordPGRPC
  • -
  • lists all the records
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleServer

-

To start exampleServer as part of a V3IOC:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../../bin/linux-x86_64/exampleServer st.cmd
-
-

You can then issue the commands dbl and pvdbl:

-
-epics> dbl
-pvdouble
-pvcounter
-pvenum
-pvdoubleArray
-pvstringArray
-epics> pvdbl
-exampleServer
-epics> 
-
-dbl shows the V3 records. -pvdbl shows the pvRecords. -

-It starts pvaSrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

- -

exampleDatabase

-

The exampleServer pvDatabase has many records including the following:

-
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

exampleDatabase can be started as a main program or as a V3 IOIC. -If started as a V3 IOC it also has a number of V3 records, -and starts pvaSrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

-

exampleLink

-

This example show how a service can access other PVRecords. -This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - cause deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all locking issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transferred between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext =
-     startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelProvider::shared_pointer provider =
-     getChannelProviderRegistry()->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
- -

Directory Layout

-
-exampleLink
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleLink.h
-       exampleLink.cpp
-       exampleLinkInclude.dbd
-       exampleLinkRegister.cpp
-    ioc
-       Db
-       src
-          exampleLinkInclude.dbd
-          exampleLinkMain.cpp
-   iocBoot
-      exampleLink
-         st.local
-         st.remote
-         ...
-
-

This example is only built to be run as part of a V3 IOC. -Note that two startup files are available: st.local and st.remote. -st.local has two records: doubleArray and exampleLink. -doubleArray is a record that can be changed via a call to pvput. -exampleLink is a record that, when processed, gets the value from doubleArray and sets its value equal -to the value read. -st.local has both records. -st.remote has only one record named exampleLinkRemote. -

-

To start the example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleLink/iocBoot/exampleLink
-mrk> ../../bin/linux-x86_64/exampleLink st.local 
-
-

then in another window:

-
-mrk> pvput doubleArray 4 100 200 300 400
-Old : doubleArray 0
-New : doubleArray 4 100 200 300 400
-mrk> pvget -r "record[process=true]field(value)" exampleLink
-exampleLink
-structure 
-    double[] value [100,200,300,400]
-mrk> 
-
-

exampleLink Implementation

-

exampleLink.h contains the following:

-
-...
-class ExampleLink :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExampleLink);
-    static ExampleLinkPtr create(
-        std::string const & recordName,
-        std::string const & providerName,
-        std::string const & channelName
-        );
-    virtual ~ExampleLink() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExampleLinkPtr ExampleLink::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExampleLinkPtr pvRecord(
-        new ExampleLink(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExampleLink instance, -and then calls the init method and the returns a ExampleLinkPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExampleLink::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

examplePowerSupply

-

This is an example of creating a service that requires a somewhat complicated -top level PVStructure. -Look at the code for details. -

-

Array Performance and Memory Example

-

This section describes main programs that demonstrate performance -of large arrays and can also be used to check for memory leaks. -Checking for memory leaks can be accomplished by running the programs with valgrind -or some other memory check program. -

-

Brief Summary

-

The programs are:

-
-
arrayPerformanceMain
-
This is server and also a configurable number of longArrayMonitor clients. - The clients can use either the local or - remote providers. The monitor code is the same code that is used by longArrayMonitorMain. -
-
longArrayMonitorMain
-
Remote client that monitors the array served by arrayPerformanceMain.
-
longArrayGetMain
-
Remote client that uses channelGet to access the array served by arrayPerformanceMain.
-
longArrayPutMain
-
Remote client that uses channelPut to access the array served by arrayPerformanceMain.
-
-

Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
-default
-arrayPerformanceMain arrayPerformance 10000000 0.0001 local 1 2 0.0
-
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName queueSize waitTime
-default
-longArrayMonitorMain arrayPerformance 2 0.0
-
-mrk> bin/linux-x86_64/longArrayGetMain -help
-longArrayGetMain channelName iterBetweenCreateChannel iterBetweenCreateChannelGet delayTime
-default
-longArrayGetMain arrayPerformance 0 0 1
-
-mrk> bin/linux-x86_64/longArrayPutMain -help
-longArrayPutMain channelName arraySize iterBetweenCreateChannel iterBetweenCreateChannelPut delayTime
-default
-longArrayPutMain arrayPerformance 10 0 0 1
-
-mrk> 
-
-

Example output

-

Note: These may fail if run on a platform that does not have sufficient memory,

-

To see an example just execute the following commands in four different terminal windows:

-
-bin/linux/<arch>/arrayPerformanceMain
-bin/linux/<arch>/longArrayMonitorMain
-bin/linux/<arch>/longArrayGetMain
-bin/linux/<arch>/longArrayPutMain
-
-

Each program generates a report every second when it has something to report. -Examples are:

-
-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0
-...
- monitors/sec 66 first 131 last 131 changed {1, 2} overrun {} megaElements/sec 656.999
-arrayPerformance value 132 time 1.00486 Iterations/sec 65.681 megaElements/sec 656.81
- monitors/sec 66 first 197 last 197 changed {1, 2} overrun {} megaElements/sec 656.304
-arrayPerformance value 198 time 1.00563 Iterations/sec 65.6307 megaElements/sec 656.307
- monitors/sec 66 first 263 last 263 changed {1, 2} overrun {} megaElements/sec 654.824
-...
-
-
-mrk> bin/linux-x86_64/longArrayMonitorMain
-longArrayMonitorMain arrayPerformance 2 0
-...
- monitors/sec 6 first 2357 last 2357 changed {1, 2} overrun {} megaElements/sec 68.6406
- monitors/sec 13 first 2385 last 2385 changed {1, 2} overrun {} megaElements/sec 118.72
- monitors/sec 9 first 2418 last 2418 changed {1, 2} overrun {1, 2} megaElements/sec 85.0984
-...
-
-
-mrk> bin/linux-x86_64/longArrayPutMain
-longArrayPutMain arrayPerformance 10 0 0 1
-...
-put numChannelPut 0 time 1.00148 Elements/sec 79.8819
-put numChannelPut 1 time 1.00176 Elements/sec 79.8598
-...
-
-
-mrk> bin/linux-x86_64/longArrayGetMain
-longArrayGetMain arrayPerformance 0 0 1
-...
-get kiloElements/sec 7384.61
-get kiloElements/sec 8726.34
-...
-
- -

arrayPerformance

-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
queueSize
-
The queueSize for the element queue. - A value less than 1 will become 1. -
-
waitTime
-
The time that longArrayMonitor will sleep after poll returns a monitorElement.
-
-

-arrayPerformance creates a PVRecord that has the structure:. -

-
-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers. -

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
Once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the iteration count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report every second showing how many elements has received. -For every monitor it also checks that the number of elements is >0 and the -the first element equals the last element. It reports an error if either -of these conditions is not true.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
queueSize
-
The queueSize. Note that any size <2 is made 2.
-
waitTime
-
The time to wait after a poll request returns a monitorElement. - This can be used to force an overrun of the client even if there is no - overrun on the server.
-
-

longArrayGet

-

This is a pvAccess client that uses channelGet to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayGetMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelGet
-
The number of iterations between destroying and recreating the channelGet. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to delay between gets.
-
-

longArrayPut

-

This is a pvAccess client that uses channelPut to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayPutMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
arraySize
-
The capacity and length of the array to put to the server.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelPut
-
The number of iterations between destroying and recreating the channelPut. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to delay between gets.
-
- -

Some results

-

array performance

-

The results were from my laptop in 2013 -It had a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system was linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 10 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 50 iterations per second -and is putting about 500million elements per second. -Since each element is an int64 this means about 4gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-
-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 25 iterations per second and 250 million elements per second. -The next section has an example that demonstrates what happens. -Note that if the array size is small enough to fit in the local cache then running longArrayMonitor -has almost no effect of arrayPerforance. -

-

memory leaks

-

Running longArrayMonitorMain, longArrayPutMain, and longArrayGetMain -under valgrind shows no memory leaks.

-

arrayPerformanceMain shows the following:

-
-==9125== LEAK SUMMARY:
-==9125==    definitely lost: 0 bytes in 0 blocks
-==9125==    indirectly lost: 0 bytes in 0 blocks
-==9125==      possibly lost: 576 bytes in 2 blocks
-
-

The possibly leaked is either 1 or 2 blocks. -It seems to be the same if clients are connected. -

-

Vector Performance

-

This example demonstrates how array size effects performance. -The example is run as:

-
-bin/linux-x86_64/vectorPerformanceMain -help
-vectorPerformanceMain size delay nThread
-default
-vectorPerformance 50000000 0.01 1 
-
-

Consider the following:

-
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
-...
-thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
-thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
-thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
-...
-thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
-thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
-thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
-thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
-thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
-thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
-thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
-thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
-thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
-thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
-thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
-thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
-thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
-thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
-thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
-thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
-thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
-thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
-
-

As more threads are running the slower each thread runs.

-

But now consider a size that fits in a local cache.

-
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
-...
-thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
-thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
-thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
-... 
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
-...
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-...
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
-...
-thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
-thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
-thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
-thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
-thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
-thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
-
-

Now the number of threads has a far smaller effect on the performance of each thread. -

diff --git a/documentation/pvDatabaseCPP_20121127.html b/documentation/pvDatabaseCPP_20121127.html deleted file mode 100644 index c53af75..0000000 --- a/documentation/pvDatabaseCPP_20121127.html +++ /dev/null @@ -1,714 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 27-Nov-2012

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP.html -
-
Previous version:
-
None
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework must be extended in order to create record instances. -The minimum that an extenson must provide is a top level PVStructure and a process method -but the framework provides for complex extensions.

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- -

Status of this Document

- -

This is the 27-Nov-2012 version of the definition of pvDatabaseCPP. -This is the original version. -

-

This is the beginning of the implementation of pvDataBaseCPP. -It describes the features that will be provided. -The class definitions for PVRecord and PVDatabase are defined but not implemented.

- - -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

This document descibes a C++ implementation of some of the components in pvIOCJava. -It extracts the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not and will not implement any of the specialized support that pvIOCJava -provides. Instead other projects will implement the specialized support. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava. -

- -

A brief description of a pvDatase is that it is a network accessible set of smart memory resident -records. Each record has data composed of a top level PVStructure. Each record has a name which is -the channelName for pvAccess. A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record.

-

This document describes components that provides the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
localChannelProvider
-
This is a complete implementation of ChannelProvider and Channel as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
-

database does not itself implement pvRecord instances. -Instead it provides a base classes that make it easy to create record instances. -What does have to be implemented is a top -level PVStructure and the following two methods:

-
-
process
-
This is what makes a record smart. - What process does is up to the implementation except that it must decide if - it's execution model is synchronous or asynchronous. - Synchronous means that when process returns the processing is complete. - Asynchronous means that when process returns the processing is not complete. - Instead process invokes other threads that will complete the processing at a later time.
-
isSynchronous
-
Which execution model is being implemented.
-
-

Example PVRecord Extension

-

Directory example/record has an example PVRecord implementation. -It implements a counter. -The top level structure is:

-
-structure
-    long value
-
-

NOTE: The example compiles but does not build because nothing -is implemented.

- -

exampleRecord.h

-

This is the class description. -The example extends PVRecord.

-
-class ExampleRecord :
-  public virtual PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleRecord);
-    static PVRecordPtr create(epics::pvData::String const & recordName);
-    virtual ~ExampleRecord();
-    virtual bool isSynchronous();
-    virtual void process(
-        epics::pvDatabase::RecordProcessRequesterPtr const &processRequester);
-private:
-    ExampleRecord(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure,
-        epics::pvData::PVLongPtr const &pvValue);
-    epics::pvData::PVLongPtr pvValue;
-};
-
-

where

-
-
create
-
This is example specific. See the implemention for details.
-
~ExampleRecord
-
The destructor must be declared virtual.
-
isSynchronous
-
The implementation must say if process is synchronous or asynchronous.
-
process
-
The implementation.
-
ExampleRecord
-
For the example this is private.
-
- -

exampleRecord.cpp

-

This is the class implementation.

-
-ExampleRecord::~ExampleRecord(){}
-
-PVRecordPtr ExampleRecord::create(String const & recordName)
-{
-    String properties;
-    PVStructurePtr pvStructure = getStandardPVField()->scalar(pvLong,properties);
-    PVLongPtr pvValue =  pvStructure->getLongField("value");
-    PVRecordPtr pvRecord(new ExampleRecord(recordName,pvStructure,pvValue));
-    return pvRecord;
-}
-
-ExampleRecord::ExampleRecord(
-    String const & recordName,
-    PVStructurePtr const & pvStructure,
-    PVLongPtr const &pvValue)
-: PVRecord(recordName,pvStructure),
-  pvValue(pvValue)
-{}
-
-bool ExampleRecord::isSynchronous() {return true;}
-
-void ExampleRecord::process(
-    RecordProcessRequesterPtr const &processRequester)
-{
-    pvValue->put(pvValue->get() + 1);
-    processRequester->recordProcessResult(Status::Ok);
-    processRequester->recordProcessComplete();
-}
-
-

where

-
-
create
-
Creates a PVStructure with a single subfield named value. - It gets the interface to the value field. - It then creates an ExampleRecord and returns it. -
-
~ExampleRecord
-
Does not have to do anything because of shared pointers.
-
ExampleRecord
-
Calls the base class constructor and sets pvValue.
-
isSynchronous
-
The example is synchronous.
-
process
-
Gets the curent value, increments it, and puts the new value. - It than calls two processRequester callbacks.
-
- -

exampleRecordMain.cpp

-

This is a main for creating and running the example.

-
-int main(int argc,char *argv[])
-{
-    String recordName("exampleRecord");
-    PVRecordPtr pvRecord = ExampleRecord::create(recordName);
-    PVDatabasePtr pvDatabase = PVDatabase::getMaster();
-    pvDatabase->addRecord(pvRecord);
-    cout << recordName << "\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-

The main program creates an example record and adds it to the database. -It then runs until the process is stopped by typing exit. -

Until the process is stopped, -pvAccess clients can put and get the value field. -For example

-
-pvget exampleRecord
-pvput exampleRecord 5
-
-

Will both work.

-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
These two features will be the first phase. - But only synchronous record processing will be supported.
-
-

Future phases of pvDatabaseCPP should include:

-
-
Install
-
This provides on-line add and delete.
-
Field support
-
Add ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava will also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
local ChannelProvider
-
This is the pvAccess package in pvIOCJava. - The localChannelProvider will access data from PVRecords. - It will implement all channel methods except channelRPC.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provide the following: -
-
PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections provide a first attempt to describe the classes required for the first -phase.

-

The last section gives a brief overview of the features provided by pvIOCJava.

- -

database

-

The classes in pvDatabase.h implement a database of memory resident -smart records. The next subsection has the definitions for all the classes -defined in this header file. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor can be implemented.
-
PVListener
-
This is implemented by anything that wants to trap calls to the PVRecord::message.
-
RecordProcessRequester
-
This is implemented by anything that calls PVRecord::queueProcessRequest.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

- -

class PVRecord

-
-class PVRecord
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    virtual void process(
-        RecordProcessRequesterPtr const &recordProcessRequester) = 0;
-    virtual bool isSynchronous() = 0;
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    void lock();
-    void unlock();
-    void registerClient(PVRecordClientPtr const & pvRecordClient);
-    void unregisterClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    void beginGroupPut();
-    void endGroupPut();
-    void registerListener(PVListenerPtr const & pvListener);
-    void unregisterListener(PVListenerPtr const & pvListener);
-    void removeEveryListener();
-    epics::pvData::Status processRequest();
-    void queueProcessRequest(
-        RecordProcessRequesterPtr const &recordProcessRequester);
-    void addRequester(epics::pvData::RequesterPtr const & requester);
-    void removeRequester(epics::pvData::RequesterPtr const & requester);
-    void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    epics::pvData::String toString();
-    epics::pvData::String toString(int indentLevel);
-};
-
-

The methods are: -

-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
~PVRecord
-
The desctructor which must be virtual. A derived class must also have - a virtual destructor.
-
process
-
Pure virtual method. Derived classes must implement this method.
-
isSynchronous
-
Pure virtual method. Derived classes must implement this method.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
registerClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
unregisterClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
beginGroupPut
-
Begin a group of puts. This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. This results in all registered PVListeners being called.
-
registerListener
-
Register a PVListener. This must be called before calling pvRecordField.addListener.
-
unregisterListener
-
Unregister a listener. The listener will also be removed from all fields to which it is attached.
-
removeEveryListener
-
This must be called by any code that is deleting or changing the structure of a record.
-
processRequest
-
This is a convenience method for clients that are willing to block if - process is asynchronous. It implements RecordProcessRequester. - If process is synchronous it just calls process and returns the result - to the caller. If process is asynchronous it calls queueProcessRequest, - and process and waits for completion and then returns the result to the caller.
-
queueProcessRequest
-
Queue a process request.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
-

class PVRecordField

-
-class PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    void removeListener(PVListenerPtr const & pvListener);
-    void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVListener

-
-class PVListener {
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord) = 0;
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The PVLister is being removed from the record. - This is called when the record is being destroyed or when the record structure - (not the data values) is being changed.
-
-

class RecordProcessRequester

-
-class RecordProcessRequester :
-    virtual public epics::pvData::Requester
-{
-public:
-    POINTER_DEFINITIONS(RecordProcessRequester);
-    virtual ~RecordProcessRequester();
-    virtual void becomeProcessor() = 0;
-    virtual void recordProcessResult(epics::pvData::Status status) = 0;
-    virtual void recordProcessComplete() = 0;
-};
-
-

where

-
-
~RecordProcessRequester
-
The destructor.
-
becomeProcessor
-
Called as a result of queueRequeProcessst. The requester can the call process.
-
recordProcessResult
-
The results of record processing. - This is called with the record locked so that the process requester - can access data from the record.
-
recordProcessComplete
-
Processing is complete. - This is called with the record unlocked. - If the process requester called process with leaveActive true then the requester - must call setInactive.
-
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    bool removeRecord(PVRecordPtr const & record);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
-

Local Channel Provider

-

Not yet described.

-

A brief description is that it must implement the following components of pvIOCJava:

-
-
pvCopy
-
monitor
-
pvAccess
-
See the next section for a description
-
-

Summary of Packages in pvIOCJAVA

-

The following are the direct sub packages of pvIOCJava/src/org/epics/pvioc:

-
-
pvCopy
-
This provides a copy of an arbitrary subset of the fields in a PVRecord. - It also provides the ability to detect and report changes to fields. - It is required for pvAccess.
-
monitor
-
This provides the ability to monitor changes to a PVRecord. It is required for pvAccess monitors.
-
pvAccess
-
The local implementation of Channel Provider and Channel. - It is accessed by the remote pvAccess server and can also be accessed by code in the same IOC.
-
database
-
This defines and implements PVRecord, PVDatabase , and PVListener. - It supports the basic feature required the implement a local Channel Provider.
-
support
-
This provides the ability to optionally attach code to any field of a pvRecord. - It and several sub packages provide a set of standard support modules.
-
install
-
This provides the ability to dynamically initialize and add new PVRecords. It also provides - the ability to dynamicall delete PVRecords. -
xml
-
This provides the ability to configure record instances without writing code.
-
util
-
This is misnamed since it is code related to scanning.
-
pdrv
-
This is portDriver, which is a proposed sucessor to the asynManager component of asynDriver.
-
swtshell
-
This is shell that is can either run under the same process as a JavaIOC or as a remote shell. - It is like a version of probe but for pvData/pvAccess. - Almost all of it's features work in either local or remote mode. - With a little more work all or it's features could work remotely. - This should be done and then only remote mode should be supported. - It can then be rewritten in a completely different language and using a complely different GUI - framework. -
-
caV3
-
This has two components: -
-
ClientFactory
-
This is a small wrapper on top of the caV3 client support implemented by pvAccess. - It allows code in the pvIOC to access V3Records via pvAccess.
-
ServerFactory
-
This is a caV3 server that allows a caV3 client to access a PVRecord. - The Java implementation uses CAJ, which does most of the work. - For now it will not be discussed in this document.
-
-
-
v3a
-
I do not know what this is.
-
-

In addition there is one class file JavaIOC.java. -This is starting a IOC instance. -This is not required for pvIOCCPP which is either a main or runs as part of a V3 IOC.

- -
- - diff --git a/documentation/pvDatabaseCPP_20121211.html b/documentation/pvDatabaseCPP_20121211.html deleted file mode 100644 index 175ef00..0000000 --- a/documentation/pvDatabaseCPP_20121211.html +++ /dev/null @@ -1,874 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 11-Dec-2012

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20121211.html -
-
Previous version:
-
pvDatabaseCPP_20121127.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework must be extended in order to create record instances. -The minimum that an extenson must provide is a top level PVStructure and a process method -but the framework provides for complex extensions.

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 11-Dec-2012 version of the definition of pvDatabaseCPP. -

-

This is the beginning of the implementation of pvDataBaseCPP. -It describes the features that will be provided. -The class definitions for PVRecord are implemented. -The class definition for PVDatabase are defined but not implemented.

- - -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

This document descibes a C++ implementation of some of the components in pvIOCJava. -It extracts the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not and will not implement any of the specialized support that pvIOCJava -provides. Instead other projects will implement the specialized support. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava. -

- -

A brief description of a pvDatase is that it is a network accessible set of smart memory resident -records. Each record has data composed of a top level PVStructure. Each record has a name which is -the channelName for pvAccess. A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record.

-

This document describes components that provides the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
localChannelProvider
-
This is a complete implementation of ChannelProvider and Channel as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
-

database does not itself implement pvRecord instances. -Instead it provides a base classes that make it easy to create record instances. -What does have to be implemented is a top -level PVStructure and the following two methods:

-
-
process
-
This is what makes a record smart. - What process does is up to the implementation except that it must decide if - it's execution model is synchronous or asynchronous. - Synchronous means that when process returns the processing is complete. - Asynchronous means that when process returns the processing is not complete. - Instead process invokes other threads that will complete the processing at a later time.
-
isSynchronous
-
Which execution model is being implemented.
-
-

Example PVRecord Extension

-

Directory example/record has an example PVRecord implementation. -It implements a counter. -The top level structure is:

-
-structure
-    long value
-
-

NOTE: The example compiles but does not build because nothing -is implemented.

- -

exampleRecord.h

-

This is the class description. -The example extends PVRecord.

-
-class ExampleRecord :
-  public virtual PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleRecord);
-    static PVRecordPtr create(epics::pvData::String const & recordName);
-    virtual ~ExampleRecord();
-    virtual bool isSynchronous();
-    virtual void process(
-        epics::pvDatabase::RecordProcessRequesterPtr const &processRequester);
-private:
-    ExampleRecord(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure,
-        epics::pvData::PVLongPtr const &pvValue);
-    epics::pvData::PVLongPtr pvValue;
-};
-
-

where

-
-
create
-
This is example specific. See the implemention for details.
-
~ExampleRecord
-
The destructor must be declared virtual.
-
isSynchronous
-
The implementation must say if process is synchronous or asynchronous.
-
process
-
The implementation.
-
ExampleRecord
-
For the example this is private.
-
- -

exampleRecord.cpp

-

This is the class implementation.

-
-ExampleRecord::~ExampleRecord(){}
-
-PVRecordPtr ExampleRecord::create(String const & recordName)
-{
-    String properties;
-    PVStructurePtr pvStructure = getStandardPVField()->scalar(pvLong,properties);
-    PVLongPtr pvValue =  pvStructure->getLongField("value");
-    PVRecordPtr pvRecord(new ExampleRecord(recordName,pvStructure,pvValue));
-    return pvRecord;
-}
-
-ExampleRecord::ExampleRecord(
-    String const & recordName,
-    PVStructurePtr const & pvStructure,
-    PVLongPtr const &pvValue)
-: PVRecord(recordName,pvStructure),
-  pvValue(pvValue)
-{}
-
-bool ExampleRecord::isSynchronous() {return true;}
-
-void ExampleRecord::process(
-    RecordProcessRequesterPtr const &processRequester,bool alreadyLocked)
-{
-    if(!alreadyLocked) lock();
-    pvValue->put(pvValue->get() + 1);
-    processRequester->recordProcessResult(Status::Ok);
-    unlock();
-    processRequester->recordProcessComplete();
-    dequeueProcessRequest(processRequester);
-}
-
-

where

-
-
create
-
Creates a PVStructure with a single subfield named value. - It gets the interface to the value field. - It then creates an ExampleRecord and returns it. -
-
~ExampleRecord
-
Does not have to do anything because of shared pointers.
-
ExampleRecord
-
Calls the base class constructor and sets pvValue.
-
isSynchronous
-
The example is synchronous.
-
process
-
Gets the curent value, increments it, and puts the new value. - It than calls two processRequester callbacks.
-
- -

exampleRecordMain.cpp

-

This is a main for creating and running the example.

-
-int main(int argc,char *argv[])
-{
-    String recordName("exampleRecord");
-    PVRecordPtr pvRecord = ExampleRecord::create(recordName);
-    PVDatabasePtr pvDatabase = PVDatabase::getMaster();
-    pvDatabase->addRecord(pvRecord);
-    cout << recordName << "\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-

The main program creates an example record and adds it to the database. -It then runs until the process is stopped by typing exit. -

Until the process is stopped, -pvAccess clients can put and get the value field. -For example

-
-pvget exampleRecord
-pvput exampleRecord 5
-
-

Will both work.

-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
These two features will be the first phase. - But only synchronous record processing will be supported.
-
-

Future phases of pvDatabaseCPP should include:

-
-
Install
-
This provides on-line add and delete.
-
Field support
-
Add ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava will also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
local ChannelProvider
-
This is the pvAccess package in pvIOCJava. - The localChannelProvider will access data from PVRecords. - It will implement all channel methods except channelRPC.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provide the following: -
-
PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections provide a first attempt to describe the classes required for the first -phase.

-

The last section gives a brief overview of the features provided by pvIOCJava.

- -

database

-

The classes in pvDatabase.h implement a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to the PVRecord::message.
-
RecordProcessRequester
-
This is implemented by anything that calls PVRecord::queueProcessRequest.
-
RecordPutRequester
-
This is implemented by anything that calls PVRecord::queuePutRequest.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordProcessRequester;
-typedef std::tr1::shared_ptr<RecordProcessRequester> RecordProcessRequesterPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    virtual void process(
-        RecordProcessRequesterPtr const &recordProcessRequester,
-        bool alreadyLocked) = 0;
-    virtual bool isSynchronous() = 0;
-    virtual bool requestImmediatePut(epics::pvData::PVFieldPtr const &pvField);
-    virtual void immediatePutDone();
-    virtual void destroy();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    void addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    void beginGroupPut();
-    void endGroupPut();
-    void queueProcessRequest(
-        RecordProcessRequesterPtr const &recordProcessRequester);
-    void dequeueProcessRequest(
-        RecordProcessRequesterPtr const &recordProcessRequester);
-    void queuePutRequest(
-        RecordPutRequesterPtr const &recordPutRequester);
-    void putDone(
-        RecordPutRequesterPtr const &recordPutRequester);
-    virtual epics::pvData::String getRequesterName();
-    void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-    //init MUST be called after derived class is constructed
-    void init();
-
-};
-
-

The methods are: -

-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
process
-
Pure virtual method. -

Derived classes must implement this method.

-

A client must only call this method when - RecordProcessRequester::becomeProcessor is called as a result - of a queueProcessRequest. - A client can either call lock before calling processs - or let process lock the record. - If a client wants to put data into the record it should lock, put, and then call - process.

-

If the record is synchronous, process will return only when all processing - is complete. If the record is asynchronous then process arranges for some - other thread to do the processing and returns.

-

When processing is done the record calls two client callbacks:

-
-
RecordProcessRequester::recordProcessResult
-
This is called with the record still locked. - The clients can get data from the record.
-
RecordProcessRequester::recordProcessComplete
-
This is called with the record unlocked. - The client can no longer access the record.
-
-
-
isSynchronous
-
Pure virtual method. Derived classes must implement this method.
-
requestImmediatePut
-
This is a virtual method. -

The purpose is to allow the implementation to provide fields - that allow a client to abort process. - For example a motor record might provide a field stop

-

The default always returns false.

-

A record implementation can override the default and return true. - In it does requestImmediatePut it returns with the record locked.

-

The client can change the value of the associated field and then call - immediatePutDone

-
-
immediatePutDone
-
This is a virtual method. -

The default does nothing.

-

Must be called by client as a result of a call to requestImmediatePut - that returns true.

-
-
destroy
-
This is a virtual method. -

The default does nothing.

-
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
queueProcessRequest
-
Queue a process request.
-
dequeueProcessRequest
-
This must be called by record implementation after it has - completed a process request. -
-
queuePutRequest
-
Queue a put request. -

This is for code that wants to change data in a record without processing. - If RecordPutRequester::requestResult is called with result true - then the record is locked and the client can make changes. - When done the client must call putDone

-
-
putDone
-
Called by RecordPutRequester after changing values in record. - This method unlocks the record
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
init
-
This method must be called by derived class - after class is completely constructed.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
-

class RecordProcessRequester

-
-class RecordProcessRequester :
-    virtual public PVRecordClient,
-    virtual public epics::pvData::Requester
-{
-public:
-    POINTER_DEFINITIONS(RecordProcessRequester);
-    virtual ~RecordProcessRequester();
-    virtual void recordDestroyed() = 0;
-    virtual void becomeProcessor() = 0;
-    virtual void recordProcessResult(epics::pvData::Status status) = 0;
-    virtual void recordProcessComplete() = 0;
-};
-
-

where

-
-
~RecordProcessRequester
-
The destructor.
-
recordDestroyed
-
Record is being destroyed.
-
becomeProcessor
-
Called as a result of queueRequeProcessst. The requester can the call process.
-
recordProcessResult
-
The results of record processing. - This is called with the record locked so that the process requester - can access data from the record.
-
recordProcessComplete
-
Processing is complete. - This is called with the record unlocked. - If the process requester called process with leaveActive true then the requester - must call setInactive.
-
-

class RecordPutRequester

-
-class RecordPutRequester :
-    virtual public PVRecordClient
-{
-public:
-    POINTER_DEFINITIONS(RecordPutRequester);
-    virtual ~RecordPutRequester();
-    virtual void requestResult(bool result) = 0;
-};
-
-

where

-
-
~RecordPutRequester
-
The destructor.
-
requestResult
-
Result of a call to queuePutRequest. If requestResult is false - then the caller can not access the record. - If requestResult is true - then the record is locked and the caller can get and put data in the record. - When done the caller must call PVRecord::putDone, which will unlock the - record. -
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    bool removeRecord(PVRecordPtr const & record);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
-

Local Channel Provider

-

Not yet described.

-

A brief description is that it must implement the following components of pvIOCJava:

-
-
pvCopy
-
monitor
-
pvAccess
-
See the next section for a description
-
-

Summary of Packages in pvIOCJAVA

-

The following are the direct sub packages of pvIOCJava/src/org/epics/pvioc:

-
-
pvCopy
-
This provides a copy of an arbitrary subset of the fields in a PVRecord. - It also provides the ability to detect and report changes to fields. - It is required for pvAccess.
-
monitor
-
This provides the ability to monitor changes to a PVRecord. It is required for pvAccess monitors.
-
pvAccess
-
The local implementation of Channel Provider and Channel. - It is accessed by the remote pvAccess server and can also be accessed by code in the same IOC.
-
database
-
This defines and implements PVRecord, PVDatabase , and PVListener. - It supports the basic feature required the implement a local Channel Provider.
-
support
-
This provides the ability to optionally attach code to any field of a pvRecord. - It and several sub packages provide a set of standard support modules.
-
install
-
This provides the ability to dynamically initialize and add new PVRecords. It also provides - the ability to dynamicall delete PVRecords. -
xml
-
This provides the ability to configure record instances without writing code.
-
util
-
This is misnamed since it is code related to scanning.
-
pdrv
-
This is portDriver, which is a proposed sucessor to the asynManager component of asynDriver.
-
swtshell
-
This is shell that is can either run under the same process as a JavaIOC or as a remote shell. - It is like a version of probe but for pvData/pvAccess. - Almost all of it's features work in either local or remote mode. - With a little more work all or it's features could work remotely. - This should be done and then only remote mode should be supported. - It can then be rewritten in a completely different language and using a complely different GUI - framework. -
-
caV3
-
This has two components: -
-
ClientFactory
-
This is a small wrapper on top of the caV3 client support implemented by pvAccess. - It allows code in the pvIOC to access V3Records via pvAccess.
-
ServerFactory
-
This is a caV3 server that allows a caV3 client to access a PVRecord. - The Java implementation uses CAJ, which does most of the work. - For now it will not be discussed in this document.
-
-
-
v3a
-
I do not know what this is.
-
-

In addition there is one class file JavaIOC.java. -This is starting a IOC instance. -This is not required for pvIOCCPP which is either a main or runs as part of a V3 IOC.

- -
- - diff --git a/documentation/pvDatabaseCPP_20130417.html b/documentation/pvDatabaseCPP_20130417.html deleted file mode 100644 index ec27e30..0000000 --- a/documentation/pvDatabaseCPP_20130417.html +++ /dev/null @@ -1,870 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 17-Apr-2013

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20130417.html -
-
Previous version:
-
pvDatabaseCPP20121211.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 17-Apr-2013 version of the definition of pvDatabaseCPP. -

-

The following Channel methods are implemented and working: getField,i -channelProcess, channelGet, channelPut, and channelPutGet. -But lots of work remains:

-
-
Other Channel Methods
-
Monitor is next.
-
Memory leaks at exit
-
May need help from Matej.
-
Scalar Arrays
-
Have not been tested. Share has not been implemented.
-
Structure Arrays
-
Has not been implemented
-
Testing
-
Needs lots more testing
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provides the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
-

database provides base classes that make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following two methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
-

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP extracts the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Example PVRecord Extension

-

The example implements a simple counter. -The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounter 
-
-
-

The example consists of two components:

-
-
ExampleCounter.h
-
The source code for the counter.
-
exampleCounterMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client.
-
-

ExampleCounter.h

-

The example resides in src/database. -The complete implementation is in the header file. -A serious implementation would probably break the code into two files: -1) a header, and 2) the implementation. The description consists of

-
-class ExampleCounter;
-typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
-
-class ExampleCounter :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleCounter);
-    static ExampleCounterPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleCounter();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleCounter(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    epics::pvData::PVLongPtr pvValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleCounter
-
The destructor must be declared virtual.
-
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleCounter
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleCounterPtr ExampleCounter::create(
-    epics::pvData::String const & recordName)
-{
-    epics::pvData::PVStructurePtr pvStructure =
-       epics::pvData::getStandardPVField()->scalar(
-           epics::pvData::pvDouble,"timeStamp,alarm"");
-    ExampleCounterPtr pvRecord(
-        new ExampleCounter(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleCounterPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleCounter::ExampleCounter(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-}
-
-The example is very simple. It just calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleCounter::~ExampleCounter()
-{
-    destroy();
-}
-
-void ExampleCounter::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor just calls destroy. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleCounter::init()
-{
-
-    initPVRecord();
-    epics::pvData::PVFieldPtr pvField;
-    pvValue = getPVStructure()->getLongField("value");
-    if(pvValue.get()==NULL) return false;
-    return true;
-}
-
-This: -
    -
  • Calls initRecord which is implemented by the base class. - It MUST be called.
  • -
  • Calls getLongField to get the interface to the value field, - which must be a scalar with type long.
  • -
  • If a long value field was not found it returns false.
  • -
  • Returns true
  • -
-

The implementation of process is:

-
-void ExampleCounter::process()
-{
-    pvValue->put(pvValue->get() + 1.0);
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It adds 1.0 to the current value. -It then sets the timeStamp to the current time. -

exampleCounterMain.cpp

-

This is in test/server. -The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleCounter");
-    PVRecordPtr pvRecord = ExampleCounter::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    cout << "exampleServer\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleCounter record with the name exampleCounter -
  • -
  • Prints exampleCounter on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the pvAccess package in pvIOCJava. - The localChannelProvider will access data from PVRecords. - It will implement all channel methods.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

database

-

This directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVREcord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
exampleCounter.h
-
- This was described in the introduction. -
-
powerSupplyRecordTest.h
-
- This provides code that simulates a power supply. - It computes the current from the voltage and power. - It is used for testing. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordProcessRequester;
-typedef std::tr1::shared_ptr<RecordProcessRequester> RecordProcessRequesterPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    virtual void destroy();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are: -

-
init
-
Virtual method. -

Derived classes must implement this method. - This method Must call initPVRecord.

-
-
process
-
Virtual method. -

Derived classes must implement this method. - The base implementation does nothing.

-
-
create
-
Static method to create dumb records, i.e. records with a process method - that does nothing.
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
destroy
-
This is a virtual method. -
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

Not yet described. -It is only of interest to someone who wants to understand how it works. -

-

A brief description is that it implements the following components of pvIOCJava:

-
-
pvCopy
-
monitor
-
local ChannelProvider and Channel
-
- -
- - diff --git a/documentation/pvDatabaseCPP_20130516.html b/documentation/pvDatabaseCPP_20130516.html deleted file mode 100644 index 323a759..0000000 --- a/documentation/pvDatabaseCPP_20130516.html +++ /dev/null @@ -1,876 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 16-May-2013

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20130516.html -
-
Previous version:
-
pvDatabaseCPP20130417.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 16-May-2013 version of the definition of pvDatabaseCPP. -

-

The following Channel methods are implemented and working: getField, -channelProcess, channelGet, channelPut, channelPutGet, and Monitor. -But lots of work remains:

-
-
Other Channel Methods
-
ChannelArray is next.
-
Monitor Algorithms
-
Monitor algorithms have no been implemented. - Thus all monitors are onPut.
-
Lifecycle problems
-
Problems when channel clients disconnect. - May need help from Matej
-
Memory leaks at exit
-
May need help from Matej.
-
Scalar Arrays
-
Have not been tested. Share has not been implemented.
-
Structure Arrays
-
Has not been implemented
-
Testing
-
Needs lots more testing
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provides the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
-

database provides base classes that make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following two methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
-

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP extracts the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Example PVRecord Extension

-

The example implements a simple counter. -The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounter 
-
-
-

The example consists of two components:

-
-
ExampleCounter.h
-
The source code for the counter.
-
exampleCounterMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client.
-
-

ExampleCounter.h

-

The example resides in src/database. -The complete implementation is in the header file. -A serious implementation would probably break the code into two files: -1) a header, and 2) the implementation. The description consists of

-
-class ExampleCounter;
-typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
-
-class ExampleCounter :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleCounter);
-    static ExampleCounterPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleCounter();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleCounter(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    epics::pvData::PVLongPtr pvValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleCounter
-
The destructor must be declared virtual.
-
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleCounter
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleCounterPtr ExampleCounter::create(
-    epics::pvData::String const & recordName)
-{
-    epics::pvData::PVStructurePtr pvStructure =
-       epics::pvData::getStandardPVField()->scalar(
-           epics::pvData::pvDouble,"timeStamp,alarm"");
-    ExampleCounterPtr pvRecord(
-        new ExampleCounter(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleCounterPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleCounter::ExampleCounter(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-}
-
-The example is very simple. It just calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleCounter::~ExampleCounter()
-{
-    destroy();
-}
-
-void ExampleCounter::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor just calls destroy. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleCounter::init()
-{
-
-    initPVRecord();
-    epics::pvData::PVFieldPtr pvField;
-    pvValue = getPVStructure()->getLongField("value");
-    if(pvValue.get()==NULL) return false;
-    return true;
-}
-
-This: -
    -
  • Calls initRecord which is implemented by the base class. - It MUST be called.
  • -
  • Calls getLongField to get the interface to the value field, - which must be a scalar with type long.
  • -
  • If a long value field was not found it returns false.
  • -
  • Returns true
  • -
-

The implementation of process is:

-
-void ExampleCounter::process()
-{
-    pvValue->put(pvValue->get() + 1.0);
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It adds 1.0 to the current value. -It then sets the timeStamp to the current time. -

exampleCounterMain.cpp

-

This is in test/server. -The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleCounter");
-    PVRecordPtr pvRecord = ExampleCounter::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    cout << "exampleServer\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleCounter record with the name exampleCounter -
  • -
  • Prints exampleCounter on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the pvAccess package in pvIOCJava. - The localChannelProvider will access data from PVRecords. - It will implement all channel methods.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

database

-

This directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVREcord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
exampleCounter.h
-
- This was described in the introduction. -
-
powerSupplyRecordTest.h
-
- This provides code that simulates a power supply. - It computes the current from the voltage and power. - It is used for testing. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordProcessRequester;
-typedef std::tr1::shared_ptr<RecordProcessRequester> RecordProcessRequesterPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    virtual void destroy();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are: -

-
init
-
Virtual method. -

Derived classes must implement this method. - This method Must call initPVRecord.

-
-
process
-
Virtual method. -

Derived classes must implement this method. - The base implementation does nothing.

-
-
create
-
Static method to create dumb records, i.e. records with a process method - that does nothing.
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
destroy
-
This is a virtual method. -
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

Not yet described. -It is only of interest to someone who wants to understand how it works. -

-

A brief description is that it implements the following components of pvIOCJava:

-
-
pvCopy
-
monitor
-
local ChannelProvider and Channel
-
- -
- - diff --git a/documentation/pvDatabaseCPP_20130523.html b/documentation/pvDatabaseCPP_20130523.html deleted file mode 100644 index 34fa7a2..0000000 --- a/documentation/pvDatabaseCPP_20130523.html +++ /dev/null @@ -1,1001 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 23-May-2013

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20130523.html -
-
Previous version:
-
pvDatabaseCPP20130516.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 23-May-2013 version of the definition of pvDatabaseCPP. -

-

The following Channel methods are implemented and working: getField, -channelProcess, channelGet, channelPut, channelPutGet, and Monitor. -But lots of work remains:

-
-
Other Channel Methods
-
Only ChannelArray remains. - Note that pvIOCJava does not implement the pvRequest for channelArray - correctly. It uses a private convention rather than using the output - of CreateRequest. This will be fixed before pvDatabaseCPP implements ChannelArray. -
-
Monitor Algorithms
-
Monitor algorithms have not been implemented. - Thus all monitors are onPut.
-
Lifecycle problems
-
Problems when channel clients disconnect. - I am asking for help from Matej
-
Memory leak at exit
-
I am asking for help from Matej
-
Scalar Arrays
-
Share has not been implemented. - This will wait until Michael has new implementation of ScalarArray.
-
Structure Arrays
-
Has not been implemented
-
Testing
-
Needs lots more testing
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
-

database provides base classes that make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following two methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
-

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP extracts the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Example PVRecord Extension

-

The example implements a simple counter. -The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounter 
-
-
-

The example consists of two components:

-
-
ExampleCounter.h
-
The source code for the counter.
-
exampleCounterMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client.
-
-

ExampleCounter.h

-

The example resides in src/database. -The complete implementation is in the header file. -A serious implementation might break the code into a header and an -implementation file.

-

The description consists of

-
-class ExampleCounter;
-typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
-
-class ExampleCounter :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleCounter);
-    static ExampleCounterPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleCounter();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleCounter(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    epics::pvData::PVLongPtr pvValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleCounter
-
The destructor must be declared virtual.
-
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleCounter
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleCounterPtr ExampleCounter::create(
-    epics::pvData::String const & recordName)
-{
-    epics::pvData::PVStructurePtr pvStructure =
-       epics::pvData::getStandardPVField()->scalar(
-           epics::pvData::pvDouble,"timeStamp,alarm"");
-    ExampleCounterPtr pvRecord(
-        new ExampleCounter(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleCounterPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleCounter::ExampleCounter(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-}
-
-The example is very simple. It just calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleCounter::~ExampleCounter()
-{
-    destroy();
-}
-
-void ExampleCounter::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor just calls destroy. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleCounter::init()
-{
-
-    initPVRecord();
-    epics::pvData::PVFieldPtr pvField;
-    pvValue = getPVStructure()->getLongField("value");
-    if(pvValue.get()==NULL) return false;
-    return true;
-}
-
-This: -
    -
  • Calls initRecord which is implemented by the base class. - It MUST be called.
  • -
  • Calls getLongField to get the interface to the value field, - which must be a scalar with type long.
  • -
  • If a long value field was not found it returns false.
  • -
  • Returns true
  • -
-

The implementation of process is:

-
-void ExampleCounter::process()
-{
-    pvValue->put(pvValue->get() + 1.0);
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It adds 1.0 to the current value. -It then sets the timeStamp to the current time. -

exampleCounterMain.cpp

-

This is in test/server. -The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleCounter");
-    PVRecordPtr pvRecord = ExampleCounter::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    cout << "exampleServer\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleCounter record with the name exampleCounter -
  • -
  • Prints exampleCounter on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the pvAccess package in pvIOCJava. - The localChannelProvider will access data from PVRecords. - It will implement all channel methods.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

database

-

This directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVREcord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
exampleCounter.h
-
- This was described in the introduction. -
-
powerSupplyRecordTest.h
-
- This provides code that simulates a power supply. - It computes the current from the voltage and power. - It is used for testing. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The testExampleServer creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-      
-
- -
-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordProcessRequester;
-typedef std::tr1::shared_ptr<RecordProcessRequester> RecordProcessRequesterPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    virtual void destroy();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are: -

-
init
-
Virtual method. -

Derived classes must implement this method. - This method Must call initPVRecord.

-
-
process
-
Virtual method. -

Derived classes must implement this method. - The base implementation does nothing.

-
-
create
-
Static method to create dumb records, i.e. records with a process method - that does nothing.
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
destroy
-
This is a virtual method. -
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is access by the server side of remote pvAccess.

-

channelProviderLocal

-

This is a complete implementation of channelProvider and , -except for channelRPC, provides a complete implementation of Channel -as defined by pvAccess. -For monitors it calls the code described in the following sections.

-

ChannelLocalDebug

-

The channelProvider implementation provides the ability to generate -debug messages based a debug level with the following meaning:

-
-
<=0
-
No debug messages
-
>0
-
Generate a message when anything is created or destroyed
-
>1
-
Also generate processing messages.
-
-

ChannelProviderLocal has a method:

-
-    void createChannelLocalDebugRecord(
-        String const & recordName);
-
-

This method creates a PVRecord that allows a pvAccess client to set the -debug level.

-

pvCopy

-

This provides code that creates a top level PVStructure that is an arbitrary -subset of the fields in the PVStructure from a PVRecord. -In addition it provides code that monitors changes to the fields in a PVRecord. -A client configures the desired set of subfields and monitoring options -via a pvRequest structure. -pvAccess provides a class CreatePVRequest that creates a pvRequest. -The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava. -

-

monitorAlgorithm

-

Currently all that is implemented is a header file. -The only algorithm currently implemented is onPut -

-

monitorFactory

-

Overview

-

epics::pvData::monitor defines the monitor interfaces -as seen by a client. -See - pvDatabaseCPP.html - For details.

-

-monitorFactory implements the -monitoring interfaces for a PVRecord. -It implements queueSize=0 and queueSize>=2. -

- -

-The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy. -When PVCopyMonitor tells monitor that changes -have occurred, monitor applies the appropriate algorithm to each changed field.

- -

Currently only algorithm onPut is implemented but, -like pvIOCJava there are plans to support for the following monitor algorithms:

-
-
onPut
-
A monitor is issued whenever a put is issued to the field. This is the - default unless the record defines deadbands for a field. An exception is - the top level timeStamp which by default is made onChange and monitor - will not be raised.
-
onChange
-
This provides two options: 1) A monitor is raised whenever a field - changes value, and 2) A monitor will never be raised for the field.
-
deadband
-
The field must be a numeric scalar. Whenever the absolute or percentage - value of the field changes by more than a deadband a monitor is issued. - The record instance can also define deadbands.
-
periodic
-
A monitor is issued at a periodic rate if a put was issued to any field - being monitored.
-
-

MonitorFactory

-

MonitorFactory provides the following methods:

-
class MonitorFactory
-{
-    static MonitorPtr create(
-        PVRecordPtr const & pvRecord,
-        MonitorRequester::shared_pointer const & monitorRequester,
-        PVStructurePtr const & pvRequest);
-    static void registerMonitorAlgorithmCreater(
-        MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
-        String const & algorithmName);
-}
- -

where

-
-
create
-
Create a monitor. The arguments are: -
-
pvRecord
-
The record being monitored.
-
monitorRequester
-
The monitor requester. This is the code to which monitot events - will be delivered.
-
pvRequest
-
The request options
-
-
-
registerMonitorAlgorithmCreater
-
Called by code that implements a monitor algorithm.
-
-

channelLocalDebugRecord

-

This implements a PVRecord that allows a client to set -a debug level for the local channel provider implementation. -The top level structure has a single integer field named value. -See ChannelProviderLocal for the meaning associated with value.

-

ChannelProviderLocal has a method:

-
-    void createChannelLocalDebugRecord(String const & recordName);
-
-

This creates an instance of a ChannelLocalDebugRecord and installs it -into the PVDatabase.

-
- - diff --git a/documentation/pvDatabaseCPP_20130627.html b/documentation/pvDatabaseCPP_20130627.html deleted file mode 100644 index e51b3c1..0000000 --- a/documentation/pvDatabaseCPP_20130627.html +++ /dev/null @@ -1,1155 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 27-Jun-2013

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20130627.html -
-
Previous version:
-
pvDatabaseCPP20130523.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 27-Jun-2013 version of the definition of pvDatabaseCPP. -

-

The following Channel methods are implemented and working: getField, -channelProcess, channelGet, channelPut, channelPutGet,channelArray and Monitor. -But work remains:

-
-
Other Channel Methods
-
channlRPC will not be implemented because pvAccess itself - provides what is required to easily implement channelRPC requests. -
-
Monitor Algorithms
-
Monitor algorithms have not been implemented. - Thus all monitors are onPut.
-
Memory leaks
-
I think all memory leaks have been fixed.
-
Scalar Arrays
-
Share has not been implemented. - This will wait for a new implementation of ScalarArray.
-
Structure Arrays
-
Has not been implemented
-
toString
-
The toString methods should be replaced by stream operator<<. -
-
Testing
-
Needs more testing
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
-

database provides base classes that make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resorurces used by the impplementation.
-
-

Getting started

-

Included with this project are two main programs that are useful for -seeing how pvDatabase can be used by clients. -The programs are: -

-
exampleCounterMain
-
This has a database consisting of two records: - The exampleCounter discussed in a following section and a record - that allows a pvAccess client to set the trace level of the - exampleCounter. This is also discussed below. -
-
testExamplServerMain
-
This has a database with several records.
- -

-

To start one of the programs on linux, do the following: -

-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/testExampleServer 
-

-

The Java programs - - swtshellJava.html -can be used to access the database.

-

In particular read the sections "Getting Started" and "Simple Example". -They will work on the testExampleServer with the following differences: -

-
startExample.zip
-
Do NOT use this. Instead run testExampleServer
-
channelList result
-
The result of channelList will show the list of records that - testExampleServer has rather than the records from startExample.zip
-
-

-

The database has the following records: -

-
exampleCounter
-
A record that is an instance of exampleCounter described below. - The most useful channel methods are channelGet, channelProcess, - and monitor.
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
examplePowerSupply
-
Can be used by channelGet, channelPut, channelPutGet, and monitor.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet but unnecessary.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
- -

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP extracts the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Example PVRecord Extension

-

The example implements a simple counter. -The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounter 
-
-
-

The example consists of two components:

-
-
ExampleCounter.h
-
The source code for the counter.
-
exampleCounterMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client.
-
-

ExampleCounter.h

-

The example resides in src/database. -The complete implementation is in the header file. -A serious implementation might break the code into a header and an -implementation file.

-

The description consists of

-
-class ExampleCounter;
-typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
-
-class ExampleCounter :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleCounter);
-    static ExampleCounterPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleCounter();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleCounter(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    epics::pvData::PVLongPtr pvValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleCounter
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleCounter
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleCounterPtr ExampleCounter::create(
-    epics::pvData::String const & recordName)
-{
-    epics::pvData::PVStructurePtr pvStructure =
-       epics::pvData::getStandardPVField()->scalar(
-           epics::pvData::pvDouble,"timeStamp,alarm"");
-    ExampleCounterPtr pvRecord(
-        new ExampleCounter(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleCounterPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleCounter::ExampleCounter(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleCounter::~ExampleCounter()
-{
-}
-
-void ExampleCounter::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleCounter::init()
-{
-
-    initPVRecord();
-    epics::pvData::PVFieldPtr pvField;
-    pvValue = getPVStructure()->getLongField("value");
-    if(pvValue.get()==NULL) return false;
-    return true;
-}
-
-This: -
    -
  • Calls initRecord which is implemented by the base class. - It MUST be called.
  • -
  • Calls getLongField to get the interface to the value field, - which must be a scalar with type long.
  • -
  • If a long value field was not found it returns false.
  • -
  • Returns true
  • -
-

The implementation of process is:

-
-void ExampleCounter::process()
-{
-    pvValue->put(pvValue->get() + 1.0);
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It adds 1.0 to the current value. -It then sets the timeStamp to the current time. -

exampleCounterMain.cpp

-

This is in test/server.

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how the create additional records. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleCounter");
-    PVRecordPtr pvRecord = ExampleCounter::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    cout << "exampleServer\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleCounter record with the name exampleCounter -
  • -
  • Prints exampleCounter on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the pvAccess package in pvIOCJava. - The localChannelProvider will access data from PVRecords. - It will implement all channel methods.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

database

-

This directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
exampleCounter.h
-
- This was described in the introduction. -
-
powerSupplyRecordTest.h
-
- This provides code that simulates a power supply. - It computes the current from the voltage and power. - It is used for testing. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The testExampleServer creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-      
-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
process
-
Virtual method. - Derived classes must implement this method. - The base implementation does nothing. -
-
destroy
-
This is a virtual method. - A derived class must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it';s own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is access by the server side of remote pvAccess.

-

channelProviderLocal

-

This is a complete implementation of channelProvider and , -except for channelRPC, provides a complete implementation of Channel -as defined by pvAccess. -For monitors it calls the code described in the following sections.

-

pvCopy

-

This provides code that creates a top level PVStructure that is an arbitrary -subset of the fields in the PVStructure from a PVRecord. -In addition it provides code that monitors changes to the fields in a PVRecord. -A client configures the desired set of subfields and monitoring options -via a pvRequest structure. -pvAccess provides a class CreatePVRequest that creates a pvRequest. -The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava. -

-

monitorAlgorithm

-

Currently all that is implemented is a header file. -The only algorithm currently implemented is onPut -

-

monitorFactory

-

Overview

-

epics::pvData::monitor defines the monitor interfaces -as seen by a client. -See - pvDatabaseCPP.html - For details.

-

-monitorFactory implements the -monitoring interfaces for a PVRecord. -It implements queueSize=0 and queueSize>=2. -

- -

-The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy. -When PVCopyMonitor tells monitor that changes -have occurred, monitor applies the appropriate algorithm to each changed field.

- -

Currently only algorithm onPut is implemented but, -like pvIOCJava there are plans to support for the following monitor algorithms:

-
-
onPut
-
A monitor is issued whenever a put is issued to the field. This is the - default unless the record defines deadbands for a field. An exception is - the top level timeStamp which by default is made onChange and monitor - will not be raised.
-
onChange
-
This provides two options: 1) A monitor is raised whenever a field - changes value, and 2) A monitor will never be raised for the field.
-
deadband
-
The field must be a numeric scalar. Whenever the absolute or percentage - value of the field changes by more than a deadband a monitor is issued. - The record instance can also define deadbands.
-
periodic
-
A monitor is issued at a periodic rate if a put was issued to any field - being monitored.
-
-

MonitorFactory

-

MonitorFactory provides the following methods:

-
class MonitorFactory
-{
-    static MonitorPtr create(
-        PVRecordPtr const & pvRecord,
-        MonitorRequester::shared_pointer const & monitorRequester,
-        PVStructurePtr const & pvRequest);
-    static void registerMonitorAlgorithmCreater(
-        MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
-        String const & algorithmName);
-}
- -

where

-
-
create
-
Create a monitor. The arguments are: -
-
pvRecord
-
The record being monitored.
-
monitorRequester
-
The monitor requester. This is the code to which monitot events - will be delivered.
-
pvRequest
-
The request options
-
-
-
registerMonitorAlgorithmCreater
-
Called by code that implements a monitor algorithm.
-
-

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generted - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

-

recordList

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string database master
-        string regularExpression .*
-    structure result
-        string status 
-        string[] names
-
-where: -
-
database
-
The name of the datbase. The default is "master"
-
regularExpression
-
For now this is ignored and the complete list of names is always - returned.
-
status
-
The status of a putGet request.
-
names
-
The list of record names.
-
-

Note that swtshell has a command channelList that -requires that a record of this type is present and calls it. -Thus user code does not have to use a channelGetPut to get the list -of record names.

-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

powerSupplyRecordTest

-

This simulates a simple power supply record. -It is used for testing.

- -
- - diff --git a/documentation/pvDatabaseCPP_20130725.html b/documentation/pvDatabaseCPP_20130725.html deleted file mode 100644 index 79c5897..0000000 --- a/documentation/pvDatabaseCPP_20130725.html +++ /dev/null @@ -1,1244 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 25-Jul-2013

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20130725.html -
-
Previous version:
-
pvDatabaseCPP20130627.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 25-Jul-2013 version of the definition of pvDatabaseCPP. -

NOTE: -This is built against pvDataCPP-md NOT against pvDataCPP. -To build you must also -checkout pvAccessCPP and build it against pvDataCPP-md. -

-

-

All channel methods except channelRPCi, which is implemented -by pvAccess, have been implemented. -This project is ready for alpha users. -

-

Future enhancements in priority order:

-
-
pvAccess directly to local channelProvider
-
Create an example record that communicates with another - record via pvAccess but directly connects to channelProviderLocal. - Thus channelGet and monitor of arrays will be done without - copying raw array data.
-
Array performance
-
Create an example record that continuously creates array data. - The idea is to compare performance allocating memory from the - heap vs using a free list.
-
Separate example that also has pvaSrv
-
Create a separate example that combines a V3IOC, - a pvDatabase, and pvaSrv.
-
Monitor Algorithms
-
Monitor algorithms have not been implemented. - Thus all monitors are onPut.
-
Memory leaks
-
I think all memory leaks have been fixed.
-
Testing
-
Needs more testing
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In tha later case the IOC has both a database of V3 Records - and a pvDatabase.
-
exampleCounter
-
This is a simple example showing how to create a PVRecord and - how to deploy it either as a standalone process or as part of a V3IOC.
-
exampleServer
-
This example has a set of PVRecords. - Again the records can be deployed either as a standalone process or - as part of a V3IOC. -
-

database provides base classes that make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resorurces used by the impplementation.
-
-

Getting started

-

Included with this project are two examples that are useful for -seeing how pvDatabase can be used by clients. -Each can be deployed either as a standalone process or as part of a V3IOC. -The examples are: -

-
exampleCounterMain
-
This has a database consisting of a single record named exampleCounter: - The exampleCounter is discussed in a following section.
-
exampleCounter
-
This is exampleCounter as part of a V3IOC.
-
exampleServerMain
-
This has a database with several records.
-
exampleServer
-
This is exampleServer as part of a V3IOC
- -

-

exampleCounter

-

To start exampleCounterMain: -

-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounterMain
-

-

To start exampleCounter as part of a V3IOC: -

-mrk> pwd
-/home/hg/pvDatabaseCPP/iocBoot/exampleCounter
-mrk> ../../../bin/linux-x86_64/exampleCounter st.cmd
-

-

You can then issue the commands dbl and pvdbl: -

-epics> dbl
-double01
-epics> pvdbl
-exampleCounter
-epics> 
-
-double01 is a v3Record. -exampleCounter is a pvRecord. -

-

Starting exampleServer is similar. -After successfully running exampleCounterMain and exampleCounter then -try starting exampleServerMain and exampleServer. -

-

swtshell

-

The Java program - - swtshell -can be used to access pvDatabase.

-

In particular read the sections "Getting Started" and "Simple Example". -They will work on the exampleServer with the following differences: -

-
startExample.zip
-
Do NOT use this. Instead run exampleServer
-
channelList result
-
The result of channelList will show the list of records that - exampleServer has rather than the records from startExample.zip
-
-

-

exampleServer

-

The exampleServer pvDatabase includes the following records: -

-
exampleCounter
-
A record that is an instance of exampleCounter described below. - The most useful channel methods are channelGet, channelProcess, - and monitor.
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
examplePowerSupply
-
Can be used by channelGet, channelPut, channelPutGet, and monitor.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet but unnecessary.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

- -

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP extracts the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Example PVRecord Extension

-

The example implements a simple counter. -The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounter 
-
-
-

The example consists of four components:

-
-
ExampleCounter.h
-
The source code for the counter. - It is located in directory pvDatabaseCPP/example/src/exampleCounter. -
-
exampleCounterMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. - It is located in directory pvDatabaseCPP/example/exampleCounter. -
-
v3IOC/exampleCounter
-
This is a directory that packages exampleCounter to make it - part of a v3IOC.
-
iocBoot/exampleCounter
-
A place to start exampleCounter as part of a v3IOC. - It follows the normal iocCore conventions.
-
-

ExampleCounter.h

-

The example resides in src/database. -The complete implementation is in the header file. -A serious implementation might break the code into a header and an -implementation file.

-

The description consists of

-
-class ExampleCounter;
-typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
-
-class ExampleCounter :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleCounter);
-    static ExampleCounterPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleCounter();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleCounter(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    epics::pvData::PVLongPtr pvValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleCounter
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleCounter
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleCounterPtr ExampleCounter::create(
-    epics::pvData::String const & recordName)
-{
-    epics::pvData::PVStructurePtr pvStructure =
-       epics::pvData::getStandardPVField()->scalar(
-           epics::pvData::pvDouble,"timeStamp,alarm"");
-    ExampleCounterPtr pvRecord(
-        new ExampleCounter(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleCounterPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleCounter::ExampleCounter(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleCounter::~ExampleCounter()
-{
-}
-
-void ExampleCounter::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleCounter::init()
-{
-
-    initPVRecord();
-    epics::pvData::PVFieldPtr pvField;
-    pvValue = getPVStructure()->getLongField("value");
-    if(pvValue.get()==NULL) return false;
-    return true;
-}
-
-This: -
    -
  • Calls initRecord which is implemented by the base class. - It MUST be called.
  • -
  • Calls getLongField to get the interface to the value field, - which must be a scalar with type long.
  • -
  • If a long value field was not found it returns false.
  • -
  • Returns true
  • -
-

The implementation of process is:

-
-void ExampleCounter::process()
-{
-    pvValue->put(pvValue->get() + 1.0);
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It adds 1.0 to the current value. -It then sets the timeStamp to the current time. -

exampleCounterMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleCounter");
-    PVRecordPtr pvRecord = ExampleCounter::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    cout << "exampleCounter\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleCounter record with the name exampleCounter -
  • -
  • Prints exampleCounter on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

v3IOC exampleCounter

-

This has two subdirectories: -

-
Db
-
This has a template for a single v3Record.
-
src
-
This has code to allow exampleCounter to reside in a v3IOC.
-
-

-

The src directory has the following components:

-
-
exampleCounterMain.cpp
-
This is just a standard Main for a v3IOC.
-
exampleCounterInclude.dbd
-
This is: -
-include "base.dbd"
-include "PVAServerRegister.dbd"
-registrar("exampleCounterRegister")
-       
- This includes the dbd components required from base, - the dbd file for starting pvAccess and the local channelProvider, - and the dbd file for the example. The later is for the code from the - next file. -
-
exampleCounter.cpp
-
This is the code that registers a command the can be issued - via the iocsh, which is the console for a v3IOC. - The example supports a single command: -
-exampleCounterCreateRecord recordName
-       
-
-
-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the pvAccess package in pvIOCJava. - The localChannelProvider will access data from PVRecords. - It will implement all channel methods.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

database

-

This directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
exampleCounter.h
-
- This was described in the introduction. -
-
powerSupplyRecordTest.h
-
- This provides code that simulates a power supply. - It computes the current from the voltage and power. - It is used for testing. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The testExampleServer creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-      
-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
process
-
Virtual method. - Derived classes must implement this method. - The base implementation does nothing. -
-
destroy
-
This is a virtual method. - A derived class must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it';s own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is access by the server side of remote pvAccess.

-

channelProviderLocal

-

This is a complete implementation of channelProvider and , -except for channelRPC, provides a complete implementation of Channel -as defined by pvAccess. -For monitors it calls the code described in the following sections.

-

pvCopy

-

This provides code that creates a top level PVStructure that is an arbitrary -subset of the fields in the PVStructure from a PVRecord. -In addition it provides code that monitors changes to the fields in a PVRecord. -A client configures the desired set of subfields and monitoring options -via a pvRequest structure. -pvAccess provides a class CreatePVRequest that creates a pvRequest. -The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava. -

-

monitorAlgorithm

-

Currently all that is implemented is a header file. -The only algorithm currently implemented is onPut -

-

monitorFactory

-

Overview

-

epics::pvData::monitor defines the monitor interfaces -as seen by a client. -See - pvDatabaseCPP.html - For details.

-

-monitorFactory implements the -monitoring interfaces for a PVRecord. -It implements queueSize=0 and queueSize>=2. -

- -

-The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy. -When PVCopyMonitor tells monitor that changes -have occurred, monitor applies the appropriate algorithm to each changed field.

- -

Currently only algorithm onPut is implemented but, -like pvIOCJava there are plans to support for the following monitor algorithms:

-
-
onPut
-
A monitor is issued whenever a put is issued to the field. This is the - default unless the record defines deadbands for a field. An exception is - the top level timeStamp which by default is made onChange and monitor - will not be raised.
-
onChange
-
This provides two options: 1) A monitor is raised whenever a field - changes value, and 2) A monitor will never be raised for the field.
-
deadband
-
The field must be a numeric scalar. Whenever the absolute or percentage - value of the field changes by more than a deadband a monitor is issued. - The record instance can also define deadbands.
-
periodic
-
A monitor is issued at a periodic rate if a put was issued to any field - being monitored.
-
-

MonitorFactory

-

MonitorFactory provides the following methods:

-
class MonitorFactory
-{
-    static MonitorPtr create(
-        PVRecordPtr const & pvRecord,
-        MonitorRequester::shared_pointer const & monitorRequester,
-        PVStructurePtr const & pvRequest);
-    static void registerMonitorAlgorithmCreater(
-        MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
-        String const & algorithmName);
-}
- -

where

-
-
create
-
Create a monitor. The arguments are: -
-
pvRecord
-
The record being monitored.
-
monitorRequester
-
The monitor requester. This is the code to which monitot events - will be delivered.
-
pvRequest
-
The request options
-
-
-
registerMonitorAlgorithmCreater
-
Called by code that implements a monitor algorithm.
-
-

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generted - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

-

recordList

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string database master
-        string regularExpression .*
-    structure result
-        string status 
-        string[] names
-
-where: -
-
database
-
The name of the datbase. The default is "master"
-
regularExpression
-
For now this is ignored and the complete list of names is always - returned.
-
status
-
The status of a putGet request.
-
names
-
The list of record names.
-
-

Note that swtshell has a command channelList that -requires that a record of this type is present and calls it. -Thus user code does not have to use a channelGetPut to get the list -of record names.

-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

powerSupplyRecordTest

-

This simulates a simple power supply record. -It is used for testing.

- -
- - diff --git a/documentation/pvDatabaseCPP_20130828.html b/documentation/pvDatabaseCPP_20130828.html deleted file mode 100644 index 79ce2f8..0000000 --- a/documentation/pvDatabaseCPP_20130828.html +++ /dev/null @@ -1,1732 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 28-Aug-2013

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20130828.html -
-
Previous version:
-
pvDatabaseCPP20130725.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 28-Aug-2013 version of of pvDatabaseCPP. -

NOTE: -This is built against pvDataCPP-md NOT against pvDataCPP. -To build you must also -checkout pvAccessCPP and build it against pvDataCPP-md. -

-

-

All channel methods except channelRPC, which is implemented -by pvAccess, have been implemented. -This project is ready for alpha users. -

-

Future enhancements in priority order:

-
-
Separate example that also has pvaSrv
-
Create a separate example that combines a V3IOC, - a pvDatabase, and pvaSrv. - This will not be done until pvDataCPP-md is merged into pvDataCPP. -
-
channelArray
-
The arguments that have type int should be changed to size_t - This will not be done until pvDataCPP-md is merged into pvDataCPP. -
-
arrayPerformanceMain
-
When this is run without a pvAccess client the performance is great. - But when a pvAccess client is monitoring then the preformance slows - more then expected. - I do not understand and must spend more time determining why.
-
Monitor Algorithms
-
Monitor algorithms have not been implemented. - Thus all monitors are onPut.
-
Testing
-
Needs more testing. - Also none of the examples that use pvAccess can be run with gdb. -
-
Termination issues.
-
longArrayMonitor has memory leaks when main terminates. -
Create regression tests
-
Currently only examples exist and have been used for testing.
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In tha later case the IOC has both a database of V3 Records - and a pvDatabase.
-
exampleCounter
-
This is a simple example showing how to create a PVRecord and - how to deploy it either as a standalone process or as part of a V3IOC.
-
exampleServer
-
This example has a set of PVRecords. - Again the records can be deployed either as a standalone process or - as part of a V3IOC.
-
examplePVADoubleArrayGet
-
This is an example PVRecord that uses channelGet to get an array of doubles from - another PVRecord via pvAccess. The example shows how to get the value either via - remote pvAccess or by directly accessing the local channelProvider. - Again the records can be deployed either as a standalone process or - as part of a V3IOC.
-
-

database provides base classes that make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resources used by the implementation.
-
-

Getting started

-

Included with this project are three examples that are useful for -seeing how pvDatabase can be used by clients. -Each can be deployed either as a standalone process or as part of a V3IOC. -The examples are: -

-
exampleCounterMain
-
This has a database consisting of a single record named exampleCounter: - The exampleCounter is discussed in a following section.
-
exampleCounter
-
This is exampleCounter as part of a V3IOC.
-
exampleServerMain
-
This has a database with several records.
-
exampleServer
-
This is exampleServer as part of a V3IOC
-
examplePVADoubleArrayGetMain
-
This is the example of how to use pvAccess to get data.
-
examplePVADoubleArrayGet
-
This is examplePVADoubleArrayGet as part of a V3IOC
- -

-

exampleCounter

-

To start exampleCounterMain: -

-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounterMain
-

-

To start exampleCounter as part of a V3IOC: -

-mrk> pwd
-/home/hg/pvDatabaseCPP/iocBoot/exampleCounter
-mrk> ../../../bin/linux-x86_64/exampleCounter st.cmd
-

-

You can then issue the commands dbl and pvdbl: -

-epics> dbl
-double01
-epics> pvdbl
-exampleCounter
-epics> 
-
-double01 is a v3Record. -exampleCounter is a pvRecord. -

-

Starting exampleServer is similar. -After successfully running exampleCounterMain and exampleCounter then -try starting exampleServerMain and exampleServer. -

-

exampleServer

-

This example provides the exampleCounter record in addition to a number of other PVRecords. -Like exampleCounter it can be started either as a standalone main program or as -a part of a V3IOC. -The exampleServer pvDatabase has many records including the following:

-
-
exampleCounter
-
A record that is an instance of exampleCounter described below. - The most useful channel methods are channelGet, channelProcess, - and monitor.
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
examplePowerSupply
-
Can be used by channelGet, channelPut, channelPutGet, and monitor.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

swtshell

-

The Java program - - swtshell -can be used to access pvDatabase.

-

In particular read the sections "Getting Started" and "Simple Example". -They will work on the exampleServer with the following differences: -

-
startExample.zip
-
Do NOT use this. Instead run the exampleServer as described above.
-
channelList result
-
The result of channelList will show the list of records that - exampleServer has rather than the records from startExample.zip
-
-

- -

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP implements the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Example PVRecord Extension

-

The example implements a simple counter. -The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounter 
-
-
-

The example consists of four components:

-
-
ExampleCounter.h
-
The source code for the counter. - It is located in directory pvDatabaseCPP/example/src/exampleCounter. -
-
exampleCounterMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. - It is located in directory pvDatabaseCPP/example/exampleCounter. -
-
V3IOC/exampleCounter
-
This is a directory that packages exampleCounter to make it - part of a V3IOC.
-
iocBoot/exampleCounter
-
A place to start exampleCounter as part of a V3IOC. - It follows the normal iocCore conventions.
-
-

ExampleCounter.h

-

The example resides in src/database. -The complete implementation is in the header file. -A serious implementation might break the code into a header and an -implementation file.

-

The description consists of

-
-class ExampleCounter;
-typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
-
-class ExampleCounter :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleCounter);
-    static ExampleCounterPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleCounter();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleCounter(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    epics::pvData::PVLongPtr pvValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleCounter
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleCounter
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleCounterPtr ExampleCounter::create(
-    epics::pvData::String const & recordName)
-{
-    epics::pvData::PVStructurePtr pvStructure =
-       epics::pvData::getStandardPVField()->scalar(
-           epics::pvData::pvDouble,"timeStamp,alarm"");
-    ExampleCounterPtr pvRecord(
-        new ExampleCounter(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleCounterPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleCounter::ExampleCounter(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleCounter::~ExampleCounter()
-{
-}
-
-void ExampleCounter::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleCounter::init()
-{
-
-    initPVRecord();
-    epics::pvData::PVFieldPtr pvField;
-    pvValue = getPVStructure()->getLongField("value");
-    if(pvValue==NULL) return false;
-    return true;
-}
-
-This: -
    -
  • Calls initRecord which is implemented by the base class. - It MUST be called.
  • -
  • Calls getLongField to get the interface to the value field, - which must be a scalar with type long.
  • -
  • If a long value field was not found it returns false.
  • -
  • Returns true
  • -
-

The implementation of process is:

-
-void ExampleCounter::process()
-{
-    pvValue->put(pvValue->get() + 1.0);
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It adds 1.0 to the current value. -It then sets the timeStamp to the current time. -

exampleCounterMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleCounter");
-    PVRecordPtr pvRecord = ExampleCounter::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    cout << "exampleCounter\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleCounter record with the name exampleCounter -
  • -
  • Prints exampleCounter on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleCounter

-

This has two subdirectories: -

-
Db
-
This has a template for a single v3Record.
-
src
-
This has code to allow exampleCounter to reside in a V3IOC.
-
-

-

The src directory has the following components:

-
-
exampleCounterMain.cpp
-
This is just a standard Main for a V3IOC.
-
exampleCounterInclude.dbd
-
This is: -
-include "base.dbd"
-include "PVAServerRegister.dbd"
-registrar("exampleCounterRegister")
-       
- This includes the dbd components required from base, - the dbd file for starting pvAccess and the local channelProvider, - and the dbd file for the example. The later is for the code from the - next file. -
-
exampleCounter.cpp
-
This is the code that registers a command the can be issued - via the iocsh, which is the console for a V3IOC. - The example supports a single command: -
-exampleCounterCreateRecord recordName
-       
-
-
-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the C++ implementation of package pvAccess in pvIOCJava. - The localChannelProvider accesses data from PVRecords. - It implements all channel methods except channelRPC, which is implemented by pvAccessCPP.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

iocshell commands

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider

-
stopPVAServer
-
Stop the local channel provider
-
pvdbl
-
Provides a list of all the pvRecords in database master - It is similar to the iocsh command dbl
-
-

The client commands are provided via PVAClientRegister.dbd and the other commands -via PVAServerRegister.dbd.

-

In addition any code that implements a PVRecord must implement an ioc command. -The directory example has examples of how to implement the registration code. -See example/V3IOC/exampleCounter/src/ for a simple example.

- - -

database

-

src/database

-

This Directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
-

src/special

-

This directory has the following files:

-
-
powerSupplyRecordTest.h
-
- This provides code that simulates a power supply. - It computes the current from the voltage and power. - It is used for testing. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The testExampleServer creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-      
-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

pvDatabase.h

-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
process
-
Virtual method. - Derived classes must implement this method. - The base implementation does nothing. -
-
destroy
-
This is a virtual method. - A derived class must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it';s own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is access by the server side of remote pvAccess.

-

channelProviderLocal

-

This is a complete implementation of channelProvider and , -except for channelRPC, provides a complete implementation of Channel -as defined by pvAccess. -For monitors it calls the code described in the following sections.

-

pvCopy

-

This provides code that creates a top level PVStructure that is an arbitrary -subset of the fields in the PVStructure from a PVRecord. -In addition it provides code that monitors changes to the fields in a PVRecord. -A client configures the desired set of subfields and monitoring options -via a pvRequest structure. -pvAccess provides a class CreatePVRequest that creates a pvRequest. -The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava. -

-

monitorAlgorithm

-

Currently all that is implemented is a header file. -The only algorithm currently implemented is onPut -

-

monitorFactory

-

Overview

-

epics::pvData::monitor defines the monitor interfaces -as seen by a client. -See - pvDatabaseCPP.html - For details.

-

-monitorFactory implements the -monitoring interfaces for a PVRecord. -It implements queueSize=0 and queueSize>=2. -

- -

-The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy. -When PVCopyMonitor tells monitor that changes -have occurred, monitor applies the appropriate algorithm to each changed field.

- -

Currently only algorithm onPut is implemented but, -like pvIOCJava there are plans to support for the following monitor algorithms:

-
-
onPut
-
A monitor is issued whenever a put is issued to the field. This is the - default unless the record defines deadbands for a field. An exception is - the top level timeStamp which by default is made onChange and monitor - will not be raised.
-
onChange
-
This provides two options: 1) A monitor is raised whenever a field - changes value, and 2) A monitor will never be raised for the field.
-
deadband
-
The field must be a numeric scalar. Whenever the absolute or percentage - value of the field changes by more than a deadband a monitor is issued. - The record instance can also define deadbands.
-
periodic
-
A monitor is issued at a periodic rate if a put was issued to any field - being monitored.
-
-

MonitorFactory

-

MonitorFactory provides the following methods:

-
class MonitorFactory
-{
-    static MonitorPtr create(
-        PVRecordPtr const & pvRecord,
-        MonitorRequester::shared_pointer const & monitorRequester,
-        PVStructurePtr const & pvRequest);
-    static void registerMonitorAlgorithmCreater(
-        MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
-        String const & algorithmName);
-}
- -

where

-
-
create
-
Create a monitor. The arguments are: -
-
pvRecord
-
The record being monitored.
-
monitorRequester
-
The monitor requester. This is the code to which monitot events - will be delivered.
-
pvRequest
-
The request options
-
-
-
registerMonitorAlgorithmCreater
-
Called by code that implements a monitor algorithm.
-
-

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generted - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

-

recordList

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string database master
-        string regularExpression .*
-    structure result
-        string status 
-        string[] names
-
-where: -
-
database
-
The name of the datbase. The default is "master"
-
regularExpression
-
For now this is ignored and the complete list of names is always - returned.
-
status
-
The status of a putGet request.
-
names
-
The list of record names.
-
-

Note that swtshell has a command channelList that -requires that a record of this type is present and calls it. -Thus user code does not have to use a channelGetPut to get the list -of record names.

-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

powerSupplyRecordTest

-

This simulates a simple power supply record. -It is used for testing.

-

Accessing Other PVRecords

-

This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - caused deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all lockimg issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transfered between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-ChannelProvider::shared_pointer provider = channelAccess->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
-

examplePVADoubleArrayGet

-examplePVADoubleArrayGet shows how to use pvAccess to get data.

-

The code resides in three directories:

-
-
example/src/examplePVADoubleArrayGet
-
This is the implementation code for the example.
-
example/examplePVADoubleArrayGet
-
This is the code that starts the example as a main program.
-
example/V3IOC/examplePVADoubleArrayGet/src
-
This is the code for starting the example as part of a V3IOC.
-
iocBoot/examplePVADoubleArrayGet
-
This has the st.cmd files to start the example.
-
-

examplePVADoubleArrayGet Implementation

-

examplePVADoubleArrayGet.h contains the following:

-
-...
-class ExamplePVADoubleArrayGet :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExamplePVADoubleArrayGet);
-    static ExamplePVADoubleArrayGetPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::String const & providerName,
-        epics::pvData::String const & channelName
-        );
-    virtual ~ExamplePVADoubleArrayGet() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExamplePVADoubleArrayGetPtr ExamplePVADoubleArrayGet::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExamplePVADoubleArrayGetPtr pvRecord(
-        new ExamplePVADoubleArrayGet(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExamplePVADoubleArrayGet instance, -and then calls the init method and the returns a ExamplePVADoubleArrayGetPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExamplePVADoubleArrayGet::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

Starting the example as a main program

-
-...
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ClientFactory::start();
-    ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
-    PVRecordPtr pvRecord;
-    bool result(false);
-    String recordName;
-    PVStructurePtr pvStructure = standardPVField->scalarArray(
-        pvDouble,"alarm,timeStamp");
-    recordName = "doubleArray";
-    pvRecord = PVRecord::create(recordName,pvStructure);
-    result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    recordName = "examplePVADoubleArrayGet";
-    if(argc>1) recordName = argv[1];
-    String providerName("local");
-    if(argc>2) providerName = argv[2];
-    String channelName("doubleArray");
-    if(argc>3) channelName = argv[3];
-    pvRecord = ExamplePVADoubleArrayGet::create(
-        recordName,providerName,channelName);
-    if(pvRecord!=NULL) {
-        result = master->addRecord(pvRecord);
-        cout << "result of addRecord " << recordName << " " << result << endl;
-    } else {
-        cout << "ExamplePVADoubleArrayGet::create failed" << endl;
-    }
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    serverContext->shutdown();
-    epicsThreadSleep(1.0);
-    serverContext->destroy();
-    ClientFactory::stop();
-    channelProvider->destroy();
-    return 0;
-}
-
-

The first statements initializes the client factory and localChannelProvider.

-

-It then creates a PVRecord that has a double array as the value field. -The name of the record is doubleArray. -This is the record that can be accessed via pvAccess if the channelName is -doubleArray. -

-

It then creates a examplePVADoubleArrayGet instance.

-

It the runs forever until the exit is typed.

- -

Start the example as part of a V3IOC

-The directory example/V3IOC/examplePVADoubleArrayGet has two subdirectories: Db and src. -These are like for any other V3IOC application. -The Db directory is for a regular ai record. -The src directory has code similar to any V3IOC application. -In particular it has definitions and code for creating the following iocsh commands:

-
-examplePVADoubleArrayGetRegister recordName providerName channelName
-
-where -
-
recordName
-
providerName
-
channelName
-
-

The directory iocBoot/examplePVADoubleArrayGet has two st.cmd files:

-
-
st.local
-
This creates the example using localChannelProvider.
-
st.remote
-
This creates the example using the remote channel provider.
-
- -

Array Performance

-

Two main programs are available for testing the performance of large -arrays: arrayPerformanceMain and longArrayMonitorMain. -Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor useQueue
-default
-arrayPerformance arrayPerformance 50000000 0.001 local 1 false
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName useQueue
-default
-longArrayMonitorMain arrayPerformance false
-mrk> 
-
-

Example output

-
-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 50000000 0.01 local 1 false
-...
-first 0 last 0 sum 0 elements/sec 525.011million changed {1, 2} overrun {}
-first 1 last 1 sum 50000000 elements/sec 494.511million changed {1, 2} overrun {}
-first 2 last 2 sum 100000000 elements/sec 515.34million changed {1, 2} overrun {}
-first 3 last 3 sum 150000000 elements/sec 154.402million changed {1, 2} overrun {}
-first 4 last 4 sum 200000000 elements/sec 513.414million changed {1, 2} overrun {}
-first 5 last 5 sum 250000000 elements/sec 473.672million changed {1, 2} overrun {}
-first 6 last 6 sum 300000000 elements/sec 503.855million changed {1, 2} overrun {}
-arrayPerformance value 8 time 1.66373 iterations/sec 4.80847 elements/sec 240.424million
-...
-
-

arrayPerformance

-

-This creates a PVRecord that has the structure:. -

-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers.

-

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
At least once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the interation count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
useQueue
-
Should the longArrayMonitors use a queue? - This must be true or false. -
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report for each monitor event. -In the example output shown above each line starting with first -is a report.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
useQueue
-
-

Some results

-

The results were from my laptop. -It has a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system is linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 50 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 8 iterations per second -and is putting about 350million elements per second. -Since each element is an int64 this means about 2.8gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-

-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 90million elements per second. -In addition the time between reports varies from just over 1 second to 3 seconds. -I do not understand why.

- -
- - diff --git a/documentation/pvDatabaseCPP_20130904.html b/documentation/pvDatabaseCPP_20130904.html deleted file mode 100644 index 2623f91..0000000 --- a/documentation/pvDatabaseCPP_20130904.html +++ /dev/null @@ -1,1809 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 04-Sep-2013

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20130904.html -
-
Previous version:
-
pvDatabaseCPP20130828.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 04-Sep-2013 version of of pvDatabaseCPP.

-

NOTE: -This is built against pvDataCPP-md NOT against pvDataCPP. -To build you must also -checkout pvAccessCPP and build it against pvDataCPP-md. -

-

All channel methods except channelRPC, which is implemented -by pvAccess, have been implemented. -This project is ready for alpha users. -

-

Future enhancements in priority order:

-
-
Separate example that also has pvaSrv
-
Create a separate example that combines a V3IOC, - a pvDatabase, and pvaSrv. - This will not be done until pvDataCPP-md is merged into pvDataCPP. -
-
channelArray
-
The arguments that have type int should be changed to size_t - This will not be done until pvDataCPP-md is merged into pvDataCPP. -
-
Monitor Algorithms
-
Monitor algorithms have not been implemented. - Thus all monitors are onPut.
-
Testing
-
Needs more testing. - Also none of the examples that use pvAccess can be run with gdb. -
-
High Performance Issues.
-
When arrayPerformance is run with size=5000 and delay really small or zero, - either arrayPerformance or longArrayMonitor will sometimes crash while running or at termination. - Also, in the same environment, longArrayMonitor frequently gets the array with size = 0
-
Create regression tests
-
Currently only examples exist and have been used for testing.
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In tha later case the IOC has both a database of V3 Records - and a pvDatabase.
-
exampleCounter
-
This is a simple example showing how to create a PVRecord and - how to deploy it either as a standalone process or as part of a V3IOC.
-
exampleServer
-
This example has a set of PVRecords. - Again the records can be deployed either as a standalone process or - as part of a V3IOC.
-
examplePVADoubleArrayGet
-
This is an example PVRecord that uses channelGet to get an array of doubles from - another PVRecord via pvAccess. The example shows how to get the value either via - remote pvAccess or by directly accessing the local channelProvider. - Again the records can be deployed either as a standalone process or - as part of a V3IOC.
-
-

database provides base classes that make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resources used by the implementation.
-
-

Getting started

-

Included with this project are three examples that are useful for -seeing how pvDatabase can be used by clients. -Each can be deployed either as a standalone process or as part of a V3IOC. -The examples are: -

-
exampleCounterMain
-
This has a database consisting of a single record named exampleCounter: - The exampleCounter is discussed in a following section.
-
exampleCounter
-
This is exampleCounter as part of a V3IOC.
-
exampleServerMain
-
This has a database with several records.
-
exampleServer
-
This is exampleServer as part of a V3IOC
-
examplePVADoubleArrayGetMain
-
This is the example of how to use pvAccess to get data.
-
examplePVADoubleArrayGet
-
This is examplePVADoubleArrayGet as part of a V3IOC
- -

-

exampleCounter

-

To start exampleCounterMain: -

-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounterMain
-

-

To start exampleCounter as part of a V3IOC: -

-mrk> pwd
-/home/hg/pvDatabaseCPP/iocBoot/exampleCounter
-mrk> ../../../bin/linux-x86_64/exampleCounter st.cmd
-

-

You can then issue the commands dbl and pvdbl: -

-epics> dbl
-double01
-epics> pvdbl
-exampleCounter
-epics> 
-
-double01 is a v3Record. -exampleCounter is a pvRecord. -

-

Starting exampleServer is similar. -After successfully running exampleCounterMain and exampleCounter then -try starting exampleServerMain and exampleServer. -

-

exampleServer

-

This example provides the exampleCounter record in addition to a number of other PVRecords. -Like exampleCounter it can be started either as a standalone main program or as -a part of a V3IOC. -The exampleServer pvDatabase has many records including the following:

-
-
exampleCounter
-
A record that is an instance of exampleCounter described below. - The most useful channel methods are channelGet, channelProcess, - and monitor.
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
examplePowerSupply
-
Can be used by channelGet, channelPut, channelPutGet, and monitor.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

swtshell

-

The Java program - - swtshell -can be used to access pvDatabase.

-

In particular read the sections "Getting Started" and "Simple Example". -They will work on the exampleServer with the following differences: -

-
startExample.zip
-
Do NOT use this. Instead run the exampleServer as described above.
-
channelList result
-
The result of channelList will show the list of records that - exampleServer has rather than the records from startExample.zip
-
-

- -

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP implements the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Example PVRecord Extension

-

The example implements a simple counter. -The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounter 
-
-
-

The example consists of four components:

-
-
ExampleCounter.h
-
The source code for the counter. - It is located in directory pvDatabaseCPP/example/src/exampleCounter. -
-
exampleCounterMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. - It is located in directory pvDatabaseCPP/example/exampleCounter. -
-
V3IOC/exampleCounter
-
This is a directory that packages exampleCounter to make it - part of a V3IOC.
-
iocBoot/exampleCounter
-
A place to start exampleCounter as part of a V3IOC. - It follows the normal iocCore conventions.
-
-

ExampleCounter.h

-

The example resides in src/database. -The complete implementation is in the header file. -A serious implementation might break the code into a header and an -implementation file.

-

The description consists of

-
-class ExampleCounter;
-typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
-
-class ExampleCounter :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleCounter);
-    static ExampleCounterPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleCounter();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleCounter(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    epics::pvData::PVLongPtr pvValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleCounter
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleCounter
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleCounterPtr ExampleCounter::create(
-    epics::pvData::String const & recordName)
-{
-    epics::pvData::PVStructurePtr pvStructure =
-       epics::pvData::getStandardPVField()->scalar(
-           epics::pvData::pvDouble,"timeStamp,alarm"");
-    ExampleCounterPtr pvRecord(
-        new ExampleCounter(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleCounterPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleCounter::ExampleCounter(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleCounter::~ExampleCounter()
-{
-}
-
-void ExampleCounter::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleCounter::init()
-{
-
-    initPVRecord();
-    epics::pvData::PVFieldPtr pvField;
-    pvValue = getPVStructure()->getLongField("value");
-    if(pvValue==NULL) return false;
-    return true;
-}
-
-This: -
    -
  • Calls initRecord which is implemented by the base class. - It MUST be called.
  • -
  • Calls getLongField to get the interface to the value field, - which must be a scalar with type long.
  • -
  • If a long value field was not found it returns false.
  • -
  • Returns true
  • -
-

The implementation of process is:

-
-void ExampleCounter::process()
-{
-    pvValue->put(pvValue->get() + 1.0);
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It adds 1.0 to the current value. -It then sets the timeStamp to the current time. -

exampleCounterMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleCounter");
-    PVRecordPtr pvRecord = ExampleCounter::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    cout << "exampleCounter\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleCounter record with the name exampleCounter -
  • -
  • Prints exampleCounter on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleCounter

-

This has two subdirectories: -

-
Db
-
This has a template for a single v3Record.
-
src
-
This has code to allow exampleCounter to reside in a V3IOC.
-
-

-

The src directory has the following components:

-
-
exampleCounterMain.cpp
-
This is just a standard Main for a V3IOC.
-
exampleCounterInclude.dbd
-
This is: -
-include "base.dbd"
-include "PVAServerRegister.dbd"
-registrar("exampleCounterRegister")
-       
- This includes the dbd components required from base, - the dbd file for starting pvAccess and the local channelProvider, - and the dbd file for the example. The later is for the code from the - next file. -
-
exampleCounter.cpp
-
This is the code that registers a command the can be issued - via the iocsh, which is the console for a V3IOC. - The example supports a single command: -
-exampleCounterCreateRecord recordName
-       
-
-
-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the C++ implementation of package pvAccess in pvIOCJava. - The localChannelProvider accesses data from PVRecords. - It implements all channel methods except channelRPC, which is implemented by pvAccessCPP.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

iocshell commands

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider

-
stopPVAServer
-
Stop the local channel provider
-
pvdbl
-
Provides a list of all the pvRecords in database master - It is similar to the iocsh command dbl
-
-

The client commands are provided via PVAClientRegister.dbd and the other commands -via PVAServerRegister.dbd.

-

In addition any code that implements a PVRecord must implement an ioc command. -The directory example has examples of how to implement the registration code. -See example/V3IOC/exampleCounter/src/ for a simple example.

- - -

database

-

src/database

-

This Directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
-

src/special

-

This directory has the following files:

-
-
powerSupplyRecordTest.h
-
- This provides code that simulates a power supply. - It computes the current from the voltage and power. - It is used for testing. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The testExampleServer creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-      
-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

pvDatabase.h

-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
process
-
Virtual method. - Derived classes must implement this method. - The base implementation does nothing. -
-
destroy
-
This is a virtual method. - A derived class must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it';s own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is access by the server side of remote pvAccess.

-

channelProviderLocal

-

This is a complete implementation of channelProvider and , -except for channelRPC, provides a complete implementation of Channel -as defined by pvAccess. -For monitors it calls the code described in the following sections.

-

pvCopy

-

This provides code that creates a top level PVStructure that is an arbitrary -subset of the fields in the PVStructure from a PVRecord. -In addition it provides code that monitors changes to the fields in a PVRecord. -A client configures the desired set of subfields and monitoring options -via a pvRequest structure. -pvAccess provides a class CreatePVRequest that creates a pvRequest. -The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava. -

-

monitorAlgorithm

-

Currently all that is implemented is a header file. -The only algorithm currently implemented is onPut -

-

monitorFactory

-

Overview

-

epics::pvData::monitor defines the monitor interfaces -as seen by a client. -See - pvDatabaseCPP.html - For details.

-

-monitorFactory implements the -monitoring interfaces for a PVRecord. -It implements queueSize=0 and queueSize>=2. -

- -

-The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy. -When PVCopyMonitor tells monitor that changes -have occurred, monitor applies the appropriate algorithm to each changed field.

- -

Currently only algorithm onPut is implemented but, -like pvIOCJava there are plans to support for the following monitor algorithms:

-
-
onPut
-
A monitor is issued whenever a put is issued to the field. This is the - default unless the record defines deadbands for a field. An exception is - the top level timeStamp which by default is made onChange and monitor - will not be raised.
-
onChange
-
This provides two options: 1) A monitor is raised whenever a field - changes value, and 2) A monitor will never be raised for the field.
-
deadband
-
The field must be a numeric scalar. Whenever the absolute or percentage - value of the field changes by more than a deadband a monitor is issued. - The record instance can also define deadbands.
-
periodic
-
A monitor is issued at a periodic rate if a put was issued to any field - being monitored.
-
-

MonitorFactory

-

MonitorFactory provides the following methods:

-
class MonitorFactory
-{
-    static MonitorPtr create(
-        PVRecordPtr const & pvRecord,
-        MonitorRequester::shared_pointer const & monitorRequester,
-        PVStructurePtr const & pvRequest);
-    static void registerMonitorAlgorithmCreater(
-        MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
-        String const & algorithmName);
-}
- -

where

-
-
create
-
Create a monitor. The arguments are: -
-
pvRecord
-
The record being monitored.
-
monitorRequester
-
The monitor requester. This is the code to which monitot events - will be delivered.
-
pvRequest
-
The request options
-
-
-
registerMonitorAlgorithmCreater
-
Called by code that implements a monitor algorithm.
-
-

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generted - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

-

recordList

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string database master
-        string regularExpression .*
-    structure result
-        string status 
-        string[] names
-
-where: -
-
database
-
The name of the datbase. The default is "master"
-
regularExpression
-
For now this is ignored and the complete list of names is always - returned.
-
status
-
The status of a putGet request.
-
names
-
The list of record names.
-
-

Note that swtshell has a command channelList that -requires that a record of this type is present and calls it. -Thus user code does not have to use a channelGetPut to get the list -of record names.

-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

powerSupplyRecordTest

-

This simulates a simple power supply record. -It is used for testing.

-

Accessing Other PVRecords

-

This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - caused deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all lockimg issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transfered between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-ChannelProvider::shared_pointer provider = channelAccess->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
-

examplePVADoubleArrayGet

-examplePVADoubleArrayGet shows how to use pvAccess to get data.

-

The code resides in three directories:

-
-
example/src/examplePVADoubleArrayGet
-
This is the implementation code for the example.
-
example/examplePVADoubleArrayGet
-
This is the code that starts the example as a main program.
-
example/V3IOC/examplePVADoubleArrayGet/src
-
This is the code for starting the example as part of a V3IOC.
-
iocBoot/examplePVADoubleArrayGet
-
This has the st.cmd files to start the example.
-
-

examplePVADoubleArrayGet Implementation

-

examplePVADoubleArrayGet.h contains the following:

-
-...
-class ExamplePVADoubleArrayGet :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExamplePVADoubleArrayGet);
-    static ExamplePVADoubleArrayGetPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::String const & providerName,
-        epics::pvData::String const & channelName
-        );
-    virtual ~ExamplePVADoubleArrayGet() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExamplePVADoubleArrayGetPtr ExamplePVADoubleArrayGet::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExamplePVADoubleArrayGetPtr pvRecord(
-        new ExamplePVADoubleArrayGet(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExamplePVADoubleArrayGet instance, -and then calls the init method and the returns a ExamplePVADoubleArrayGetPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExamplePVADoubleArrayGet::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

Starting the example as a main program

-
-...
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ClientFactory::start();
-    ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
-    PVRecordPtr pvRecord;
-    bool result(false);
-    String recordName;
-    PVStructurePtr pvStructure = standardPVField->scalarArray(
-        pvDouble,"alarm,timeStamp");
-    recordName = "doubleArray";
-    pvRecord = PVRecord::create(recordName,pvStructure);
-    result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    recordName = "examplePVADoubleArrayGet";
-    if(argc>1) recordName = argv[1];
-    String providerName("local");
-    if(argc>2) providerName = argv[2];
-    String channelName("doubleArray");
-    if(argc>3) channelName = argv[3];
-    pvRecord = ExamplePVADoubleArrayGet::create(
-        recordName,providerName,channelName);
-    if(pvRecord!=NULL) {
-        result = master->addRecord(pvRecord);
-        cout << "result of addRecord " << recordName << " " << result << endl;
-    } else {
-        cout << "ExamplePVADoubleArrayGet::create failed" << endl;
-    }
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    serverContext->shutdown();
-    epicsThreadSleep(1.0);
-    serverContext->destroy();
-    ClientFactory::stop();
-    channelProvider->destroy();
-    return 0;
-}
-
-

The first statements initializes the client factory and localChannelProvider.

-

-It then creates a PVRecord that has a double array as the value field. -The name of the record is doubleArray. -This is the record that can be accessed via pvAccess if the channelName is -doubleArray. -

-

It then creates a examplePVADoubleArrayGet instance.

-

It the runs forever until the exit is typed.

- -

Start the example as part of a V3IOC

-The directory example/V3IOC/examplePVADoubleArrayGet has two subdirectories: Db and src. -These are like for any other V3IOC application. -The Db directory is for a regular ai record. -The src directory has code similar to any V3IOC application. -In particular it has definitions and code for creating the following iocsh commands:

-
-examplePVADoubleArrayGetRegister recordName providerName channelName
-
-where -
-
recordName
-
providerName
-
channelName
-
-

The directory iocBoot/examplePVADoubleArrayGet has two st.cmd files:

-
-
st.local
-
This creates the example using localChannelProvider.
-
st.remote
-
This creates the example using the remote channel provider.
-
- -

Array Performance

-

Two main programs are available for testing the performance of large -arrays: arrayPerformanceMain and longArrayMonitorMain. -Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor useQueue
-default
-arrayPerformance arrayPerformance 50000000 0.001 local 1 false
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName useQueue
-default
-longArrayMonitorMain arrayPerformance false
-mrk> 
-
-

Example output

-
-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 50000000 0.01 local 1 false
-...
-first 0 last 0 sum 0 elements/sec 529.007million changed {1, 2} overrun {}
-first 1 last 1 sum 50000000 elements/sec 510.686million changed {1, 2} overrun {}
-first 2 last 2 sum 100000000 elements/sec 520.114million changed {1, 2} overrun {}
-first 3 last 3 sum 150000000 elements/sec 514.842million changed {1, 2} overrun {}
-first 4 last 4 sum 200000000 elements/sec 507.642million changed {1, 2} overrun {}
-first 5 last 5 sum 250000000 elements/sec 505.598million changed {1, 2} overrun {}
-first 6 last 6 sum 300000000 elements/sec 517.081million changed {1, 2} overrun {}
-first 7 last 7 sum 350000000 elements/sec 516.508million changed {1, 2} overrun {}
-first 8 last 8 sum 400000000 elements/sec 513.711million changed {1, 2} overrun {}
-first 9 last 9 sum 450000000 elements/sec 505.309million changed {1, 2} overrun {}
-arrayPerformance value 11 time 1.08257 iterations/sec 10.161 elements/sec 508.049million
-...
-
-

arrayPerformance

-

-This creates a PVRecord that has the structure:. -

-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers.

-

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
At least once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the interation count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
useQueue
-
Should the longArrayMonitors use a queue? - This must be true or false. -
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report for each monitor event. -In the example output shown above each line starting with first -is a report.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
useQueue
-
-

Some results

-

The results were from my laptop. -It has a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system is linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 50 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 10 iterations per second -and is putting about 500million elements per second. -Since each element is an int64 this means about 4gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-

-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 300million elements per second. -In addition the time between reports varies from just over 1 second to 1.3 seconds. -The reason is contention for transfering data between main memory and local caches. -The next section has an example that demonstrates what happens. -Note that if the array size is small enouggh to fix in the local cache then running longArrayMonitor -has almost no effect of arrayPerforance. -

-

Vector Performance

-

This example demonstrates how array size effects performance. -The example is run as:

-
-bin/linux-x86_64/vectorPerformanceMain -help
-vectorPerformanceMain size delay nThread
-default
-vectorPerformance 50000000 0.01 1 
-
-

Consider the following:

-
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
-...
-thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
-thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
-thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
-...
-thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
-thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
-thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
-thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
-thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
-thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
-thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
-thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
-thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
-thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
-thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
-thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
-thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
-thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
-thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
-thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
-thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
-thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
-
-

As more threads are running the slower each thread runs.

-

But now consider a size that fits in a local cache.

-

-bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
-...
-thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
-thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
-thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
-... 
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
-...
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-...
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
-...
-thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
-thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
-thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
-thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
-thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
-thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
-
-

Now the number of threads has a far smaller effect on the performance of each thread. -

- -
- - diff --git a/documentation/pvDatabaseCPP_20131112.html b/documentation/pvDatabaseCPP_20131112.html deleted file mode 100644 index 84eb79f..0000000 --- a/documentation/pvDatabaseCPP_20131112.html +++ /dev/null @@ -1,1831 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 12-Nov-2013

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20131112.html -
-
Previous version:
-
pvDatabaseCPP20130904.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 12-Nov-2013 version of of pvDatabaseCPP.

-

-

Problem When arrayPerformance is run with a small array and queueSize 1 it fails. -For example: -

-mrk> bin/linux-x86_64/arrayPerformanceMain arrayPerformance 50 0.00 local 1 1 0.001
-arrayPerformance arrayPerformance 50 0 local 1 1 0
-...
-first 2245711 last 2245711 sum 112285550 elements/sec 0.0452426million changed {1, 2} overrun {}
-first 2246087 last 2246087 sum 112304350 elements/sec 0.0451597million changed {1, 2} overrun {}
-arrayPerformance value 2246117 time 1 iterations/sec 336447 elements/sec 16.8223million
-first 2246448 last 2246448 sum 112322400 elements/sec 0.0450084million changed {1, 2} overrun {}
-first 2246827 last 2246827 sum 112341350 elements/sec 0.0446581million changed {1, 2} overrun {}
-...
-first 2282180 last 2282180 sum 114109000 elements/sec 0.0447461million changed {1, 2} overrun {}
-first 2282540 last 2282540 sum 114127000 elements/sec 0.044695million changed {1, 2} overrun {}
-Segmentation fault (core dumped)
-
-I do not know why and am stuck about what to do. -Looking at SingleElementQueue I do not see any problem.

- -

All channel methods except channelRPC, which is implemented -by pvAccess, have been implemented. -This project is ready for alpha users. -

-

Future enhancements in priority order:

-
-
Separate example that also has pvaSrv
-
Create a separate example that combines a V3IOC, - a pvDatabase, and pvaSrv. - This will not be done until pvDataCPP-md is merged into pvDataCPP. -
-
channelArray
-
The arguments that have type int should be changed to size_t - This will not be done until pvDataCPP-md is merged into pvDataCPP. -
-
Monitor Algorithms
-
Monitor algorithms have not been implemented. - Thus all monitors are onPut.
-
Testing
-
Needs more testing. - Also none of the examples that use pvAccess can be run with gdb. -
-
High Performance Issues.
-
When arrayPerformance is run with size=5000 and delay really small or zero, - either arrayPerformance or longArrayMonitor will sometimes crash while running or at termination. - Also, in the same environment, longArrayMonitor frequently gets the array with size = 0
-
Create regression tests
-
Currently only examples exist and have been used for testing.
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In tha later case the IOC has both a database of V3 Records - and a pvDatabase.
-
exampleCounter
-
This is a simple example showing how to create a PVRecord and - how to deploy it either as a standalone process or as part of a V3IOC.
-
exampleServer
-
This example has a set of PVRecords. - Again the records can be deployed either as a standalone process or - as part of a V3IOC.
-
examplePVADoubleArrayGet
-
This is an example PVRecord that uses channelGet to get an array of doubles from - another PVRecord via pvAccess. The example shows how to get the value either via - remote pvAccess or by directly accessing the local channelProvider. - Again the records can be deployed either as a standalone process or - as part of a V3IOC.
-
-

database provides base classes that make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resources used by the implementation.
-
-

Getting started

-

Included with this project are three examples that are useful for -seeing how pvDatabase can be used by clients. -Each can be deployed either as a standalone process or as part of a V3IOC. -The examples are: -

-
exampleCounterMain
-
This has a database consisting of a single record named exampleCounter: - The exampleCounter is discussed in a following section.
-
exampleCounter
-
This is exampleCounter as part of a V3IOC.
-
exampleServerMain
-
This has a database with several records.
-
exampleServer
-
This is exampleServer as part of a V3IOC
-
examplePVADoubleArrayGetMain
-
This is the example of how to use pvAccess to get data.
-
examplePVADoubleArrayGet
-
This is examplePVADoubleArrayGet as part of a V3IOC
- -

-

exampleCounter

-

To start exampleCounterMain: -

-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounterMain
-

-

To start exampleCounter as part of a V3IOC: -

-mrk> pwd
-/home/hg/pvDatabaseCPP/iocBoot/exampleCounter
-mrk> ../../../bin/linux-x86_64/exampleCounter st.cmd
-

-

You can then issue the commands dbl and pvdbl: -

-epics> dbl
-double01
-epics> pvdbl
-exampleCounter
-epics> 
-
-double01 is a v3Record. -exampleCounter is a pvRecord. -

-

Starting exampleServer is similar. -After successfully running exampleCounterMain and exampleCounter then -try starting exampleServerMain and exampleServer. -

-

exampleServer

-

This example provides the exampleCounter record in addition to a number of other PVRecords. -Like exampleCounter it can be started either as a standalone main program or as -a part of a V3IOC. -The exampleServer pvDatabase has many records including the following:

-
-
exampleCounter
-
A record that is an instance of exampleCounter described below. - The most useful channel methods are channelGet, channelProcess, - and monitor.
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
examplePowerSupply
-
Can be used by channelGet, channelPut, channelPutGet, and monitor.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

swtshell

-

The Java program - - swtshell -can be used to access pvDatabase.

-

In particular read the sections "Getting Started" and "Simple Example". -They will work on the exampleServer with the following differences: -

-
startExample.zip
-
Do NOT use this. Instead run the exampleServer as described above.
-
channelList result
-
The result of channelList will show the list of records that - exampleServer has rather than the records from startExample.zip
-
-

- -

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP implements the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Example PVRecord Extension

-

The example implements a simple counter. -The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounter 
-
-
-

The example consists of four components:

-
-
ExampleCounter.h
-
The source code for the counter. - It is located in directory pvDatabaseCPP/example/src/exampleCounter. -
-
exampleCounterMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. - It is located in directory pvDatabaseCPP/example/exampleCounter. -
-
V3IOC/exampleCounter
-
This is a directory that packages exampleCounter to make it - part of a V3IOC.
-
iocBoot/exampleCounter
-
A place to start exampleCounter as part of a V3IOC. - It follows the normal iocCore conventions.
-
-

ExampleCounter.h

-

The example resides in src/database. -The complete implementation is in the header file. -A serious implementation might break the code into a header and an -implementation file.

-

The description consists of

-
-class ExampleCounter;
-typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
-
-class ExampleCounter :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleCounter);
-    static ExampleCounterPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleCounter();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleCounter(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    epics::pvData::PVLongPtr pvValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleCounter
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleCounter
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleCounterPtr ExampleCounter::create(
-    epics::pvData::String const & recordName)
-{
-    epics::pvData::PVStructurePtr pvStructure =
-       epics::pvData::getStandardPVField()->scalar(
-           epics::pvData::pvDouble,"timeStamp,alarm"");
-    ExampleCounterPtr pvRecord(
-        new ExampleCounter(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleCounterPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleCounter::ExampleCounter(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleCounter::~ExampleCounter()
-{
-}
-
-void ExampleCounter::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleCounter::init()
-{
-
-    initPVRecord();
-    epics::pvData::PVFieldPtr pvField;
-    pvValue = getPVStructure()->getLongField("value");
-    if(pvValue==NULL) return false;
-    return true;
-}
-
-This: -
    -
  • Calls initRecord which is implemented by the base class. - It MUST be called.
  • -
  • Calls getLongField to get the interface to the value field, - which must be a scalar with type long.
  • -
  • If a long value field was not found it returns false.
  • -
  • Returns true
  • -
-

The implementation of process is:

-
-void ExampleCounter::process()
-{
-    pvValue->put(pvValue->get() + 1.0);
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It adds 1.0 to the current value. -It then sets the timeStamp to the current time. -

exampleCounterMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleCounter");
-    PVRecordPtr pvRecord = ExampleCounter::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    cout << "exampleCounter\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleCounter record with the name exampleCounter -
  • -
  • Prints exampleCounter on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleCounter

-

This has two subdirectories: -

-
Db
-
This has a template for a single v3Record.
-
src
-
This has code to allow exampleCounter to reside in a V3IOC.
-
-

-

The src directory has the following components:

-
-
exampleCounterMain.cpp
-
This is just a standard Main for a V3IOC.
-
exampleCounterInclude.dbd
-
This is: -
-include "base.dbd"
-include "PVAServerRegister.dbd"
-registrar("exampleCounterRegister")
-       
- This includes the dbd components required from base, - the dbd file for starting pvAccess and the local channelProvider, - and the dbd file for the example. The later is for the code from the - next file. -
-
exampleCounter.cpp
-
This is the code that registers a command the can be issued - via the iocsh, which is the console for a V3IOC. - The example supports a single command: -
-exampleCounterCreateRecord recordName
-       
-
-
-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the C++ implementation of package pvAccess in pvIOCJava. - The localChannelProvider accesses data from PVRecords. - It implements all channel methods except channelRPC, which is implemented by pvAccessCPP.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

iocshell commands

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider

-
stopPVAServer
-
Stop the local channel provider
-
pvdbl
-
Provides a list of all the pvRecords in database master - It is similar to the iocsh command dbl
-
-

The client commands are provided via PVAClientRegister.dbd and the other commands -via PVAServerRegister.dbd.

-

In addition any code that implements a PVRecord must implement an ioc command. -The directory example has examples of how to implement the registration code. -See example/V3IOC/exampleCounter/src/ for a simple example.

- - -

database

-

src/database

-

This Directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
-

src/special

-

This directory has the following files:

-
-
powerSupplyRecordTest.h
-
- This provides code that simulates a power supply. - It computes the current from the voltage and power. - It is used for testing. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The testExampleServer creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-      
-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

pvDatabase.h

-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
process
-
Virtual method. - Derived classes must implement this method. - The base implementation does nothing. -
-
destroy
-
This is a virtual method. - A derived class must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it';s own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is access by the server side of remote pvAccess.

-

channelProviderLocal

-

This is a complete implementation of channelProvider and , -except for channelRPC, provides a complete implementation of Channel -as defined by pvAccess. -For monitors it calls the code described in the following sections.

-

pvCopy

-

This provides code that creates a top level PVStructure that is an arbitrary -subset of the fields in the PVStructure from a PVRecord. -In addition it provides code that monitors changes to the fields in a PVRecord. -A client configures the desired set of subfields and monitoring options -via a pvRequest structure. -pvAccess provides a class CreatePVRequest that creates a pvRequest. -The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava. -

-

monitorAlgorithm

-

Currently all that is implemented is a header file. -The only algorithm currently implemented is onPut -

-

monitorFactory

-

Overview

-

epics::pvData::monitor defines the monitor interfaces -as seen by a client. -See - pvDatabaseCPP.html - For details.

-

-monitorFactory implements the -monitoring interfaces for a PVRecord. -It implements queueSize=0 and queueSize>=2. -

- -

-The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy. -When PVCopyMonitor tells monitor that changes -have occurred, monitor applies the appropriate algorithm to each changed field.

- -

Currently only algorithm onPut is implemented but, -like pvIOCJava there are plans to support for the following monitor algorithms:

-
-
onPut
-
A monitor is issued whenever a put is issued to the field. This is the - default unless the record defines deadbands for a field. An exception is - the top level timeStamp which by default is made onChange and monitor - will not be raised.
-
onChange
-
This provides two options: 1) A monitor is raised whenever a field - changes value, and 2) A monitor will never be raised for the field.
-
deadband
-
The field must be a numeric scalar. Whenever the absolute or percentage - value of the field changes by more than a deadband a monitor is issued. - The record instance can also define deadbands.
-
periodic
-
A monitor is issued at a periodic rate if a put was issued to any field - being monitored.
-
-

MonitorFactory

-

MonitorFactory provides the following methods:

-
class MonitorFactory
-{
-    static MonitorPtr create(
-        PVRecordPtr const & pvRecord,
-        MonitorRequester::shared_pointer const & monitorRequester,
-        PVStructurePtr const & pvRequest);
-    static void registerMonitorAlgorithmCreater(
-        MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
-        String const & algorithmName);
-}
- -

where

-
-
create
-
Create a monitor. The arguments are: -
-
pvRecord
-
The record being monitored.
-
monitorRequester
-
The monitor requester. This is the code to which monitot events - will be delivered.
-
pvRequest
-
The request options
-
-
-
registerMonitorAlgorithmCreater
-
Called by code that implements a monitor algorithm.
-
-

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generted - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

-

recordList

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string database master
-        string regularExpression .*
-    structure result
-        string status 
-        string[] names
-
-where: -
-
database
-
The name of the datbase. The default is "master"
-
regularExpression
-
For now this is ignored and the complete list of names is always - returned.
-
status
-
The status of a putGet request.
-
names
-
The list of record names.
-
-

Note that swtshell has a command channelList that -requires that a record of this type is present and calls it. -Thus user code does not have to use a channelGetPut to get the list -of record names.

-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

powerSupplyRecordTest

-

This simulates a simple power supply record. -It is used for testing.

-

Accessing Other PVRecords

-

This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - caused deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all lockimg issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transfered between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-ChannelProvider::shared_pointer provider = channelAccess->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
-

examplePVADoubleArrayGet

-examplePVADoubleArrayGet shows how to use pvAccess to get data.

-

The code resides in three directories:

-
-
example/src/examplePVADoubleArrayGet
-
This is the implementation code for the example.
-
example/examplePVADoubleArrayGet
-
This is the code that starts the example as a main program.
-
example/V3IOC/examplePVADoubleArrayGet/src
-
This is the code for starting the example as part of a V3IOC.
-
iocBoot/examplePVADoubleArrayGet
-
This has the st.cmd files to start the example.
-
-

examplePVADoubleArrayGet Implementation

-

examplePVADoubleArrayGet.h contains the following:

-
-...
-class ExamplePVADoubleArrayGet :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExamplePVADoubleArrayGet);
-    static ExamplePVADoubleArrayGetPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::String const & providerName,
-        epics::pvData::String const & channelName
-        );
-    virtual ~ExamplePVADoubleArrayGet() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExamplePVADoubleArrayGetPtr ExamplePVADoubleArrayGet::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExamplePVADoubleArrayGetPtr pvRecord(
-        new ExamplePVADoubleArrayGet(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExamplePVADoubleArrayGet instance, -and then calls the init method and the returns a ExamplePVADoubleArrayGetPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExamplePVADoubleArrayGet::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

Starting the example as a main program

-
-...
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ClientFactory::start();
-    ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
-    PVRecordPtr pvRecord;
-    bool result(false);
-    String recordName;
-    PVStructurePtr pvStructure = standardPVField->scalarArray(
-        pvDouble,"alarm,timeStamp");
-    recordName = "doubleArray";
-    pvRecord = PVRecord::create(recordName,pvStructure);
-    result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    recordName = "examplePVADoubleArrayGet";
-    if(argc>1) recordName = argv[1];
-    String providerName("local");
-    if(argc>2) providerName = argv[2];
-    String channelName("doubleArray");
-    if(argc>3) channelName = argv[3];
-    pvRecord = ExamplePVADoubleArrayGet::create(
-        recordName,providerName,channelName);
-    if(pvRecord!=NULL) {
-        result = master->addRecord(pvRecord);
-        cout << "result of addRecord " << recordName << " " << result << endl;
-    } else {
-        cout << "ExamplePVADoubleArrayGet::create failed" << endl;
-    }
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    serverContext->shutdown();
-    epicsThreadSleep(1.0);
-    serverContext->destroy();
-    ClientFactory::stop();
-    channelProvider->destroy();
-    return 0;
-}
-
-

The first statements initializes the client factory and localChannelProvider.

-

-It then creates a PVRecord that has a double array as the value field. -The name of the record is doubleArray. -This is the record that can be accessed via pvAccess if the channelName is -doubleArray. -

-

It then creates a examplePVADoubleArrayGet instance.

-

It the runs forever until the exit is typed.

- -

Start the example as part of a V3IOC

-The directory example/V3IOC/examplePVADoubleArrayGet has two subdirectories: Db and src. -These are like for any other V3IOC application. -The Db directory is for a regular ai record. -The src directory has code similar to any V3IOC application. -In particular it has definitions and code for creating the following iocsh commands:

-
-examplePVADoubleArrayGetRegister recordName providerName channelName
-
-where -
-
recordName
-
providerName
-
channelName
-
-

The directory iocBoot/examplePVADoubleArrayGet has two st.cmd files:

-
-
st.local
-
This creates the example using localChannelProvider.
-
st.remote
-
This creates the example using the remote channel provider.
-
- -

Array Performance

-

Two main programs are available for testing the performance of large -arrays: arrayPerformanceMain and longArrayMonitorMain. -Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
-default
-arrayPerformance arrayPerformance 50000000 0.01 local 1 2 0.0
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName queueSize waitTime
-default
-longArrayMonitorMain arrayPerformance 2 0.0
-mrk> 
-
-

Example output

-
-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 50000000 0.01 local 1 2 0.0
-...
-first 5 last 5 sum 250000000 elements/sec 51.5164million changed {0} overrun {}
-arrayPerformance value 7 time 1.08532 iterations/sec 6.44973 elements/sec 322.487million
-first 6 last 6 sum 300000000 elements/sec 493.414million changed {1, 2} overrun {}
-first 7 last 7 sum 350000000 elements/sec 172.566million changed {1, 2} overrun {}
-first 8 last 8 sum 400000000 elements/sec 515.064million changed {1, 2} overrun {}
-first 9 last 9 sum 450000000 elements/sec 515.359million changed {1, 2} overrun {}
-arrayPerformance value 11 time 1.10601 iterations/sec 3.6166 elements/sec 180.83million
-first 10 last 10 sum 500000000 elements/sec 80.5679million changed {1, 2} overrun {}
-first 11 last 11 sum 550000000 elements/sec 499.455million changed {1, 2} overrun {}
-first 12 last 12 sum 600000000 elements/sec 504.324million changed {1, 2} overrun {}
-first 13 last 13 sum 650000000 elements/sec 101.517million changed {1, 2} overrun {}
-...
-
-

arrayPerformance

-

-This creates a PVRecord that has the structure:. -

-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers.

-

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
At least once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the interation count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
queueSize
-
The queueSize for the element queue. - A value less than 1 will become 1. -
-
waitTime
-
The time that longArrayMonitor will sleep after poll returns a monitorElement.
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report for each monitor event. -In the example output shown above each line starting with first -is a report.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
queueSize
-
waitTime
-
-

Some results

-

The results were from my laptop. -It has a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system is linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 50 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 4 iterations per second -and is putting about 200million elements per second. -Since each element is an int64 this means about 1.6gigaBytes per second. -

-

If queueSize is set to 1 thereport shows that arrayPerformance can perform about 10 iterations -per second and is putting about 500million elements per second. -Since each element is an int64 this means about 4gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-

-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 300million elements per second. -In addition the time between reports varies from just over 1 second to 1.3 seconds. -The reason is contention for transfering data between main memory and local caches. -The next section has an example that demonstrates what happens. -Note that if the array size is small enough to fix in the local cache then running longArrayMonitor -has almost no effect of arrayPerforance. -

-

Vector Performance

-

This example demonstrates how array size effects performance. -The example is run as:

-
-bin/linux-x86_64/vectorPerformanceMain -help
-vectorPerformanceMain size delay nThread
-default
-vectorPerformance 50000000 0.01 1 
-
-

Consider the following:

-
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
-...
-thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
-thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
-thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
-...
-thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
-thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
-thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
-thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
-thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
-thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
-thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
-thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
-thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
-thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
-thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
-thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
-thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
-thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
-thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
-thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
-thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
-thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
-
-

As more threads are running the slower each thread runs.

-

But now consider a size that fits in a local cache.

-

-bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
-...
-thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
-thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
-thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
-... 
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
-...
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-...
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
-...
-thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
-thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
-thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
-thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
-thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
-thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
-
-

Now the number of threads has a far smaller effect on the performance of each thread. -

- -
- - diff --git a/documentation/pvDatabaseCPP_20131113.html b/documentation/pvDatabaseCPP_20131113.html deleted file mode 100644 index f489f53..0000000 --- a/documentation/pvDatabaseCPP_20131113.html +++ /dev/null @@ -1,1803 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 13-Nov-2013

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20131113.html -
-
Previous version:
-
pvDatabaseCPP20131112.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 13-Nov-2013 version of of pvDatabaseCPP.

-

-

The previous version reported a problem with a queueSize of 1. -Further thought showed that queueSize must be >= 2. -This ensures that the locking between poll and dataChanged ensures that these two -methods always use separate instances of pvStructure, changeBitSet, and overrunBitSet.

- -

All channel methods except channelRPC, which is implemented -by pvAccess, have been implemented. -This project is ready for alpha users. -

-

Future enhancements in priority order:

-
-
Separate example that also has pvaSrv
-
Create a separate example that combines a V3IOC, - a pvDatabase, and pvaSrv. - This will not be done until pvDataCPP-md is merged into pvDataCPP. -
-
channelArray
-
The arguments that have type int should be changed to size_t - This will not be done until pvDataCPP-md is merged into pvDataCPP. -
-
Monitor Algorithms
-
Monitor algorithms have not been implemented. - Thus all monitors are onPut.
-
Create regression tests
-
Currently only examples exist and have been used for testing.
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In tha later case the IOC has both a database of V3 Records - and a pvDatabase.
-
exampleCounter
-
This is a simple example showing how to create a PVRecord and - how to deploy it either as a standalone process or as part of a V3IOC.
-
exampleServer
-
This example has a set of PVRecords. - Again the records can be deployed either as a standalone process or - as part of a V3IOC.
-
examplePVADoubleArrayGet
-
This is an example PVRecord that uses channelGet to get an array of doubles from - another PVRecord via pvAccess. The example shows how to get the value either via - remote pvAccess or by directly accessing the local channelProvider. - Again the records can be deployed either as a standalone process or - as part of a V3IOC.
-
-

database provides base classes that make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resources used by the implementation.
-
-

Getting started

-

Included with this project are three examples that are useful for -seeing how pvDatabase can be used by clients. -Each can be deployed either as a standalone process or as part of a V3IOC. -The examples are: -

-
exampleCounterMain
-
This has a database consisting of a single record named exampleCounter: - The exampleCounter is discussed in a following section.
-
exampleCounter
-
This is exampleCounter as part of a V3IOC.
-
exampleServerMain
-
This has a database with several records.
-
exampleServer
-
This is exampleServer as part of a V3IOC
-
examplePVADoubleArrayGetMain
-
This is the example of how to use pvAccess to get data.
-
examplePVADoubleArrayGet
-
This is examplePVADoubleArrayGet as part of a V3IOC
- -

-

exampleCounter

-

To start exampleCounterMain: -

-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounterMain
-

-

To start exampleCounter as part of a V3IOC: -

-mrk> pwd
-/home/hg/pvDatabaseCPP/iocBoot/exampleCounter
-mrk> ../../../bin/linux-x86_64/exampleCounter st.cmd
-

-

You can then issue the commands dbl and pvdbl: -

-epics> dbl
-double01
-epics> pvdbl
-exampleCounter
-epics> 
-
-double01 is a v3Record. -exampleCounter is a pvRecord. -

-

Starting exampleServer is similar. -After successfully running exampleCounterMain and exampleCounter then -try starting exampleServerMain and exampleServer. -

-

exampleServer

-

This example provides the exampleCounter record in addition to a number of other PVRecords. -Like exampleCounter it can be started either as a standalone main program or as -a part of a V3IOC. -The exampleServer pvDatabase has many records including the following:

-
-
exampleCounter
-
A record that is an instance of exampleCounter described below. - The most useful channel methods are channelGet, channelProcess, - and monitor.
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
examplePowerSupply
-
Can be used by channelGet, channelPut, channelPutGet, and monitor.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

swtshell

-

The Java program - - swtshell -can be used to access pvDatabase.

-

In particular read the sections "Getting Started" and "Simple Example". -They will work on the exampleServer with the following differences: -

-
startExample.zip
-
Do NOT use this. Instead run the exampleServer as described above.
-
channelList result
-
The result of channelList will show the list of records that - exampleServer has rather than the records from startExample.zip
-
-

- -

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP implements the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Example PVRecord Extension

-

The example implements a simple counter. -The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounter 
-
-
-

The example consists of four components:

-
-
ExampleCounter.h
-
The source code for the counter. - It is located in directory pvDatabaseCPP/example/src/exampleCounter. -
-
exampleCounterMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. - It is located in directory pvDatabaseCPP/example/exampleCounter. -
-
V3IOC/exampleCounter
-
This is a directory that packages exampleCounter to make it - part of a V3IOC.
-
iocBoot/exampleCounter
-
A place to start exampleCounter as part of a V3IOC. - It follows the normal iocCore conventions.
-
-

ExampleCounter.h

-

The example resides in src/database. -The complete implementation is in the header file. -A serious implementation might break the code into a header and an -implementation file.

-

The description consists of

-
-class ExampleCounter;
-typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
-
-class ExampleCounter :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleCounter);
-    static ExampleCounterPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleCounter();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleCounter(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    epics::pvData::PVLongPtr pvValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleCounter
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleCounter
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleCounterPtr ExampleCounter::create(
-    epics::pvData::String const & recordName)
-{
-    epics::pvData::PVStructurePtr pvStructure =
-       epics::pvData::getStandardPVField()->scalar(
-           epics::pvData::pvDouble,"timeStamp,alarm"");
-    ExampleCounterPtr pvRecord(
-        new ExampleCounter(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleCounterPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleCounter::ExampleCounter(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleCounter::~ExampleCounter()
-{
-}
-
-void ExampleCounter::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleCounter::init()
-{
-
-    initPVRecord();
-    epics::pvData::PVFieldPtr pvField;
-    pvValue = getPVStructure()->getLongField("value");
-    if(pvValue==NULL) return false;
-    return true;
-}
-
-This: -
    -
  • Calls initRecord which is implemented by the base class. - It MUST be called.
  • -
  • Calls getLongField to get the interface to the value field, - which must be a scalar with type long.
  • -
  • If a long value field was not found it returns false.
  • -
  • Returns true
  • -
-

The implementation of process is:

-
-void ExampleCounter::process()
-{
-    pvValue->put(pvValue->get() + 1.0);
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It adds 1.0 to the current value. -It then sets the timeStamp to the current time. -

exampleCounterMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleCounter");
-    PVRecordPtr pvRecord = ExampleCounter::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    cout << "exampleCounter\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleCounter record with the name exampleCounter -
  • -
  • Prints exampleCounter on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleCounter

-

This has two subdirectories: -

-
Db
-
This has a template for a single v3Record.
-
src
-
This has code to allow exampleCounter to reside in a V3IOC.
-
-

-

The src directory has the following components:

-
-
exampleCounterMain.cpp
-
This is just a standard Main for a V3IOC.
-
exampleCounterInclude.dbd
-
This is: -
-include "base.dbd"
-include "PVAServerRegister.dbd"
-registrar("exampleCounterRegister")
-       
- This includes the dbd components required from base, - the dbd file for starting pvAccess and the local channelProvider, - and the dbd file for the example. The later is for the code from the - next file. -
-
exampleCounter.cpp
-
This is the code that registers a command the can be issued - via the iocsh, which is the console for a V3IOC. - The example supports a single command: -
-exampleCounterCreateRecord recordName
-       
-
-
-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the C++ implementation of package pvAccess in pvIOCJava. - The localChannelProvider accesses data from PVRecords. - It implements all channel methods except channelRPC, which is implemented by pvAccessCPP.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

iocshell commands

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider

-
stopPVAServer
-
Stop the local channel provider
-
pvdbl
-
Provides a list of all the pvRecords in database master - It is similar to the iocsh command dbl
-
-

The client commands are provided via PVAClientRegister.dbd and the other commands -via PVAServerRegister.dbd.

-

In addition any code that implements a PVRecord must implement an ioc command. -The directory example has examples of how to implement the registration code. -See example/V3IOC/exampleCounter/src/ for a simple example.

- - -

database

-

src/database

-

This Directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
-

src/special

-

This directory has the following files:

-
-
powerSupplyRecordTest.h
-
- This provides code that simulates a power supply. - It computes the current from the voltage and power. - It is used for testing. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The testExampleServer creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-      
-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

pvDatabase.h

-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
process
-
Virtual method. - Derived classes must implement this method. - The base implementation does nothing. -
-
destroy
-
This is a virtual method. - A derived class must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it';s own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is access by the server side of remote pvAccess.

-

channelProviderLocal

-

This is a complete implementation of channelProvider and , -except for channelRPC, provides a complete implementation of Channel -as defined by pvAccess. -For monitors it calls the code described in the following sections.

-

pvCopy

-

This provides code that creates a top level PVStructure that is an arbitrary -subset of the fields in the PVStructure from a PVRecord. -In addition it provides code that monitors changes to the fields in a PVRecord. -A client configures the desired set of subfields and monitoring options -via a pvRequest structure. -pvAccess provides a class CreatePVRequest that creates a pvRequest. -The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava. -

-

monitorAlgorithm

-

Currently all that is implemented is a header file. -The only algorithm currently implemented is onPut -

-

monitorFactory

-

Overview

-

epics::pvData::monitor defines the monitor interfaces -as seen by a client. -See - pvDatabaseCPP.html - For details.

-

-monitorFactory implements the -monitoring interfaces for a PVRecord. -It implements queueSize=0 and queueSize>=2. -

- -

-The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy. -When PVCopyMonitor tells monitor that changes -have occurred, monitor applies the appropriate algorithm to each changed field.

- -

Currently only algorithm onPut is implemented but, -like pvIOCJava there are plans to support for the following monitor algorithms:

-
-
onPut
-
A monitor is issued whenever a put is issued to the field. This is the - default unless the record defines deadbands for a field. An exception is - the top level timeStamp which by default is made onChange and monitor - will not be raised.
-
onChange
-
This provides two options: 1) A monitor is raised whenever a field - changes value, and 2) A monitor will never be raised for the field.
-
deadband
-
The field must be a numeric scalar. Whenever the absolute or percentage - value of the field changes by more than a deadband a monitor is issued. - The record instance can also define deadbands.
-
periodic
-
A monitor is issued at a periodic rate if a put was issued to any field - being monitored.
-
-

MonitorFactory

-

MonitorFactory provides the following methods:

-
class MonitorFactory
-{
-    static MonitorPtr create(
-        PVRecordPtr const & pvRecord,
-        MonitorRequester::shared_pointer const & monitorRequester,
-        PVStructurePtr const & pvRequest);
-    static void registerMonitorAlgorithmCreater(
-        MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
-        String const & algorithmName);
-}
- -

where

-
-
create
-
Create a monitor. The arguments are: -
-
pvRecord
-
The record being monitored.
-
monitorRequester
-
The monitor requester. This is the code to which monitot events - will be delivered.
-
pvRequest
-
The request options
-
-
-
registerMonitorAlgorithmCreater
-
Called by code that implements a monitor algorithm.
-
-

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generted - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

-

recordList

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string database master
-        string regularExpression .*
-    structure result
-        string status 
-        string[] names
-
-where: -
-
database
-
The name of the datbase. The default is "master"
-
regularExpression
-
For now this is ignored and the complete list of names is always - returned.
-
status
-
The status of a putGet request.
-
names
-
The list of record names.
-
-

Note that swtshell has a command channelList that -requires that a record of this type is present and calls it. -Thus user code does not have to use a channelGetPut to get the list -of record names.

-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

powerSupplyRecordTest

-

This simulates a simple power supply record. -It is used for testing.

-

Accessing Other PVRecords

-

This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - caused deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all lockimg issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transfered between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-ChannelProvider::shared_pointer provider = channelAccess->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
-

examplePVADoubleArrayGet

-examplePVADoubleArrayGet shows how to use pvAccess to get data.

-

The code resides in three directories:

-
-
example/src/examplePVADoubleArrayGet
-
This is the implementation code for the example.
-
example/examplePVADoubleArrayGet
-
This is the code that starts the example as a main program.
-
example/V3IOC/examplePVADoubleArrayGet/src
-
This is the code for starting the example as part of a V3IOC.
-
iocBoot/examplePVADoubleArrayGet
-
This has the st.cmd files to start the example.
-
-

examplePVADoubleArrayGet Implementation

-

examplePVADoubleArrayGet.h contains the following:

-
-...
-class ExamplePVADoubleArrayGet :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExamplePVADoubleArrayGet);
-    static ExamplePVADoubleArrayGetPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::String const & providerName,
-        epics::pvData::String const & channelName
-        );
-    virtual ~ExamplePVADoubleArrayGet() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExamplePVADoubleArrayGetPtr ExamplePVADoubleArrayGet::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExamplePVADoubleArrayGetPtr pvRecord(
-        new ExamplePVADoubleArrayGet(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExamplePVADoubleArrayGet instance, -and then calls the init method and the returns a ExamplePVADoubleArrayGetPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExamplePVADoubleArrayGet::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

Starting the example as a main program

-
-...
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ClientFactory::start();
-    ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
-    PVRecordPtr pvRecord;
-    bool result(false);
-    String recordName;
-    PVStructurePtr pvStructure = standardPVField->scalarArray(
-        pvDouble,"alarm,timeStamp");
-    recordName = "doubleArray";
-    pvRecord = PVRecord::create(recordName,pvStructure);
-    result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    recordName = "examplePVADoubleArrayGet";
-    if(argc>1) recordName = argv[1];
-    String providerName("local");
-    if(argc>2) providerName = argv[2];
-    String channelName("doubleArray");
-    if(argc>3) channelName = argv[3];
-    pvRecord = ExamplePVADoubleArrayGet::create(
-        recordName,providerName,channelName);
-    if(pvRecord!=NULL) {
-        result = master->addRecord(pvRecord);
-        cout << "result of addRecord " << recordName << " " << result << endl;
-    } else {
-        cout << "ExamplePVADoubleArrayGet::create failed" << endl;
-    }
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    serverContext->shutdown();
-    epicsThreadSleep(1.0);
-    serverContext->destroy();
-    ClientFactory::stop();
-    channelProvider->destroy();
-    return 0;
-}
-
-

The first statements initializes the client factory and localChannelProvider.

-

-It then creates a PVRecord that has a double array as the value field. -The name of the record is doubleArray. -This is the record that can be accessed via pvAccess if the channelName is -doubleArray. -

-

It then creates a examplePVADoubleArrayGet instance.

-

It the runs forever until the exit is typed.

- -

Start the example as part of a V3IOC

-The directory example/V3IOC/examplePVADoubleArrayGet has two subdirectories: Db and src. -These are like for any other V3IOC application. -The Db directory is for a regular ai record. -The src directory has code similar to any V3IOC application. -In particular it has definitions and code for creating the following iocsh commands:

-
-examplePVADoubleArrayGetRegister recordName providerName channelName
-
-where -
-
recordName
-
providerName
-
channelName
-
-

The directory iocBoot/examplePVADoubleArrayGet has two st.cmd files:

-
-
st.local
-
This creates the example using localChannelProvider.
-
st.remote
-
This creates the example using the remote channel provider.
-
- -

Array Performance

-

Two main programs are available for testing the performance of large -arrays: arrayPerformanceMain and longArrayMonitorMain. -Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
-default
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0.0
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName queueSize waitTime
-default
-longArrayMonitorMain arrayPerformance 2 0.0
-mrk> 
-
-

Example output

-
-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0.0
-...
-first 526 last 526 sum 5260000000 elements/sec 499.337million changed {1, 2} overrun {}
-first 527 last 527 sum 5270000000 elements/sec 568.729million changed {1, 2} overrun {}
-first 528 last 528 sum 5280000000 elements/sec 505.281million changed {1, 2} overrun {}
-first 529 last 529 sum 5290000000 elements/sec 506.33million changed {1, 2} overrun {}
-first 530 last 530 sum 5300000000 elements/sec 574.022million changed {1, 2} overrun {}
-arrayPerformance value 532 time 1.00839 iterations/sec 51.5672 elements/sec 515.672million
-first 531 last 531 sum 5310000000 elements/sec 484.185million changed {1, 2} overrun {}
-first 532 last 532 sum 5320000000 elements/sec 531.4million changed {1, 2} overrun {}
-first 533 last 533 sum 5330000000 elements/sec 428.229million changed {1, 2} overrun {}
-first 534 last 534 sum 5340000000 elements/sec 427.387million changed {1, 2} overrun {}
-first 535 last 535 sum 5350000000 elements/sec 443.669million changed {1, 2} overrun {}
-...
-
-

arrayPerformance

-

-This creates a PVRecord that has the structure:. -

-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers.

-

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
At least once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the interation count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
queueSize
-
The queueSize for the element queue. - A value less than 1 will become 1. -
-
waitTime
-
The time that longArrayMonitor will sleep after poll returns a monitorElement.
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report for each monitor event. -In the example output shown above each line starting with first -is a report.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
queueSize
-
waitTime
-
-

Some results

-

The results were from my laptop. -It has a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system is linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 10 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 50 iterations per second -and is putting about 500million elements per second. -Since each element is an int64 this means about 4gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-

-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 25 interations per second and 250 million elements per second. -The next section has an example that demonstrates what happens. -Note that if the array size is small enough to fit in the local cache then running longArrayMonitor -has almost no effect of arrayPerforance. -

-

Vector Performance

-

This example demonstrates how array size effects performance. -The example is run as:

-
-bin/linux-x86_64/vectorPerformanceMain -help
-vectorPerformanceMain size delay nThread
-default
-vectorPerformance 50000000 0.01 1 
-
-

Consider the following:

-
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
-...
-thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
-thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
-thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
-...
-thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
-thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
-thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
-thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
-thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
-thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
-thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
-thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
-thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
-thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
-thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
-thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
-thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
-thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
-thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
-thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
-thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
-thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
-
-

As more threads are running the slower each thread runs.

-

But now consider a size that fits in a local cache.

-

-bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
-...
-thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
-thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
-thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
-... 
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
-...
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-...
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
-...
-thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
-thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
-thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
-thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
-thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
-thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
-
-

Now the number of threads has a far smaller effect on the performance of each thread. -

- -
- - diff --git a/documentation/pvDatabaseCPP_20131120.html b/documentation/pvDatabaseCPP_20131120.html deleted file mode 100644 index e33ad37..0000000 --- a/documentation/pvDatabaseCPP_20131120.html +++ /dev/null @@ -1,1936 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 20-Nov-2013

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20131120.html -
-
Previous version:
-
pvDatabaseCPP20131112.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 20-Nov-2013 version of of pvDatabaseCPP.

-

- -

All channel methods except channelRPC, which is implemented -by pvAccess, have been implemented. -This project is ready for alpha users. -

- -

Since the last version the longArrayGet and longArrayPut examples were added. -More testing for monitor queues and memory leaks was done. -Everything looks good!! -But there are still two unresolved problems: -

-
memory leak
-
arrayPerformanceMain shows a slight memory leak at termination.
-
channel destroy and recreate
-
longArrayGet and longArrayPut fail if the channel is destroyed and - immediately recreated. -
-
-

-

Future enhancements in priority order:

-
-
Separate example that also has pvaSrv
-
Create a separate example that combines a V3IOC, - a pvDatabase, and pvaSrv. - This will not be done until pvDataCPP-md is merged into pvDataCPP. -
-
channelArray
-
The arguments that have type int should be changed to size_t - This will not be done until pvDataCPP-md is merged into pvDataCPP. -
-
Monitor Algorithms
-
Monitor algorithms have not been implemented. - Thus all monitors are onPut.
-
Create regression tests
-
Currently only examples exist and have been used for testing.
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In tha later case the IOC has both a database of V3 Records - and a pvDatabase.
-
exampleCounter
-
This is a simple example showing how to create a PVRecord and - how to deploy it either as a standalone process or as part of a V3IOC.
-
exampleServer
-
This example has a set of PVRecords. - Again the records can be deployed either as a standalone process or - as part of a V3IOC.
-
examplePVADoubleArrayGet
-
This is an example PVRecord that uses channelGet to get an array of doubles from - another PVRecord via pvAccess. The example shows how to get the value either via - remote pvAccess or by directly accessing the local channelProvider. - Again the records can be deployed either as a standalone process or - as part of a V3IOC.
-
-

database provides base classes that make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resources used by the implementation.
-
-

Getting started

-

Included with this project are three examples that are useful for -seeing how pvDatabase can be used by clients. -Each can be deployed either as a standalone process or as part of a V3IOC. -The examples are: -

-
exampleCounterMain
-
This has a database consisting of a single record named exampleCounter: - The exampleCounter is discussed in a following section.
-
exampleCounter
-
This is exampleCounter as part of a V3IOC.
-
exampleServerMain
-
This has a database with several records.
-
exampleServer
-
This is exampleServer as part of a V3IOC
-
examplePVADoubleArrayGetMain
-
This is the example of how to use pvAccess to get data.
-
examplePVADoubleArrayGet
-
This is examplePVADoubleArrayGet as part of a V3IOC
- -

-

exampleCounter

-

To start exampleCounterMain: -

-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounterMain
-

-

To start exampleCounter as part of a V3IOC: -

-mrk> pwd
-/home/hg/pvDatabaseCPP/iocBoot/exampleCounter
-mrk> ../../../bin/linux-x86_64/exampleCounter st.cmd
-

-

You can then issue the commands dbl and pvdbl: -

-epics> dbl
-double01
-epics> pvdbl
-exampleCounter
-epics> 
-
-double01 is a v3Record. -exampleCounter is a pvRecord. -

-

Starting exampleServer is similar. -After successfully running exampleCounterMain and exampleCounter then -try starting exampleServerMain and exampleServer. -

-

exampleServer

-

This example provides the exampleCounter record in addition to a number of other PVRecords. -Like exampleCounter it can be started either as a standalone main program or as -a part of a V3IOC. -The exampleServer pvDatabase has many records including the following:

-
-
exampleCounter
-
A record that is an instance of exampleCounter described below. - The most useful channel methods are channelGet, channelProcess, - and monitor.
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
examplePowerSupply
-
Can be used by channelGet, channelPut, channelPutGet, and monitor.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

swtshell

-

The Java program - - swtshell -can be used to access pvDatabase.

-

In particular read the sections "Getting Started" and "Simple Example". -They will work on the exampleServer with the following differences: -

-
startExample.zip
-
Do NOT use this. Instead run the exampleServer as described above.
-
channelList result
-
The result of channelList will show the list of records that - exampleServer has rather than the records from startExample.zip
-
-

- -

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP implements the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Example PVRecord Extension

-

The example implements a simple counter. -The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounter 
-
-
-

The example consists of four components:

-
-
ExampleCounter.h
-
The source code for the counter. - It is located in directory pvDatabaseCPP/example/src/exampleCounter. -
-
exampleCounterMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. - It is located in directory pvDatabaseCPP/example/exampleCounter. -
-
V3IOC/exampleCounter
-
This is a directory that packages exampleCounter to make it - part of a V3IOC.
-
iocBoot/exampleCounter
-
A place to start exampleCounter as part of a V3IOC. - It follows the normal iocCore conventions.
-
-

ExampleCounter.h

-

The example resides in src/database. -The complete implementation is in the header file. -A serious implementation might break the code into a header and an -implementation file.

-

The description consists of

-
-class ExampleCounter;
-typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
-
-class ExampleCounter :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleCounter);
-    static ExampleCounterPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleCounter();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleCounter(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    epics::pvData::PVLongPtr pvValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleCounter
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleCounter
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleCounterPtr ExampleCounter::create(
-    epics::pvData::String const & recordName)
-{
-    epics::pvData::PVStructurePtr pvStructure =
-       epics::pvData::getStandardPVField()->scalar(
-           epics::pvData::pvDouble,"timeStamp,alarm"");
-    ExampleCounterPtr pvRecord(
-        new ExampleCounter(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleCounterPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleCounter::ExampleCounter(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleCounter::~ExampleCounter()
-{
-}
-
-void ExampleCounter::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleCounter::init()
-{
-
-    initPVRecord();
-    epics::pvData::PVFieldPtr pvField;
-    pvValue = getPVStructure()->getLongField("value");
-    if(pvValue==NULL) return false;
-    return true;
-}
-
-This: -
    -
  • Calls initRecord which is implemented by the base class. - It MUST be called.
  • -
  • Calls getLongField to get the interface to the value field, - which must be a scalar with type long.
  • -
  • If a long value field was not found it returns false.
  • -
  • Returns true
  • -
-

The implementation of process is:

-
-void ExampleCounter::process()
-{
-    pvValue->put(pvValue->get() + 1.0);
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It adds 1.0 to the current value. -It then sets the timeStamp to the current time. -

exampleCounterMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleCounter");
-    PVRecordPtr pvRecord = ExampleCounter::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    cout << "exampleCounter\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleCounter record with the name exampleCounter -
  • -
  • Prints exampleCounter on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleCounter

-

This has two subdirectories: -

-
Db
-
This has a template for a single v3Record.
-
src
-
This has code to allow exampleCounter to reside in a V3IOC.
-
-

-

The src directory has the following components:

-
-
exampleCounterMain.cpp
-
This is just a standard Main for a V3IOC.
-
exampleCounterInclude.dbd
-
This is: -
-include "base.dbd"
-include "PVAServerRegister.dbd"
-registrar("exampleCounterRegister")
-       
- This includes the dbd components required from base, - the dbd file for starting pvAccess and the local channelProvider, - and the dbd file for the example. The later is for the code from the - next file. -
-
exampleCounter.cpp
-
This is the code that registers a command the can be issued - via the iocsh, which is the console for a V3IOC. - The example supports a single command: -
-exampleCounterCreateRecord recordName
-       
-
-
-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the C++ implementation of package pvAccess in pvIOCJava. - The localChannelProvider accesses data from PVRecords. - It implements all channel methods except channelRPC, which is implemented by pvAccessCPP.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

iocshell commands

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider

-
stopPVAServer
-
Stop the local channel provider
-
pvdbl
-
Provides a list of all the pvRecords in database master - It is similar to the iocsh command dbl
-
-

The client commands are provided via PVAClientRegister.dbd and the other commands -via PVAServerRegister.dbd.

-

In addition any code that implements a PVRecord must implement an ioc command. -The directory example has examples of how to implement the registration code. -See example/V3IOC/exampleCounter/src/ for a simple example.

- - -

database

-

src/database

-

This Directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
-

src/special

-

This directory has the following files:

-
-
powerSupplyRecordTest.h
-
- This provides code that simulates a power supply. - It computes the current from the voltage and power. - It is used for testing. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The testExampleServer creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-      
-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

pvDatabase.h

-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
process
-
Virtual method. - Derived classes must implement this method. - The base implementation does nothing. -
-
destroy
-
This is a virtual method. - A derived class must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it';s own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is access by the server side of remote pvAccess.

-

channelProviderLocal

-

This is a complete implementation of channelProvider and , -except for channelRPC, provides a complete implementation of Channel -as defined by pvAccess. -For monitors it calls the code described in the following sections.

-

pvCopy

-

This provides code that creates a top level PVStructure that is an arbitrary -subset of the fields in the PVStructure from a PVRecord. -In addition it provides code that monitors changes to the fields in a PVRecord. -A client configures the desired set of subfields and monitoring options -via a pvRequest structure. -pvAccess provides a class CreatePVRequest that creates a pvRequest. -The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava. -

-

monitorAlgorithm

-

Currently all that is implemented is a header file. -The only algorithm currently implemented is onPut -

-

monitorFactory

-

Overview

-

epics::pvData::monitor defines the monitor interfaces -as seen by a client. -See - pvDatabaseCPP.html - For details.

-

-monitorFactory implements the -monitoring interfaces for a PVRecord. -It implements queueSize=0 and queueSize>=2. -

- -

-The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy. -When PVCopyMonitor tells monitor that changes -have occurred, monitor applies the appropriate algorithm to each changed field.

- -

Currently only algorithm onPut is implemented but, -like pvIOCJava there are plans to support for the following monitor algorithms:

-
-
onPut
-
A monitor is issued whenever a put is issued to the field. This is the - default unless the record defines deadbands for a field. An exception is - the top level timeStamp which by default is made onChange and monitor - will not be raised.
-
onChange
-
This provides two options: 1) A monitor is raised whenever a field - changes value, and 2) A monitor will never be raised for the field.
-
deadband
-
The field must be a numeric scalar. Whenever the absolute or percentage - value of the field changes by more than a deadband a monitor is issued. - The record instance can also define deadbands.
-
periodic
-
A monitor is issued at a periodic rate if a put was issued to any field - being monitored.
-
-

MonitorFactory

-

MonitorFactory provides the following methods:

-
class MonitorFactory
-{
-    static MonitorPtr create(
-        PVRecordPtr const & pvRecord,
-        MonitorRequester::shared_pointer const & monitorRequester,
-        PVStructurePtr const & pvRequest);
-    static void registerMonitorAlgorithmCreater(
-        MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
-        String const & algorithmName);
-}
- -

where

-
-
create
-
Create a monitor. The arguments are: -
-
pvRecord
-
The record being monitored.
-
monitorRequester
-
The monitor requester. This is the code to which monitot events - will be delivered.
-
pvRequest
-
The request options
-
-
-
registerMonitorAlgorithmCreater
-
Called by code that implements a monitor algorithm.
-
-

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generted - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

-

recordList

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string database master
-        string regularExpression .*
-    structure result
-        string status 
-        string[] names
-
-where: -
-
database
-
The name of the datbase. The default is "master"
-
regularExpression
-
For now this is ignored and the complete list of names is always - returned.
-
status
-
The status of a putGet request.
-
names
-
The list of record names.
-
-

Note that swtshell has a command channelList that -requires that a record of this type is present and calls it. -Thus user code does not have to use a channelGetPut to get the list -of record names.

-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

powerSupplyRecordTest

-

This simulates a simple power supply record. -It is used for testing.

-

Accessing Other PVRecords

-

This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - caused deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all lockimg issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transfered between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-ChannelProvider::shared_pointer provider = channelAccess->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
-

examplePVADoubleArrayGet

-examplePVADoubleArrayGet shows how to use pvAccess to get data.

-

The code resides in three directories:

-
-
example/src/examplePVADoubleArrayGet
-
This is the implementation code for the example.
-
example/examplePVADoubleArrayGet
-
This is the code that starts the example as a main program.
-
example/V3IOC/examplePVADoubleArrayGet/src
-
This is the code for starting the example as part of a V3IOC.
-
iocBoot/examplePVADoubleArrayGet
-
This has the st.cmd files to start the example.
-
-

examplePVADoubleArrayGet Implementation

-

examplePVADoubleArrayGet.h contains the following:

-
-...
-class ExamplePVADoubleArrayGet :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExamplePVADoubleArrayGet);
-    static ExamplePVADoubleArrayGetPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::String const & providerName,
-        epics::pvData::String const & channelName
-        );
-    virtual ~ExamplePVADoubleArrayGet() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExamplePVADoubleArrayGetPtr ExamplePVADoubleArrayGet::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExamplePVADoubleArrayGetPtr pvRecord(
-        new ExamplePVADoubleArrayGet(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExamplePVADoubleArrayGet instance, -and then calls the init method and the returns a ExamplePVADoubleArrayGetPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExamplePVADoubleArrayGet::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

Starting the example as a main program

-
-...
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ClientFactory::start();
-    ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
-    PVRecordPtr pvRecord;
-    bool result(false);
-    String recordName;
-    PVStructurePtr pvStructure = standardPVField->scalarArray(
-        pvDouble,"alarm,timeStamp");
-    recordName = "doubleArray";
-    pvRecord = PVRecord::create(recordName,pvStructure);
-    result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    recordName = "examplePVADoubleArrayGet";
-    if(argc>1) recordName = argv[1];
-    String providerName("local");
-    if(argc>2) providerName = argv[2];
-    String channelName("doubleArray");
-    if(argc>3) channelName = argv[3];
-    pvRecord = ExamplePVADoubleArrayGet::create(
-        recordName,providerName,channelName);
-    if(pvRecord!=NULL) {
-        result = master->addRecord(pvRecord);
-        cout << "result of addRecord " << recordName << " " << result << endl;
-    } else {
-        cout << "ExamplePVADoubleArrayGet::create failed" << endl;
-    }
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    serverContext->shutdown();
-    epicsThreadSleep(1.0);
-    serverContext->destroy();
-    ClientFactory::stop();
-    channelProvider->destroy();
-    return 0;
-}
-
-

The first statements initializes the client factory and localChannelProvider.

-

-It then creates a PVRecord that has a double array as the value field. -The name of the record is doubleArray. -This is the record that can be accessed via pvAccess if the channelName is -doubleArray. -

-

It then creates a examplePVADoubleArrayGet instance.

-

It the runs forever until the exit is typed.

- -

Start the example as part of a V3IOC

-The directory example/V3IOC/examplePVADoubleArrayGet has two subdirectories: Db and src. -These are like for any other V3IOC application. -The Db directory is for a regular ai record. -The src directory has code similar to any V3IOC application. -In particular it has definitions and code for creating the following iocsh commands:

-
-examplePVADoubleArrayGetRegister recordName providerName channelName
-
-where -
-
recordName
-
providerName
-
channelName
-
-

The directory iocBoot/examplePVADoubleArrayGet has two st.cmd files:

-
-
st.local
-
This creates the example using localChannelProvider.
-
st.remote
-
This creates the example using the remote channel provider.
-
- -

Array Performance and Memory Example

-

This section describes main programs that demonstrate performance -of large arrays and can also be used to check for memory leaks. -Checking for memory leaks can be accomplished by running the programs with valgrind -or some other memory check program. -

-

Brief Summary

-

The programs are:

-
-
arrayPerformanceMain
-
This is server and also a configurable number of longArrayMonitor clients. - The clients can use either the local or - remote providers. The moitor code is the same code that is used by longArrayMonitorMain. -
-
longArrayMonitorMain
-
Remote client that monitors the array served by arrayPerformanceMain.
-
longArrayGetMain
-
Remote client that uses channelGet to access the array served by arrayPerformanceMain.
-
longArrayPutMain
-
Remote client that uses channelPut to access the array served by arrayPerformanceMain.
-
-

Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
-default
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0.0
-
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName queueSize waitTime
-default
-longArrayMonitorMain arrayPerformance 2 0.0
-
-mrk> bin/linux-x86_64/longArrayGetMain -help
-longArrayGetMain channelName iterBetweenCreateChannel iterBetweenCreateChannelGet delayTime
-default
-longArrayGetMain arrayPerformance 0 0 1
-
-mrk> bin/linux-x86_64/longArrayPutMain -help
-longArrayPutMain channelName arraySize iterBetweenCreateChannel iterBetweenCreateChannelPut delayTime
-default
-longArrayPutMain arrayPerformance 10 0 0 1
-
-mrk> 
-
-

Example output

-

Note: These may fail if run on a platform that does not have sufficent memory,

-

To see an example just execute the following commands in four different terminal windows:

-
-bin/linux/<arch>/arrayPerformanceMain
-bin/linux/<arch>/longArrayMonitorMain
-bin/linux/<arch>/longArrayGetMain
-bin/linux/<arch>/longArrayPutMain
-
-

Each program generates a report every second when it has somthing to report. -Examples are: -

-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0
-...
- monitors/sec 66 first 131 last 131 changed {1, 2} overrun {} megaElements/sec 656.999
-arrayPerformance value 132 time 1.00486 Iterations/sec 65.681 megaElements/sec 656.81
- monitors/sec 66 first 197 last 197 changed {1, 2} overrun {} megaElements/sec 656.304
-arrayPerformance value 198 time 1.00563 Iterations/sec 65.6307 megaElements/sec 656.307
- monitors/sec 66 first 263 last 263 changed {1, 2} overrun {} megaElements/sec 654.824
-...
-
-
-mrk> bin/linux-x86_64/longArrayMonitorMain
-longArrayMonitorMain arrayPerformance 2 0
-...
- monitors/sec 6 first 2357 last 2357 changed {1, 2} overrun {} megaElements/sec 68.6406
- monitors/sec 13 first 2385 last 2385 changed {1, 2} overrun {} megaElements/sec 118.72
- monitors/sec 9 first 2418 last 2418 changed {1, 2} overrun {1, 2} megaElements/sec 85.0984
-...
-
-
-mrk> bin/linux-x86_64/longArrayPutMain
-longArrayPutMain arrayPerformance 10 0 0 1
-...
-put numChannelPut 0 time 1.00148 Elements/sec 79.8819
-put numChannelPut 1 time 1.00176 Elements/sec 79.8598
-...
-
-
-mrk> bin/linux-x86_64/longArrayGetMain
-longArrayGetMain arrayPerformance 0 0 1
-...
-get kiloElements/sec 7384.61
-get kiloElements/sec 8726.34
-...
-
- -

arrayPerformance

-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
queueSize
-
The queueSize for the element queue. - A value less than 1 will become 1. -
-
waitTime
-
The time that longArrayMonitor will sleep after poll returns a monitorElement.
-
-

-arrayPerformance creates a PVRecord that has the structure:. -

-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers.

-

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
Once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the interation count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report every second showing how many elements has received. -For every monitor it also checks that the number of alements is >0 and the -the first element equals the last element. It reports an error if either -of these conditions is not true.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
queueSize
-
The queueSize. Note that any size <2 is made 2.
-
waitTime
-
The time to wait after a poll request returns a monitorElement. - This can be used to force an overrun of the client even if there is no - overrun on the server.
-
-

longArrayGet

-

This is a pvAccess client that uses channelGet to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayGetMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelGet
-
The number of iterations between destroying and recreating the channelGet. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to dalay between gets.
-
-

longArrayPut

-

This is a pvAccess client that uses channelPut to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayPutMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
arraySize
-
The capacity and length of the array to put to the server.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelPut
-
The number of iterations between destroying and recreating the channelPut. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to dalay between gets.
-
- -

Some results

-

array performance

-

The results were from my laptop. -It has a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system is linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 10 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 50 iterations per second -and is putting about 500million elements per second. -Since each element is an int64 this means about 4gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-

-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 25 interations per second and 250 million elements per second. -The next section has an example that demonstrates what happens. -Note that if the array size is small enough to fit in the local cache then running longArrayMonitor -has almost no effect of arrayPerforance. -

-

memory leaks

-

Running longArrayMonitorMain, longArrayPutMain, and longArrayGetMain -under valgrind shows no memory leaks.

-

arrayPerformanceMain shows the following:

-
-==9125== LEAK SUMMARY:
-==9125==    definitely lost: 0 bytes in 0 blocks
-==9125==    indirectly lost: 0 bytes in 0 blocks
-==9125==      possibly lost: 576 bytes in 2 blocks
-
-

The possibly leaked is either 1 or 2 blocks. -It seems to be the same if clients are connected. -

-

iterBetweenCreateChannel

-

If this is not zero then the attempt to destroy and recreate the channel fails.

-

Vector Performance

-

This example demonstrates how array size effects performance. -The example is run as:

-
-bin/linux-x86_64/vectorPerformanceMain -help
-vectorPerformanceMain size delay nThread
-default
-vectorPerformance 50000000 0.01 1 
-
-

Consider the following:

-
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
-...
-thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
-thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
-thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
-...
-thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
-thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
-thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
-thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
-thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
-thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
-thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
-thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
-thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
-thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
-thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
-thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
-thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
-thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
-thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
-thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
-thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
-thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
-
-

As more threads are running the slower each thread runs.

-

But now consider a size that fits in a local cache.

-

-bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
-...
-thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
-thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
-thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
-... 
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
-...
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-...
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
-...
-thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
-thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
-thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
-thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
-thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
-thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
-
-

Now the number of threads has a far smaller effect on the performance of each thread. -

- -
- - diff --git a/documentation/pvDatabaseCPP_20131121.html b/documentation/pvDatabaseCPP_20131121.html deleted file mode 100644 index 87b1425..0000000 --- a/documentation/pvDatabaseCPP_20131121.html +++ /dev/null @@ -1,1936 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 21-Nov-2013

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20131121.html -
-
Previous version:
-
pvDatabaseCPP20131120.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 21-Nov-2013 version of of pvDatabaseCPP.

-

- -

All channel methods except channelRPC, which is implemented -by pvAccess, have been implemented. -This project is ready for alpha users. -

- -

Since the last version the longArrayGet and longArrayPut examples were added. -More testing for monitor queues and memory leaks was done. -Everything looks good!! -But there are still two unresolved problems: -

-
memory leak
-
arrayPerformanceMain shows a slight memory leak at termination.
-
channel destroy and recreate
-
longArrayGet and longArrayPut fail if the channel is destroyed and - immediately recreated. - If epicsThreadSleep(1.0) is called between destroy and recreate then they work. - The current version of each does wait. -
-
-

-

Future enhancements in priority order:

-
-
Separate example that also has pvaSrv
-
Create a separate example that combines a V3IOC, - a pvDatabase, and pvaSrv. - This will not be done until pvDataCPP-md is merged into pvDataCPP. -
-
channelArray
-
The arguments that have type int should be changed to size_t - This will not be done until pvDataCPP-md is merged into pvDataCPP. -
-
Monitor Algorithms
-
Monitor algorithms have not been implemented. - Thus all monitors are onPut.
-
Create regression tests
-
Currently only examples exist and have been used for testing.
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also includes the monitor and pvCopy components from pvIOCJava
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In tha later case the IOC has both a database of V3 Records - and a pvDatabase.
-
exampleCounter
-
This is a simple example showing how to create a PVRecord and - how to deploy it either as a standalone process or as part of a V3IOC.
-
exampleServer
-
This example has a set of PVRecords. - Again the records can be deployed either as a standalone process or - as part of a V3IOC.
-
examplePVADoubleArrayGet
-
This is an example PVRecord that uses channelGet to get an array of doubles from - another PVRecord via pvAccess. The example shows how to get the value either via - remote pvAccess or by directly accessing the local channelProvider. - Again the records can be deployed either as a standalone process or - as part of a V3IOC.
-
-

database provides base classes that make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resources used by the implementation.
-
-

Getting started

-

Included with this project are three examples that are useful for -seeing how pvDatabase can be used by clients. -Each can be deployed either as a standalone process or as part of a V3IOC. -The examples are: -

-
exampleCounterMain
-
This has a database consisting of a single record named exampleCounter: - The exampleCounter is discussed in a following section.
-
exampleCounter
-
This is exampleCounter as part of a V3IOC.
-
exampleServerMain
-
This has a database with several records.
-
exampleServer
-
This is exampleServer as part of a V3IOC
-
examplePVADoubleArrayGetMain
-
This is the example of how to use pvAccess to get data.
-
examplePVADoubleArrayGet
-
This is examplePVADoubleArrayGet as part of a V3IOC
- -

-

exampleCounter

-

To start exampleCounterMain: -

-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounterMain
-

-

To start exampleCounter as part of a V3IOC: -

-mrk> pwd
-/home/hg/pvDatabaseCPP/iocBoot/exampleCounter
-mrk> ../../../bin/linux-x86_64/exampleCounter st.cmd
-

-

You can then issue the commands dbl and pvdbl: -

-epics> dbl
-double01
-epics> pvdbl
-exampleCounter
-epics> 
-
-double01 is a v3Record. -exampleCounter is a pvRecord. -

-

Starting exampleServer is similar. -After successfully running exampleCounterMain and exampleCounter then -try starting exampleServerMain and exampleServer. -

-

exampleServer

-

This example provides the exampleCounter record in addition to a number of other PVRecords. -Like exampleCounter it can be started either as a standalone main program or as -a part of a V3IOC. -The exampleServer pvDatabase has many records including the following:

-
-
exampleCounter
-
A record that is an instance of exampleCounter described below. - The most useful channel methods are channelGet, channelProcess, - and monitor.
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
examplePowerSupply
-
Can be used by channelGet, channelPut, channelPutGet, and monitor.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

swtshell

-

The Java program - - swtshell -can be used to access pvDatabase.

-

In particular read the sections "Getting Started" and "Simple Example". -They will work on the exampleServer with the following differences: -

-
startExample.zip
-
Do NOT use this. Instead run the exampleServer as described above.
-
channelList result
-
The result of channelList will show the list of records that - exampleServer has rather than the records from startExample.zip
-
-

- -

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP implements the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Example PVRecord Extension

-

The example implements a simple counter. -The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> bin/linux-x86_64/exampleCounter 
-
-
-

The example consists of four components:

-
-
ExampleCounter.h
-
The source code for the counter. - It is located in directory pvDatabaseCPP/example/src/exampleCounter. -
-
exampleCounterMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. - It is located in directory pvDatabaseCPP/example/exampleCounter. -
-
V3IOC/exampleCounter
-
This is a directory that packages exampleCounter to make it - part of a V3IOC.
-
iocBoot/exampleCounter
-
A place to start exampleCounter as part of a V3IOC. - It follows the normal iocCore conventions.
-
-

ExampleCounter.h

-

The example resides in src/database. -The complete implementation is in the header file. -A serious implementation might break the code into a header and an -implementation file.

-

The description consists of

-
-class ExampleCounter;
-typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;
-
-class ExampleCounter :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleCounter);
-    static ExampleCounterPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleCounter();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleCounter(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    epics::pvData::PVLongPtr pvValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleCounter
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleCounter
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleCounterPtr ExampleCounter::create(
-    epics::pvData::String const & recordName)
-{
-    epics::pvData::PVStructurePtr pvStructure =
-       epics::pvData::getStandardPVField()->scalar(
-           epics::pvData::pvDouble,"timeStamp,alarm"");
-    ExampleCounterPtr pvRecord(
-        new ExampleCounter(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleCounterPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleCounter::ExampleCounter(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleCounter::~ExampleCounter()
-{
-}
-
-void ExampleCounter::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleCounter::init()
-{
-
-    initPVRecord();
-    epics::pvData::PVFieldPtr pvField;
-    pvValue = getPVStructure()->getLongField("value");
-    if(pvValue==NULL) return false;
-    return true;
-}
-
-This: -
    -
  • Calls initRecord which is implemented by the base class. - It MUST be called.
  • -
  • Calls getLongField to get the interface to the value field, - which must be a scalar with type long.
  • -
  • If a long value field was not found it returns false.
  • -
  • Returns true
  • -
-

The implementation of process is:

-
-void ExampleCounter::process()
-{
-    pvValue->put(pvValue->get() + 1.0);
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It adds 1.0 to the current value. -It then sets the timeStamp to the current time. -

exampleCounterMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleCounter");
-    PVRecordPtr pvRecord = ExampleCounter::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    cout << "exampleCounter\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleCounter record with the name exampleCounter -
  • -
  • Prints exampleCounter on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleCounter

-

This has two subdirectories: -

-
Db
-
This has a template for a single v3Record.
-
src
-
This has code to allow exampleCounter to reside in a V3IOC.
-
-

-

The src directory has the following components:

-
-
exampleCounterMain.cpp
-
This is just a standard Main for a V3IOC.
-
exampleCounterInclude.dbd
-
This is: -
-include "base.dbd"
-include "PVAServerRegister.dbd"
-registrar("exampleCounterRegister")
-       
- This includes the dbd components required from base, - the dbd file for starting pvAccess and the local channelProvider, - and the dbd file for the example. The later is for the code from the - next file. -
-
exampleCounter.cpp
-
This is the code that registers a command the can be issued - via the iocsh, which is the console for a V3IOC. - The example supports a single command: -
-exampleCounterCreateRecord recordName
-       
-
-
-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the C++ implementation of package pvAccess in pvIOCJava. - The localChannelProvider accesses data from PVRecords. - It implements all channel methods except channelRPC, which is implemented by pvAccessCPP.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for starting to implement services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

iocshell commands

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider

-
stopPVAServer
-
Stop the local channel provider
-
pvdbl
-
Provides a list of all the pvRecords in database master - It is similar to the iocsh command dbl
-
-

The client commands are provided via PVAClientRegister.dbd and the other commands -via PVAServerRegister.dbd.

-

In addition any code that implements a PVRecord must implement an ioc command. -The directory example has examples of how to implement the registration code. -See example/V3IOC/exampleCounter/src/ for a simple example.

- - -

database

-

src/database

-

This Directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
-

src/special

-

This directory has the following files:

-
-
powerSupplyRecordTest.h
-
- This provides code that simulates a power supply. - It computes the current from the voltage and power. - It is used for testing. - The complete implementation is provided in the header file. - Thus code will be generated only if other code includes the - header file and creates a record instance. -
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The testExampleServer creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-      
-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

pvDatabase.h

-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
process
-
Virtual method. - Derived classes must implement this method. - The base implementation does nothing. -
-
destroy
-
This is a virtual method. - A derived class must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it';s own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is access by the server side of remote pvAccess.

-

channelProviderLocal

-

This is a complete implementation of channelProvider and , -except for channelRPC, provides a complete implementation of Channel -as defined by pvAccess. -For monitors it calls the code described in the following sections.

-

pvCopy

-

This provides code that creates a top level PVStructure that is an arbitrary -subset of the fields in the PVStructure from a PVRecord. -In addition it provides code that monitors changes to the fields in a PVRecord. -A client configures the desired set of subfields and monitoring options -via a pvRequest structure. -pvAccess provides a class CreatePVRequest that creates a pvRequest. -The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava. -

-

monitorAlgorithm

-

Currently all that is implemented is a header file. -The only algorithm currently implemented is onPut -

-

monitorFactory

-

Overview

-

epics::pvData::monitor defines the monitor interfaces -as seen by a client. -See - pvDatabaseCPP.html - For details.

-

-monitorFactory implements the -monitoring interfaces for a PVRecord. -It implements queueSize=0 and queueSize>=2. -

- -

-The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy. -When PVCopyMonitor tells monitor that changes -have occurred, monitor applies the appropriate algorithm to each changed field.

- -

Currently only algorithm onPut is implemented but, -like pvIOCJava there are plans to support for the following monitor algorithms:

-
-
onPut
-
A monitor is issued whenever a put is issued to the field. This is the - default unless the record defines deadbands for a field. An exception is - the top level timeStamp which by default is made onChange and monitor - will not be raised.
-
onChange
-
This provides two options: 1) A monitor is raised whenever a field - changes value, and 2) A monitor will never be raised for the field.
-
deadband
-
The field must be a numeric scalar. Whenever the absolute or percentage - value of the field changes by more than a deadband a monitor is issued. - The record instance can also define deadbands.
-
periodic
-
A monitor is issued at a periodic rate if a put was issued to any field - being monitored.
-
-

MonitorFactory

-

MonitorFactory provides the following methods:

-
class MonitorFactory
-{
-    static MonitorPtr create(
-        PVRecordPtr const & pvRecord,
-        MonitorRequester::shared_pointer const & monitorRequester,
-        PVStructurePtr const & pvRequest);
-    static void registerMonitorAlgorithmCreater(
-        MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
-        String const & algorithmName);
-}
- -

where

-
-
create
-
Create a monitor. The arguments are: -
-
pvRecord
-
The record being monitored.
-
monitorRequester
-
The monitor requester. This is the code to which monitot events - will be delivered.
-
pvRequest
-
The request options
-
-
-
registerMonitorAlgorithmCreater
-
Called by code that implements a monitor algorithm.
-
-

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generted - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

-

recordList

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string database master
-        string regularExpression .*
-    structure result
-        string status 
-        string[] names
-
-where: -
-
database
-
The name of the datbase. The default is "master"
-
regularExpression
-
For now this is ignored and the complete list of names is always - returned.
-
status
-
The status of a putGet request.
-
names
-
The list of record names.
-
-

Note that swtshell has a command channelList that -requires that a record of this type is present and calls it. -Thus user code does not have to use a channelGetPut to get the list -of record names.

-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

powerSupplyRecordTest

-

This simulates a simple power supply record. -It is used for testing.

-

Accessing Other PVRecords

-

This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - caused deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all lockimg issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transfered between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-ChannelProvider::shared_pointer provider = channelAccess->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
-

examplePVADoubleArrayGet

-examplePVADoubleArrayGet shows how to use pvAccess to get data.

-

The code resides in three directories:

-
-
example/src/examplePVADoubleArrayGet
-
This is the implementation code for the example.
-
example/examplePVADoubleArrayGet
-
This is the code that starts the example as a main program.
-
example/V3IOC/examplePVADoubleArrayGet/src
-
This is the code for starting the example as part of a V3IOC.
-
iocBoot/examplePVADoubleArrayGet
-
This has the st.cmd files to start the example.
-
-

examplePVADoubleArrayGet Implementation

-

examplePVADoubleArrayGet.h contains the following:

-
-...
-class ExamplePVADoubleArrayGet :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExamplePVADoubleArrayGet);
-    static ExamplePVADoubleArrayGetPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::String const & providerName,
-        epics::pvData::String const & channelName
-        );
-    virtual ~ExamplePVADoubleArrayGet() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExamplePVADoubleArrayGetPtr ExamplePVADoubleArrayGet::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExamplePVADoubleArrayGetPtr pvRecord(
-        new ExamplePVADoubleArrayGet(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExamplePVADoubleArrayGet instance, -and then calls the init method and the returns a ExamplePVADoubleArrayGetPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExamplePVADoubleArrayGet::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

Starting the example as a main program

-
-...
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ClientFactory::start();
-    ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
-    PVRecordPtr pvRecord;
-    bool result(false);
-    String recordName;
-    PVStructurePtr pvStructure = standardPVField->scalarArray(
-        pvDouble,"alarm,timeStamp");
-    recordName = "doubleArray";
-    pvRecord = PVRecord::create(recordName,pvStructure);
-    result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    recordName = "examplePVADoubleArrayGet";
-    if(argc>1) recordName = argv[1];
-    String providerName("local");
-    if(argc>2) providerName = argv[2];
-    String channelName("doubleArray");
-    if(argc>3) channelName = argv[3];
-    pvRecord = ExamplePVADoubleArrayGet::create(
-        recordName,providerName,channelName);
-    if(pvRecord!=NULL) {
-        result = master->addRecord(pvRecord);
-        cout << "result of addRecord " << recordName << " " << result << endl;
-    } else {
-        cout << "ExamplePVADoubleArrayGet::create failed" << endl;
-    }
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    serverContext->shutdown();
-    epicsThreadSleep(1.0);
-    serverContext->destroy();
-    ClientFactory::stop();
-    channelProvider->destroy();
-    return 0;
-}
-
-

The first statements initializes the client factory and localChannelProvider.

-

-It then creates a PVRecord that has a double array as the value field. -The name of the record is doubleArray. -This is the record that can be accessed via pvAccess if the channelName is -doubleArray. -

-

It then creates a examplePVADoubleArrayGet instance.

-

It the runs forever until the exit is typed.

- -

Start the example as part of a V3IOC

-The directory example/V3IOC/examplePVADoubleArrayGet has two subdirectories: Db and src. -These are like for any other V3IOC application. -The Db directory is for a regular ai record. -The src directory has code similar to any V3IOC application. -In particular it has definitions and code for creating the following iocsh commands:

-
-examplePVADoubleArrayGetRegister recordName providerName channelName
-
-where -
-
recordName
-
providerName
-
channelName
-
-

The directory iocBoot/examplePVADoubleArrayGet has two st.cmd files:

-
-
st.local
-
This creates the example using localChannelProvider.
-
st.remote
-
This creates the example using the remote channel provider.
-
- -

Array Performance and Memory Example

-

This section describes main programs that demonstrate performance -of large arrays and can also be used to check for memory leaks. -Checking for memory leaks can be accomplished by running the programs with valgrind -or some other memory check program. -

-

Brief Summary

-

The programs are:

-
-
arrayPerformanceMain
-
This is server and also a configurable number of longArrayMonitor clients. - The clients can use either the local or - remote providers. The moitor code is the same code that is used by longArrayMonitorMain. -
-
longArrayMonitorMain
-
Remote client that monitors the array served by arrayPerformanceMain.
-
longArrayGetMain
-
Remote client that uses channelGet to access the array served by arrayPerformanceMain.
-
longArrayPutMain
-
Remote client that uses channelPut to access the array served by arrayPerformanceMain.
-
-

Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
-default
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0.0
-
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName queueSize waitTime
-default
-longArrayMonitorMain arrayPerformance 2 0.0
-
-mrk> bin/linux-x86_64/longArrayGetMain -help
-longArrayGetMain channelName iterBetweenCreateChannel iterBetweenCreateChannelGet delayTime
-default
-longArrayGetMain arrayPerformance 0 0 1
-
-mrk> bin/linux-x86_64/longArrayPutMain -help
-longArrayPutMain channelName arraySize iterBetweenCreateChannel iterBetweenCreateChannelPut delayTime
-default
-longArrayPutMain arrayPerformance 10 0 0 1
-
-mrk> 
-
-

Example output

-

Note: These may fail if run on a platform that does not have sufficent memory,

-

To see an example just execute the following commands in four different terminal windows:

-
-bin/linux/<arch>/arrayPerformanceMain
-bin/linux/<arch>/longArrayMonitorMain
-bin/linux/<arch>/longArrayGetMain
-bin/linux/<arch>/longArrayPutMain
-
-

Each program generates a report every second when it has somthing to report. -Examples are: -

-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0
-...
- monitors/sec 66 first 131 last 131 changed {1, 2} overrun {} megaElements/sec 656.999
-arrayPerformance value 132 time 1.00486 Iterations/sec 65.681 megaElements/sec 656.81
- monitors/sec 66 first 197 last 197 changed {1, 2} overrun {} megaElements/sec 656.304
-arrayPerformance value 198 time 1.00563 Iterations/sec 65.6307 megaElements/sec 656.307
- monitors/sec 66 first 263 last 263 changed {1, 2} overrun {} megaElements/sec 654.824
-...
-
-
-mrk> bin/linux-x86_64/longArrayMonitorMain
-longArrayMonitorMain arrayPerformance 2 0
-...
- monitors/sec 6 first 2357 last 2357 changed {1, 2} overrun {} megaElements/sec 68.6406
- monitors/sec 13 first 2385 last 2385 changed {1, 2} overrun {} megaElements/sec 118.72
- monitors/sec 9 first 2418 last 2418 changed {1, 2} overrun {1, 2} megaElements/sec 85.0984
-...
-
-
-mrk> bin/linux-x86_64/longArrayPutMain
-longArrayPutMain arrayPerformance 10 0 0 1
-...
-put numChannelPut 0 time 1.00148 Elements/sec 79.8819
-put numChannelPut 1 time 1.00176 Elements/sec 79.8598
-...
-
-
-mrk> bin/linux-x86_64/longArrayGetMain
-longArrayGetMain arrayPerformance 0 0 1
-...
-get kiloElements/sec 7384.61
-get kiloElements/sec 8726.34
-...
-
- -

arrayPerformance

-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
queueSize
-
The queueSize for the element queue. - A value less than 1 will become 1. -
-
waitTime
-
The time that longArrayMonitor will sleep after poll returns a monitorElement.
-
-

-arrayPerformance creates a PVRecord that has the structure:. -

-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers.

-

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
Once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the interation count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report every second showing how many elements has received. -For every monitor it also checks that the number of alements is >0 and the -the first element equals the last element. It reports an error if either -of these conditions is not true.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
queueSize
-
The queueSize. Note that any size <2 is made 2.
-
waitTime
-
The time to wait after a poll request returns a monitorElement. - This can be used to force an overrun of the client even if there is no - overrun on the server.
-
-

longArrayGet

-

This is a pvAccess client that uses channelGet to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayGetMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelGet
-
The number of iterations between destroying and recreating the channelGet. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to dalay between gets.
-
-

longArrayPut

-

This is a pvAccess client that uses channelPut to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayPutMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
arraySize
-
The capacity and length of the array to put to the server.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelPut
-
The number of iterations between destroying and recreating the channelPut. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to dalay between gets.
-
- -

Some results

-

array performance

-

The results were from my laptop. -It has a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system is linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 10 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 50 iterations per second -and is putting about 500million elements per second. -Since each element is an int64 this means about 4gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-

-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 25 interations per second and 250 million elements per second. -The next section has an example that demonstrates what happens. -Note that if the array size is small enough to fit in the local cache then running longArrayMonitor -has almost no effect of arrayPerforance. -

-

memory leaks

-

Running longArrayMonitorMain, longArrayPutMain, and longArrayGetMain -under valgrind shows no memory leaks.

-

arrayPerformanceMain shows the following:

-
-==9125== LEAK SUMMARY:
-==9125==    definitely lost: 0 bytes in 0 blocks
-==9125==    indirectly lost: 0 bytes in 0 blocks
-==9125==      possibly lost: 576 bytes in 2 blocks
-
-

The possibly leaked is either 1 or 2 blocks. -It seems to be the same if clients are connected. -

-

Vector Performance

-

This example demonstrates how array size effects performance. -The example is run as:

-
-bin/linux-x86_64/vectorPerformanceMain -help
-vectorPerformanceMain size delay nThread
-default
-vectorPerformance 50000000 0.01 1 
-
-

Consider the following:

-
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
-...
-thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
-thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
-thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
-...
-thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
-thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
-thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
-thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
-thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
-thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
-thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
-thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
-thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
-thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
-thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
-thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
-thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
-thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
-thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
-thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
-thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
-thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
-
-

As more threads are running the slower each thread runs.

-

But now consider a size that fits in a local cache.

-

-bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
-...
-thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
-thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
-thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
-... 
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
-...
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-...
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
-...
-thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
-thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
-thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
-thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
-thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
-thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
-
-

Now the number of threads has a far smaller effect on the performance of each thread. -

- -
- - diff --git a/documentation/pvDatabaseCPP_20140207.html b/documentation/pvDatabaseCPP_20140207.html deleted file mode 100644 index 9b20656..0000000 --- a/documentation/pvDatabaseCPP_20140207.html +++ /dev/null @@ -1,2040 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 07-Feb-2014

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20140207.html -
-
Previous version:
-
pvDatabaseCPP20131121.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 07-Feb-2014 version of of pvDatabaseCPP.

-

- -

-Since the last version of the documentation: -

-
examples
-
The examples have been moved to separate top level build areas.
-
test
-
The regression tests have been moved to a separate top level build area. - It is built from the top but nothing from the tests appears in the top - level bin directory. -
exampleServer
-
This example now also includes pvaSrv, i. e. the pvAccess server for - interfacing to iocCore V3 records. -
-
-

-This project is ready for alpha users. -

- -

-I have not had time to look at -two unresolved problems reported in the previous version of this document: -

-
memory leak
-
arrayPerformanceMain shows a slight memory leak at termination.
-
channel destroy and recreate
-
longArrayGet and longArrayPut fail if the channel is destroyed and - immediately recreated. - If epicsThreadSleep(1.0) is called between destroy and recreate then they work. - The current version of each does wait. -
-
-

-

Future enhancements in priority order:

-
-
channelArray
-
The arguments that have type int should be changed to size_t. - This requires changes to pvAccessCPP. -
-
Monitor Algorithms
-
Monitor algorithms have not been implemented. - Thus all monitors are onPut.
-
Create more regression tests
-
Currently only some simple tests exist. - Most of the testing has been via the examples.
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

The main purpose of this project to make it easier to implement services that are accessed via pvAccess. -This project supplies is a complete implementation of the server side of pvAccess. -All that a service has to provide is a top level PVStructure and a process method. -A service can be run as a main process or can be part of a V3 IOC. -Thus services can be developed that interact with V3 records, asynDriver, -areaDetector, etc. -

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also provides a C++ implementation - of the monitor and pvCopy components from pvIOCJava
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In the later case the IOC has both a database of V3 Records - and a pvDatabase.
-
-

Base classes make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resources used by the implementation.
-
-

Getting started

-

The first step is to build pvDatabaseCPP as described in the next section.

-

One of the examples is exampleServer. -It can be started either via a main program or as part of a V3 IOC. -

-

To start it as a main program do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> bin/linux-x86_64/exampleServerMain 
-
-

You should see something like the following:

-
-result of addRecord exampleServer 1
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-exampleServer
-Type exit to stop: 
-
-

Then in another window execute a pvput and pvget as follows:

-
-mrk> pvput -r "field(argument.value)" exampleServer World
-...
-mrk> pvget -r "record[process=true]field(result.value)" exampleServer
-exampleServer
-structure 
-    string value Hello World
-mrk> 
-
-

To run the example as part of a V3 IOC do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../bin/linux-x86_64/exampleServer st.cmd 
-
-

You will see the following:

-
-> envPaths
-epicsEnvSet("ARCH","linux-x86_64")
-epicsEnvSet("IOC","exampleServer")
-epicsEnvSet("TOP","/home/hg/pvDatabaseCPP/exampleServer")
-epicsEnvSet("EPICS_BASE","/home/install/epics/base")
-epicsEnvSet("EPICSV4HOME","/home/hg")
-cd /home/hg/pvDatabaseCPP/exampleServer
-## Register all support components
-dbLoadDatabase("dbd/exampleServer.dbd")
-exampleServer_registerRecordDeviceDriver(pdbbase)
-## Load record instances
-dbLoadRecords("db/dbScalar.db","name=pvdouble,type=ao")
-dbLoadRecords("db/dbArray.db","name=pvdoubleArray,type=DOUBLE")
-dbLoadRecords("db/dbStringArray.db","name=pvstringArray")
-dbLoadRecords("db/dbEnum.db","name=pvenum")
-dbLoadRecords("db/dbCounter.db","name=pvcounter");
-cd /home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-iocInit()
-Starting iocInit
-############################################################################
-## EPICS R3.14.12.3 $Date: Mon 2012-12-17 14:11:47 -0600$
-## EPICS Base built Dec 21 2013
-############################################################################
-iocRun: All initialization complete
-dbl
-pvdouble
-pvcounter
-pvenum
-pvdoubleArray
-pvstringArray
-epicsThreadSleep(1.0)
-exampleServerCreateRecord pvaServer
-startPVAServer
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : dbPv local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-pvdbl
-pvaServer
-epics> 
-
-

Just like previously you can then execute a pvput and pvget and see Hello World. -

-

The examples, i. e. exampleServer, exampleLink, examplePowerSupply, -and exampleDatabase, are described in separate sections below. -In addition arrayPerformance can be used to measure that performance of big -arrays. It is also described in a later section.

-

Reading section exampleServer and looking at it's code is a good way -to learn how to implement a service.

- -

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP implements the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the C++ implementation of package pvAccess in pvIOCJava. - The localChannelProvider accesses data from PVRecords. - It implements all channel methods except channelRPC, which is implemented by pvAccessCPP.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for implementing many services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

Building pvDatabaseCPP

-

To build pvDatabaseCPP You must provide a file RELEASE.local -in directory configure. -Thus do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product pvDatabaseCPP requires. -Than at the top level just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> make
-
-

This builds pvDatabaseCPP and also the tests.

-

Each example and arrayPerformance is build as a completely separate top. -This is done so that:

-

    -
  1. - It is easier to understand each example including how it is built so that - it can be run as a main or as part of a V3 IOC. -
  2. -
  3. - Each example can be copied somewhere else and used as the basis - for creating a new service. -
  4. -
-

Thus to build each example just follow the same instructions as for -building pvDatabaseCPP itself. -For example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product the example requires. -Than at the top level just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> make
-
-

This builds the example.

- -

iocshell commands

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider

-
stopPVAServer
-
Stop the local channel provider
-
pvdbl
-
Provides a list of all the pvRecords in database master -
-
-

The client commands are provided via PVAClientRegister.dbd and the other commands -via PVAServerRegister.dbd.

-

In addition any code that implements a PVRecord must implement an ioc command. -The directory example has examples of how to implement the registration code. -See example/V3IOC/exampleCounter/src/ for a simple example.

- - -

database

-

src/database

-

This Directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
-

src/special

-

This directory has the following files:

-
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The exampleDatabase creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-      
-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

pvDatabase.h

-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
process
-
Virtual method. - Derived classes must implement this method. - The base implementation does nothing. -
-
destroy
-
This is a virtual method. - A derived class must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it';s own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is access by the server side of remote pvAccess.

-

channelProviderLocal

-

This is a complete implementation of channelProvider and , -except for channelRPC, provides a complete implementation of Channel -as defined by pvAccess. -For monitors it calls the code described in the following sections.

-

pvCopy

-

This provides code that creates a top level PVStructure that is an arbitrary -subset of the fields in the PVStructure from a PVRecord. -In addition it provides code that monitors changes to the fields in a PVRecord. -A client configures the desired set of subfields and monitoring options -via a pvRequest structure. -pvAccess provides a class CreatePVRequest that creates a pvRequest. -The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava. -

-

monitorAlgorithm

-

Currently all that is implemented is a header file. -The only algorithm currently implemented is onPut -

-

monitorFactory

-

Overview

-

epics::pvData::monitor defines the monitor interfaces -as seen by a client. -See - pvDatabaseCPP.html - For details.

-

-monitorFactory implements the -monitoring interfaces for a PVRecord. -It implements queueSize=0 and queueSize>=2. -

- -

-The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy. -When PVCopyMonitor tells monitor that changes -have occurred, monitor applies the appropriate algorithm to each changed field.

- -

Currently only algorithm onPut is implemented but, -like pvIOCJava there are plans to support for the following monitor algorithms:

-
-
onPut
-
A monitor is issued whenever a put is issued to the field. This is the - default unless the record defines deadbands for a field. An exception is - the top level timeStamp which by default is made onChange and monitor - will not be raised.
-
onChange
-
This provides two options: 1) A monitor is raised whenever a field - changes value, and 2) A monitor will never be raised for the field.
-
deadband
-
The field must be a numeric scalar. Whenever the absolute or percentage - value of the field changes by more than a deadband a monitor is issued. - The record instance can also define deadbands.
-
periodic
-
A monitor is issued at a periodic rate if a put was issued to any field - being monitored.
-
-

MonitorFactory

-

MonitorFactory provides the following methods:

-
class MonitorFactory
-{
-    static MonitorPtr create(
-        PVRecordPtr const & pvRecord,
-        MonitorRequester::shared_pointer const & monitorRequester,
-        PVStructurePtr const & pvRequest);
-    static void registerMonitorAlgorithmCreater(
-        MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
-        String const & algorithmName);
-}
- -

where

-
-
create
-
Create a monitor. The arguments are: -
-
pvRecord
-
The record being monitored.
-
monitorRequester
-
The monitor requester. This is the code to which monitot events - will be delivered.
-
pvRequest
-
The request options
-
-
-
registerMonitorAlgorithmCreater
-
Called by code that implements a monitor algorithm.
-
-

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generted - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

-

recordList

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string database master
-        string regularExpression .*
-    structure result
-        string status 
-        string[] names
-
-where: -
-
database
-
The name of the datbase. The default is "master"
-
regularExpression
-
For now this is ignored and the complete list of names is always - returned.
-
status
-
The status of a putGet request.
-
names
-
The list of record names.
-
-

Note that swtshell, which is a Java GUI tool, has a command channelList that -requires that a record of this type is present and calls it. -Thus user code does not have to use a channelGetPut to get the list -of record names.

-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

- -

exampleServer

-

Overview

-

The example implements a simple service that has a top level pvStructure: -

-structure
-    structure argument
-        string value
-    structure result
-        string value
-        time_t timeStamp
-            long secondsPastEpoch
-            int nanoSeconds
-            int userTag
-
-

It is designed to be accessed via a channelPutGet request. -The client sets argument.value -When the record processes it sets result.value to "Hello " -concatenated with argument.value. -Thus if the client sets argument.value equal to "World" -result.value will be "Hello World". -In addition the timeStamp is set to the time when process is called.

-

-The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleService
-mrk> bin/linux-x86_64/exampleService
-
-

Directory Layout

-

-The directory layout is: -

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerInclude.dbd
-       exampleServerMain.cpp
-       exampleServerRegister.cpp
-    ioc
-       Db
-          ...
-       src
-          exampleServerInclude.dbd
-          exampleServerMain.cpp
-   iocBoot
-      exampleServer
-         st.cmd
-         ...
-
-where -
-
ExampleRELEASE.local
-
This is the file that must be copied to RELEASE.local - and edited.
-
exampleServer.h
-
The header file for the service.
-
exampleServer.cpp
-
The service implementation.
-
exampleServerMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. -
-
exampleServerInclude.dbd
-
This has a register command so that the service can be started - on a V3 IOC via iocsh. -
-
exampleServerRegister.cpp
-
This has the code to start the service via the following iocsh - command. -
-exampleServerCreateRecord exampleServer
-
- Multiple commands can be issued to create multiple service records. -
-
ioc
-
This is for building a V3 IOC application.
-
ioc/Db
-
This has template files for creating V3 records.
-
ioc/src
-
The files for running a V3 IOC.
-
iocBoot/exampleServer
-
A place to start exampleServer as part of a V3IOC. - It has a st.cmd file that starts the ioc and also starts pvAccess - and the example.
-
-

If only a main program is desired then the directory layout is:

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerMain.cpp
-
-

Thus if only a main program is required the directory layout is simple.

-

Also many sites will want to build the src directory in an area -separate from where the iocs are build.

-

exampleServer.h

-

The example resides in src -The implementation is in exampleServer.cpp. -

-

The description consists of

-
-class ExampleServer;
-typedef std::tr1::shared_ptr<ExampleServer> ExampleServerPtr;
-
-class ExampleServer :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleServer);
-    static ExampleServerPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleServer();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleServer(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-
-    epics::pvData::PVStringPtr pvArgumentValue;
-    epics::pvData::PVStringPtr pvResultValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleServer
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleServer
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleServerPtr ExampleServer::create(
-    epics::pvData::String const & recordName)
-{
-    StandardPVFieldPtr standardPVField = getStandardPVField();
-    PVDataCreatePtr pvDataCreate = getPVDataCreate();
-    PVStructurePtr pvArgument = standardPVField->scalar(pvString,"");
-    PVStructurePtr pvResult = standardPVField->scalar(pvString,"timeStamp");
-    StringArray names;
-    names.reserve(2);
-    PVFieldPtrArray fields;
-    fields.reserve(2);
-    names.push_back("argument");
-    fields.push_back(pvArgument);
-    names.push_back("result");
-    fields.push_back(pvResult);
-    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(names,fields);
-    ExampleServerPtr pvRecord(
-        new ExampleServer(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleServerPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleServer::ExampleServer(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleServer::~ExampleServer()
-{
-}
-
-void ExampleServer::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleServer::init()
-{
-    initPVRecord();
-    PVFieldPtr pvField;
-    pvArgumentValue = getPVStructure()->getStringField("argument.value");
-    if(pvArgumentValue.get()==NULL) return false;
-    pvResultValue = getPVStructure()->getStringField("result.value");
-    if(pvResultValue.get()==NULL) return false;
-    pvTimeStamp.attach(getPVStructure()->getSubField("result.timeStamp"));
-    return true;
-}
-
-

The implementation of process is:

-
-void ExampleServer::process()
-{
-    pvResultValue->put(String("Hello ") + pvArgumentValue->get());
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It gives a value to result.value and -then sets the timeStamp to the current time. -

src/exampleServerMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleServer");
-    PVRecordPtr pvRecord = ExampleServer::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    cout << "exampleServer\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleServer record with the name exampleServer -
  • -
  • Prints exampleServer on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleServer

-

To start exampleServer as part of a V3IOC: -

-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../../bin/linux-x86_64/exampleServer st.cmd
-

-

You can then issue the commands dbl and pvdbl: -

-epics> dbl
-double01
-epics> pvdbl
-exampleServer
-epics> 
-
-double01 is a v3Record. -exampleServer is a pvRecord. -

-

-It starts pvASrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

- -

exampleDatabase

-

The exampleServer pvDatabase has many records including the following:

-
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

exampleDatabase can be started as a main program or as a V3 IOIC. -If started as a V3 IOC it also has a number of V3 records, -and starts pvaSrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

-

exampleLink

-

This example show how a service can access other PVRecords. -This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - cause deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all locking issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transfered between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-ChannelProvider::shared_pointer provider = channelAccess->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
- -

Directory Layout

-
-exampleLink
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleLink.h
-       exampleLink.cpp
-       exampleLinkInclude.dbd
-       exampleLinkRegister.cpp
-    ioc
-       Db
-       src
-          exampleLinkInclude.dbd
-          exampleLinkMain.cpp
-   iocBoot
-      exampleLink
-         st.local
-         st.remote
-         ...
-
-

This example is only built to be run as part of a V3 IOC. -Note that two startup files are available: st.local and st.remote. -st.local has two records: doubleArray and exampleLink. -doubleArray is a record that can be changed via a call to pvput. -exampleLink is a record that, when processed, gets the value from doubleArray and sets its value equal -to the value read. -st.local has both records. -st.remote has only one record named exampleLinkRemote. -

-

To start the example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleLink/iocBoot/exampleLink
-mrk> ../../bin/linux-x86_64/exampleLink st.local 
-
-

then in another window:

-
-mrk> pvput doubleArray 4 100 200 300 400
-Old : doubleArray 0
-New : doubleArray 4 100 200 300 400
-mrk> pvget -r "record[process=true]field(value)" exampleLink
-exampleLink
-structure 
-    double[] value [100,200,300,400]
-mrk> 
-
-

exampleLink Implementation

-

exampleLink.h contains the following:

-
-...
-class ExampleLink :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExampleLink);
-    static ExampleLinkPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::String const & providerName,
-        epics::pvData::String const & channelName
-        );
-    virtual ~ExampleLink() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExampleLinkPtr ExampleLink::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExampleLinkPtr pvRecord(
-        new ExampleLink(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExampleLink instance, -and then calls the init method and the returns a ExampleLinkPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExampleLink::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

examplePowerSupply

-

This is an example of creating a service that requires a somewhat complicated -top level PVStructure. -It is similar to the powerSupply example that is provided with pvIOCJava. -Look at the code for details. -

-

Array Performance and Memory Example

-

This section describes main programs that demonstrate performance -of large arrays and can also be used to check for memory leaks. -Checking for memory leaks can be accomplished by running the programs with valgrind -or some other memory check program. -

-

Brief Summary

-

The programs are:

-
-
arrayPerformanceMain
-
This is server and also a configurable number of longArrayMonitor clients. - The clients can use either the local or - remote providers. The moitor code is the same code that is used by longArrayMonitorMain. -
-
longArrayMonitorMain
-
Remote client that monitors the array served by arrayPerformanceMain.
-
longArrayGetMain
-
Remote client that uses channelGet to access the array served by arrayPerformanceMain.
-
longArrayPutMain
-
Remote client that uses channelPut to access the array served by arrayPerformanceMain.
-
-

Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
-default
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0.0
-
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName queueSize waitTime
-default
-longArrayMonitorMain arrayPerformance 2 0.0
-
-mrk> bin/linux-x86_64/longArrayGetMain -help
-longArrayGetMain channelName iterBetweenCreateChannel iterBetweenCreateChannelGet delayTime
-default
-longArrayGetMain arrayPerformance 0 0 1
-
-mrk> bin/linux-x86_64/longArrayPutMain -help
-longArrayPutMain channelName arraySize iterBetweenCreateChannel iterBetweenCreateChannelPut delayTime
-default
-longArrayPutMain arrayPerformance 10 0 0 1
-
-mrk> 
-
-

Example output

-

Note: These may fail if run on a platform that does not have sufficent memory,

-

To see an example just execute the following commands in four different terminal windows:

-
-bin/linux/<arch>/arrayPerformanceMain
-bin/linux/<arch>/longArrayMonitorMain
-bin/linux/<arch>/longArrayGetMain
-bin/linux/<arch>/longArrayPutMain
-
-

Each program generates a report every second when it has somthing to report. -Examples are: -

-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0
-...
- monitors/sec 66 first 131 last 131 changed {1, 2} overrun {} megaElements/sec 656.999
-arrayPerformance value 132 time 1.00486 Iterations/sec 65.681 megaElements/sec 656.81
- monitors/sec 66 first 197 last 197 changed {1, 2} overrun {} megaElements/sec 656.304
-arrayPerformance value 198 time 1.00563 Iterations/sec 65.6307 megaElements/sec 656.307
- monitors/sec 66 first 263 last 263 changed {1, 2} overrun {} megaElements/sec 654.824
-...
-
-
-mrk> bin/linux-x86_64/longArrayMonitorMain
-longArrayMonitorMain arrayPerformance 2 0
-...
- monitors/sec 6 first 2357 last 2357 changed {1, 2} overrun {} megaElements/sec 68.6406
- monitors/sec 13 first 2385 last 2385 changed {1, 2} overrun {} megaElements/sec 118.72
- monitors/sec 9 first 2418 last 2418 changed {1, 2} overrun {1, 2} megaElements/sec 85.0984
-...
-
-
-mrk> bin/linux-x86_64/longArrayPutMain
-longArrayPutMain arrayPerformance 10 0 0 1
-...
-put numChannelPut 0 time 1.00148 Elements/sec 79.8819
-put numChannelPut 1 time 1.00176 Elements/sec 79.8598
-...
-
-
-mrk> bin/linux-x86_64/longArrayGetMain
-longArrayGetMain arrayPerformance 0 0 1
-...
-get kiloElements/sec 7384.61
-get kiloElements/sec 8726.34
-...
-
- -

arrayPerformance

-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
queueSize
-
The queueSize for the element queue. - A value less than 1 will become 1. -
-
waitTime
-
The time that longArrayMonitor will sleep after poll returns a monitorElement.
-
-

-arrayPerformance creates a PVRecord that has the structure:. -

-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers.

-

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
Once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the interation count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report every second showing how many elements has received. -For every monitor it also checks that the number of alements is >0 and the -the first element equals the last element. It reports an error if either -of these conditions is not true.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
queueSize
-
The queueSize. Note that any size <2 is made 2.
-
waitTime
-
The time to wait after a poll request returns a monitorElement. - This can be used to force an overrun of the client even if there is no - overrun on the server.
-
-

longArrayGet

-

This is a pvAccess client that uses channelGet to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayGetMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelGet
-
The number of iterations between destroying and recreating the channelGet. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to dalay between gets.
-
-

longArrayPut

-

This is a pvAccess client that uses channelPut to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayPutMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
arraySize
-
The capacity and length of the array to put to the server.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelPut
-
The number of iterations between destroying and recreating the channelPut. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to dalay between gets.
-
- -

Some results

-

array performance

-

The results were from my laptop. -It has a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system is linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 10 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 50 iterations per second -and is putting about 500million elements per second. -Since each element is an int64 this means about 4gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-

-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 25 interations per second and 250 million elements per second. -The next section has an example that demonstrates what happens. -Note that if the array size is small enough to fit in the local cache then running longArrayMonitor -has almost no effect of arrayPerforance. -

-

memory leaks

-

Running longArrayMonitorMain, longArrayPutMain, and longArrayGetMain -under valgrind shows no memory leaks.

-

arrayPerformanceMain shows the following:

-
-==9125== LEAK SUMMARY:
-==9125==    definitely lost: 0 bytes in 0 blocks
-==9125==    indirectly lost: 0 bytes in 0 blocks
-==9125==      possibly lost: 576 bytes in 2 blocks
-
-

The possibly leaked is either 1 or 2 blocks. -It seems to be the same if clients are connected. -

-

Vector Performance

-

This example demonstrates how array size effects performance. -The example is run as:

-
-bin/linux-x86_64/vectorPerformanceMain -help
-vectorPerformanceMain size delay nThread
-default
-vectorPerformance 50000000 0.01 1 
-
-

Consider the following:

-
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
-...
-thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
-thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
-thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
-...
-thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
-thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
-thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
-thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
-thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
-thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
-thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
-thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
-thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
-thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
-thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
-thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
-thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
-thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
-thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
-thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
-thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
-thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
-
-

As more threads are running the slower each thread runs.

-

But now consider a size that fits in a local cache.

-

-bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
-...
-thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
-thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
-thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
-... 
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
-...
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-...
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
-...
-thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
-thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
-thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
-thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
-thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
-thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
-
-

Now the number of threads has a far smaller effect on the performance of each thread. -

- -
- - diff --git a/documentation/pvDatabaseCPP_20140219.html b/documentation/pvDatabaseCPP_20140219.html deleted file mode 100644 index 62fd254..0000000 --- a/documentation/pvDatabaseCPP_20140219.html +++ /dev/null @@ -1,2043 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 19-Feb-2014

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20140219.html -
-
Previous version:
-
pvDatabaseCPP20140207.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
-
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 19-Feb-2014 version of of pvDatabaseCPP.

-

- -

-Since the last version of the documentation: -

-
examples
-
The examples have been moved to separate top level build areas.
-
test
-
The regression tests have been moved to a separate top level build area. - It is built from the top but nothing from the tests appears in the top - level bin directory. -
exampleServer
-
This example now also includes pvaSrv, i. e. the pvAccess server for - interfacing to iocCore V3 records. -
-
-

-This project is ready for alpha users. -

- -

-I have not had time to look at -two unresolved problems reported in the previous version of this document: -

-
memory leak
-
arrayPerformanceMain shows a slight memory leak at termination.
-
channel destroy and recreate
-
longArrayGet and longArrayPut fail if the channel is destroyed and - immediately recreated. - If epicsThreadSleep(1.0) is called between destroy and recreate then they work. - The current version of each does wait. -
-
-

-

Future enhancements in priority order:

-
-
channelArray
-
The arguments that have type int should be changed to size_t. - This requires changes to pvAccessCPP. -
-
Monitor Algorithms
-
Monitor algorithms have not been implemented. - Thus all monitors are onPut.
-
Create more regression tests
-
Currently only some simple tests exist. - Most of the testing has been via the examples.
-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

The main purpose of this project to make it easier to implement services that are accessed via pvAccess. -This project supplies is a complete implementation of the server side of pvAccess. -All that a service has to provide is a top level PVStructure and a process method. -A service can be run as a main process or can be part of a V3 IOC. -Thus services can be developed that interact with V3 records, asynDriver, -areaDetector, etc. -

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also provides a C++ implementation - of the monitor and pvCopy components from pvIOCJava
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In the later case the IOC has both a database of V3 Records - and a pvDatabase.
-
-

Base classes make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resources used by the implementation.
-
-

Getting started

-

The first step is to build pvDatabaseCPP as described in the next section.

-

One of the examples is exampleServer. -It can be started either via a main program or as part of a V3 IOC. -

-

To start it as a main program do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> bin/linux-x86_64/exampleServerMain 
-
-

You should see something like the following:

-
-result of addRecord exampleServer 1
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-exampleServer
-Type exit to stop: 
-
-

Then in another window execute a pvput and pvget as follows:

-
-mrk> pvput -r "field(argument.value)" exampleServer World
-...
-mrk> pvget -r "record[process=true]field(result.value)" exampleServer
-exampleServer
-structure 
-    string value Hello World
-mrk> 
-
-

To run the example as part of a V3 IOC do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../bin/linux-x86_64/exampleServer st.cmd 
-
-

You will see the following:

-
-> envPaths
-epicsEnvSet("ARCH","linux-x86_64")
-epicsEnvSet("IOC","exampleServer")
-epicsEnvSet("TOP","/home/hg/pvDatabaseCPP/exampleServer")
-epicsEnvSet("EPICS_BASE","/home/install/epics/base")
-epicsEnvSet("EPICSV4HOME","/home/hg")
-cd /home/hg/pvDatabaseCPP/exampleServer
-## Register all support components
-dbLoadDatabase("dbd/exampleServer.dbd")
-exampleServer_registerRecordDeviceDriver(pdbbase)
-## Load record instances
-dbLoadRecords("db/dbScalar.db","name=pvdouble,type=ao")
-dbLoadRecords("db/dbArray.db","name=pvdoubleArray,type=DOUBLE")
-dbLoadRecords("db/dbStringArray.db","name=pvstringArray")
-dbLoadRecords("db/dbEnum.db","name=pvenum")
-dbLoadRecords("db/dbCounter.db","name=pvcounter");
-cd /home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-iocInit()
-Starting iocInit
-############################################################################
-## EPICS R3.14.12.3 $Date: Mon 2012-12-17 14:11:47 -0600$
-## EPICS Base built Dec 21 2013
-############################################################################
-iocRun: All initialization complete
-dbl
-pvdouble
-pvcounter
-pvenum
-pvdoubleArray
-pvstringArray
-epicsThreadSleep(1.0)
-exampleServerCreateRecord pvaServer
-startPVAServer
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : dbPv local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-pvdbl
-pvaServer
-epics> 
-
-

Just like previously you can then execute a pvput and pvget and see Hello World. -

-

The examples, i. e. exampleServer, exampleLink, examplePowerSupply, -and exampleDatabase, are described in separate sections below. -In addition arrayPerformance can be used to measure that performance of big -arrays. It is also described in a later section.

-

Reading section exampleServer and looking at it's code is a good way -to learn how to implement a service.

- -

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP implements the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
pvCopy
-
Creates a PVStructure that contains a copy of an arbitary - subset of the fields of another top level PVStructure. - It can copy data between the two and maintains a bitSet that show - which fields are changed.
-
monitor
-
This provides the ability to monitor changes to fields of a record.
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the C++ implementation of package pvAccess in pvIOCJava. - The localChannelProvider accesses data from PVRecords. - It implements all channel methods except channelRPC, which is implemented by pvAccessCPP.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for implementing many services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

Building pvDatabaseCPP

-

To build pvDatabaseCPP You must provide a file RELEASE.local -in directory configure. -Thus do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product pvDatabaseCPP requires. -Than at the top level just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> make
-
-

This builds pvDatabaseCPP and also the tests and all examples.

-

Each example and arrayPerformance is a completely separate top, -but is also built when make is run in pvDatabaseCPP itself.

-

-Each is a separate top for the following reasons:

-
    -
  1. - It is easier to understand each example including how it is built so that - it can be run as a main or as part of a V3 IOC. -
  2. -
  3. - Each example can be copied somewhere else and used as the basis - for creating a new service. -
  4. -
-

-If it is desired to build an example all by itself, -just follow the same instructions as for building pvDatabaseCPP itself. -For example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product the example requires. -Than at the top level of the example just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> make
-
-

This builds the example.

- -

iocshell commands

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider

-
stopPVAServer
-
Stop the local channel provider
-
pvdbl
-
Provides a list of all the pvRecords in database master -
-
-

The client commands are provided via PVAClientRegister.dbd and the other commands -via PVAServerRegister.dbd.

-

In addition any code that implements a PVRecord must implement an ioc command. -The directory example has examples of how to implement the registration code. -See example/V3IOC/exampleCounter/src/ for a simple example.

- - -

database

-

src/database

-

This Directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
-

src/special

-

This directory has the following files:

-
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The exampleDatabase creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-      
-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

pvDatabase.h

-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    epics::pvData::String getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    bool addRequester(epics::pvData::RequesterPtr const & requester);
-    bool removeRequester(epics::pvData::RequesterPtr const & requester);
-    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    epics::pvData::String getRequesterName() {return getRecordName();}
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void message(
-        PVRecordFieldPtr const & pvRecordField,
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-    void toString(epics::pvData::StringBuilder buf);
-    void toString(epics::pvData::StringBuilder buf,int indentLevel);
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
process
-
Virtual method. - Derived classes must implement this method. - The base implementation does nothing. -
-
destroy
-
This is a virtual method. - A derived class must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it';s own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
addRequester
-
Add a requester to receive messages.
-
removeRequester
-
Remove a message requester.
-
lock_guard
-
This is an inline method that locks the record. The record will automatically - be unlocked when control leaves the block that has the call. -
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getRequesterName
-
virtual method of Requester -
-
message
-
Can be called by implementation code. - The message will be sent to every requester.
-
toString
-
Just calls the top level PVStructure toString method.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    epics::pvData::String getFullFieldName();
-    epics::pvData::String getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-    virtual void message(
-        epics::pvData::String const & message,
-        epics::pvData::MessageType messageType);
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
message
-
Called by implementation code. It calls PVRecord::message after prepending the full - fieldname.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordFieldPtrArrayPtr const & pvRecordField);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(epics::pvData::String const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-    virtual epics::pvData::String getRequesterName();
-    virtual void message(
-        epics::pvData::String const &message,
-        epics::pvData::MessageType messageType);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
message
-
Virtual message of Requester.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is access by the server side of remote pvAccess.

-

channelProviderLocal

-

This is a complete implementation of channelProvider and , -except for channelRPC, provides a complete implementation of Channel -as defined by pvAccess. -For monitors it calls the code described in the following sections.

-

pvCopy

-

This provides code that creates a top level PVStructure that is an arbitrary -subset of the fields in the PVStructure from a PVRecord. -In addition it provides code that monitors changes to the fields in a PVRecord. -A client configures the desired set of subfields and monitoring options -via a pvRequest structure. -pvAccess provides a class CreatePVRequest that creates a pvRequest. -The pvCopy code provides the same functionality as the pvCopy code in pvIOCJava. -

-

monitorAlgorithm

-

Currently all that is implemented is a header file. -The only algorithm currently implemented is onPut -

-

monitorFactory

-

Overview

-

epics::pvData::monitor defines the monitor interfaces -as seen by a client. -See - pvDatabaseCPP.html - For details.

-

-monitorFactory implements the -monitoring interfaces for a PVRecord. -It implements queueSize=0 and queueSize>=2. -

- -

-The implementation uses PVCopy and PVCopyMonitor which are implemented in pvCopy. -When PVCopyMonitor tells monitor that changes -have occurred, monitor applies the appropriate algorithm to each changed field.

- -

Currently only algorithm onPut is implemented but, -like pvIOCJava there are plans to support for the following monitor algorithms:

-
-
onPut
-
A monitor is issued whenever a put is issued to the field. This is the - default unless the record defines deadbands for a field. An exception is - the top level timeStamp which by default is made onChange and monitor - will not be raised.
-
onChange
-
This provides two options: 1) A monitor is raised whenever a field - changes value, and 2) A monitor will never be raised for the field.
-
deadband
-
The field must be a numeric scalar. Whenever the absolute or percentage - value of the field changes by more than a deadband a monitor is issued. - The record instance can also define deadbands.
-
periodic
-
A monitor is issued at a periodic rate if a put was issued to any field - being monitored.
-
-

MonitorFactory

-

MonitorFactory provides the following methods:

-
class MonitorFactory
-{
-    static MonitorPtr create(
-        PVRecordPtr const & pvRecord,
-        MonitorRequester::shared_pointer const & monitorRequester,
-        PVStructurePtr const & pvRequest);
-    static void registerMonitorAlgorithmCreater(
-        MonitorAlgorithmCreatePtr const & monitorAlgorithmCreate,
-        String const & algorithmName);
-}
- -

where

-
-
create
-
Create a monitor. The arguments are: -
-
pvRecord
-
The record being monitored.
-
monitorRequester
-
The monitor requester. This is the code to which monitot events - will be delivered.
-
pvRequest
-
The request options
-
-
-
registerMonitorAlgorithmCreater
-
Called by code that implements a monitor algorithm.
-
-

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generted - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

-

recordList

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure arguments
-        string database master
-        string regularExpression .*
-    structure result
-        string status 
-        string[] names
-
-where: -
-
database
-
The name of the datbase. The default is "master"
-
regularExpression
-
For now this is ignored and the complete list of names is always - returned.
-
status
-
The status of a putGet request.
-
names
-
The list of record names.
-
-

Note that swtshell, which is a Java GUI tool, has a command channelList that -requires that a record of this type is present and calls it. -Thus user code does not have to use a channelGetPut to get the list -of record names.

-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

- -

exampleServer

-

Overview

-

The example implements a simple service that has a top level pvStructure: -

-structure
-    structure argument
-        string value
-    structure result
-        string value
-        time_t timeStamp
-            long secondsPastEpoch
-            int nanoSeconds
-            int userTag
-
-

It is designed to be accessed via a channelPutGet request. -The client sets argument.value -When the record processes it sets result.value to "Hello " -concatenated with argument.value. -Thus if the client sets argument.value equal to "World" -result.value will be "Hello World". -In addition the timeStamp is set to the time when process is called.

-

-The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleService
-mrk> bin/linux-x86_64/exampleService
-
-

Directory Layout

-

-The directory layout is: -

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerInclude.dbd
-       exampleServerMain.cpp
-       exampleServerRegister.cpp
-    ioc
-       Db
-          ...
-       src
-          exampleServerInclude.dbd
-          exampleServerMain.cpp
-   iocBoot
-      exampleServer
-         st.cmd
-         ...
-
-where -
-
ExampleRELEASE.local
-
This is the file that must be copied to RELEASE.local - and edited.
-
exampleServer.h
-
The header file for the service.
-
exampleServer.cpp
-
The service implementation.
-
exampleServerMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. -
-
exampleServerInclude.dbd
-
This has a register command so that the service can be started - on a V3 IOC via iocsh. -
-
exampleServerRegister.cpp
-
This has the code to start the service via the following iocsh - command. -
-exampleServerCreateRecord exampleServer
-
- Multiple commands can be issued to create multiple service records. -
-
ioc
-
This is for building a V3 IOC application.
-
ioc/Db
-
This has template files for creating V3 records.
-
ioc/src
-
The files for running a V3 IOC.
-
iocBoot/exampleServer
-
A place to start exampleServer as part of a V3IOC. - It has a st.cmd file that starts the ioc and also starts pvAccess - and the example.
-
-

If only a main program is desired then the directory layout is:

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerMain.cpp
-
-

Thus if only a main program is required the directory layout is simple.

-

Also many sites will want to build the src directory in an area -separate from where the iocs are build.

-

exampleServer.h

-

The example resides in src -The implementation is in exampleServer.cpp. -

-

The description consists of

-
-class ExampleServer;
-typedef std::tr1::shared_ptr<ExampleServer> ExampleServerPtr;
-
-class ExampleServer :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleServer);
-    static ExampleServerPtr create(
-        epics::pvData::String const & recordName);
-    virtual ~ExampleServer();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleServer(epics::pvData::String const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-
-    epics::pvData::PVStringPtr pvArgumentValue;
-    epics::pvData::PVStringPtr pvResultValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleServer
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleServer
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleServerPtr ExampleServer::create(
-    epics::pvData::String const & recordName)
-{
-    StandardPVFieldPtr standardPVField = getStandardPVField();
-    PVDataCreatePtr pvDataCreate = getPVDataCreate();
-    PVStructurePtr pvArgument = standardPVField->scalar(pvString,"");
-    PVStructurePtr pvResult = standardPVField->scalar(pvString,"timeStamp");
-    StringArray names;
-    names.reserve(2);
-    PVFieldPtrArray fields;
-    fields.reserve(2);
-    names.push_back("argument");
-    fields.push_back(pvArgument);
-    names.push_back("result");
-    fields.push_back(pvResult);
-    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(names,fields);
-    ExampleServerPtr pvRecord(
-        new ExampleServer(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleServerPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleServer::ExampleServer(
-    epics::pvData::String const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleServer::~ExampleServer()
-{
-}
-
-void ExampleServer::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleServer::init()
-{
-    initPVRecord();
-    PVFieldPtr pvField;
-    pvArgumentValue = getPVStructure()->getStringField("argument.value");
-    if(pvArgumentValue.get()==NULL) return false;
-    pvResultValue = getPVStructure()->getStringField("result.value");
-    if(pvResultValue.get()==NULL) return false;
-    pvTimeStamp.attach(getPVStructure()->getSubField("result.timeStamp"));
-    return true;
-}
-
-

The implementation of process is:

-
-void ExampleServer::process()
-{
-    pvResultValue->put(String("Hello ") + pvArgumentValue->get());
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It gives a value to result.value and -then sets the timeStamp to the current time. -

src/exampleServerMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleServer");
-    PVRecordPtr pvRecord = ExampleServer::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    cout << "result of addRecord " << recordName << " " << result << endl;
-    pvRecord.reset();
-    startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    cout << "exampleServer\n";
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-        getline(cin,str);
-        if(str.compare("exit")==0) break;
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates a ExampleServer record with the name exampleServer -
  • -
  • Prints exampleServer on standard out.
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleServer

-

To start exampleServer as part of a V3IOC: -

-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../../bin/linux-x86_64/exampleServer st.cmd
-

-

You can then issue the commands dbl and pvdbl: -

-epics> dbl
-double01
-epics> pvdbl
-exampleServer
-epics> 
-
-double01 is a v3Record. -exampleServer is a pvRecord. -

-

-It starts pvASrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

- -

exampleDatabase

-

The exampleServer pvDatabase has many records including the following:

-
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

exampleDatabase can be started as a main program or as a V3 IOIC. -If started as a V3 IOC it also has a number of V3 records, -and starts pvaSrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

-

exampleLink

-

This example show how a service can access other PVRecords. -This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - cause deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all locking issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transfered between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-ChannelProvider::shared_pointer provider = channelAccess->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
- -

Directory Layout

-
-exampleLink
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleLink.h
-       exampleLink.cpp
-       exampleLinkInclude.dbd
-       exampleLinkRegister.cpp
-    ioc
-       Db
-       src
-          exampleLinkInclude.dbd
-          exampleLinkMain.cpp
-   iocBoot
-      exampleLink
-         st.local
-         st.remote
-         ...
-
-

This example is only built to be run as part of a V3 IOC. -Note that two startup files are available: st.local and st.remote. -st.local has two records: doubleArray and exampleLink. -doubleArray is a record that can be changed via a call to pvput. -exampleLink is a record that, when processed, gets the value from doubleArray and sets its value equal -to the value read. -st.local has both records. -st.remote has only one record named exampleLinkRemote. -

-

To start the example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleLink/iocBoot/exampleLink
-mrk> ../../bin/linux-x86_64/exampleLink st.local 
-
-

then in another window:

-
-mrk> pvput doubleArray 4 100 200 300 400
-Old : doubleArray 0
-New : doubleArray 4 100 200 300 400
-mrk> pvget -r "record[process=true]field(value)" exampleLink
-exampleLink
-structure 
-    double[] value [100,200,300,400]
-mrk> 
-
-

exampleLink Implementation

-

exampleLink.h contains the following:

-
-...
-class ExampleLink :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExampleLink);
-    static ExampleLinkPtr create(
-        epics::pvData::String const & recordName,
-        epics::pvData::String const & providerName,
-        epics::pvData::String const & channelName
-        );
-    virtual ~ExampleLink() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExampleLinkPtr ExampleLink::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExampleLinkPtr pvRecord(
-        new ExampleLink(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExampleLink instance, -and then calls the init method and the returns a ExampleLinkPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExampleLink::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

examplePowerSupply

-

This is an example of creating a service that requires a somewhat complicated -top level PVStructure. -It is similar to the powerSupply example that is provided with pvIOCJava. -Look at the code for details. -

-

Array Performance and Memory Example

-

This section describes main programs that demonstrate performance -of large arrays and can also be used to check for memory leaks. -Checking for memory leaks can be accomplished by running the programs with valgrind -or some other memory check program. -

-

Brief Summary

-

The programs are:

-
-
arrayPerformanceMain
-
This is server and also a configurable number of longArrayMonitor clients. - The clients can use either the local or - remote providers. The moitor code is the same code that is used by longArrayMonitorMain. -
-
longArrayMonitorMain
-
Remote client that monitors the array served by arrayPerformanceMain.
-
longArrayGetMain
-
Remote client that uses channelGet to access the array served by arrayPerformanceMain.
-
longArrayPutMain
-
Remote client that uses channelPut to access the array served by arrayPerformanceMain.
-
-

Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
-default
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0.0
-
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName queueSize waitTime
-default
-longArrayMonitorMain arrayPerformance 2 0.0
-
-mrk> bin/linux-x86_64/longArrayGetMain -help
-longArrayGetMain channelName iterBetweenCreateChannel iterBetweenCreateChannelGet delayTime
-default
-longArrayGetMain arrayPerformance 0 0 1
-
-mrk> bin/linux-x86_64/longArrayPutMain -help
-longArrayPutMain channelName arraySize iterBetweenCreateChannel iterBetweenCreateChannelPut delayTime
-default
-longArrayPutMain arrayPerformance 10 0 0 1
-
-mrk> 
-
-

Example output

-

Note: These may fail if run on a platform that does not have sufficent memory,

-

To see an example just execute the following commands in four different terminal windows:

-
-bin/linux/<arch>/arrayPerformanceMain
-bin/linux/<arch>/longArrayMonitorMain
-bin/linux/<arch>/longArrayGetMain
-bin/linux/<arch>/longArrayPutMain
-
-

Each program generates a report every second when it has somthing to report. -Examples are: -

-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0
-...
- monitors/sec 66 first 131 last 131 changed {1, 2} overrun {} megaElements/sec 656.999
-arrayPerformance value 132 time 1.00486 Iterations/sec 65.681 megaElements/sec 656.81
- monitors/sec 66 first 197 last 197 changed {1, 2} overrun {} megaElements/sec 656.304
-arrayPerformance value 198 time 1.00563 Iterations/sec 65.6307 megaElements/sec 656.307
- monitors/sec 66 first 263 last 263 changed {1, 2} overrun {} megaElements/sec 654.824
-...
-
-
-mrk> bin/linux-x86_64/longArrayMonitorMain
-longArrayMonitorMain arrayPerformance 2 0
-...
- monitors/sec 6 first 2357 last 2357 changed {1, 2} overrun {} megaElements/sec 68.6406
- monitors/sec 13 first 2385 last 2385 changed {1, 2} overrun {} megaElements/sec 118.72
- monitors/sec 9 first 2418 last 2418 changed {1, 2} overrun {1, 2} megaElements/sec 85.0984
-...
-
-
-mrk> bin/linux-x86_64/longArrayPutMain
-longArrayPutMain arrayPerformance 10 0 0 1
-...
-put numChannelPut 0 time 1.00148 Elements/sec 79.8819
-put numChannelPut 1 time 1.00176 Elements/sec 79.8598
-...
-
-
-mrk> bin/linux-x86_64/longArrayGetMain
-longArrayGetMain arrayPerformance 0 0 1
-...
-get kiloElements/sec 7384.61
-get kiloElements/sec 8726.34
-...
-
- -

arrayPerformance

-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
queueSize
-
The queueSize for the element queue. - A value less than 1 will become 1. -
-
waitTime
-
The time that longArrayMonitor will sleep after poll returns a monitorElement.
-
-

-arrayPerformance creates a PVRecord that has the structure:. -

-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers.

-

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
Once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the interation count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report every second showing how many elements has received. -For every monitor it also checks that the number of alements is >0 and the -the first element equals the last element. It reports an error if either -of these conditions is not true.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
queueSize
-
The queueSize. Note that any size <2 is made 2.
-
waitTime
-
The time to wait after a poll request returns a monitorElement. - This can be used to force an overrun of the client even if there is no - overrun on the server.
-
-

longArrayGet

-

This is a pvAccess client that uses channelGet to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayGetMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelGet
-
The number of iterations between destroying and recreating the channelGet. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to dalay between gets.
-
-

longArrayPut

-

This is a pvAccess client that uses channelPut to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayPutMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
arraySize
-
The capacity and length of the array to put to the server.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelPut
-
The number of iterations between destroying and recreating the channelPut. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to dalay between gets.
-
- -

Some results

-

array performance

-

The results were from my laptop. -It has a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system is linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 10 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 50 iterations per second -and is putting about 500million elements per second. -Since each element is an int64 this means about 4gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-

-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 25 interations per second and 250 million elements per second. -The next section has an example that demonstrates what happens. -Note that if the array size is small enough to fit in the local cache then running longArrayMonitor -has almost no effect of arrayPerforance. -

-

memory leaks

-

Running longArrayMonitorMain, longArrayPutMain, and longArrayGetMain -under valgrind shows no memory leaks.

-

arrayPerformanceMain shows the following:

-
-==9125== LEAK SUMMARY:
-==9125==    definitely lost: 0 bytes in 0 blocks
-==9125==    indirectly lost: 0 bytes in 0 blocks
-==9125==      possibly lost: 576 bytes in 2 blocks
-
-

The possibly leaked is either 1 or 2 blocks. -It seems to be the same if clients are connected. -

-

Vector Performance

-

This example demonstrates how array size effects performance. -The example is run as:

-
-bin/linux-x86_64/vectorPerformanceMain -help
-vectorPerformanceMain size delay nThread
-default
-vectorPerformance 50000000 0.01 1 
-
-

Consider the following:

-
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
-...
-thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
-thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
-thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
-...
-thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
-thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
-thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
-thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
-thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
-thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
-thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
-thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
-thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
-thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
-thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
-thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
-thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
-thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
-thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
-thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
-thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
-thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
-
-

As more threads are running the slower each thread runs.

-

But now consider a size that fits in a local cache.

-

-bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
-...
-thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
-thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
-thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
-... 
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
-...
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-...
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
-...
-thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
-thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
-thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
-thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
-thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
-thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
-
-

Now the number of threads has a far smaller effect on the performance of each thread. -

- -
- - diff --git a/documentation/pvDatabaseCPP_20140710.html b/documentation/pvDatabaseCPP_20140710.html deleted file mode 100644 index 047824a..0000000 --- a/documentation/pvDatabaseCPP_20140710.html +++ /dev/null @@ -1,1940 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 10-July-2014

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20140710.html -
-
Previous version:
-
pvDatabaseCPP20140219.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessable database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extenson must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 10-July-2014 version of of pvDatabaseCPP.

-

-

This version is a complete implementation of what is described in this manual. -

- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

The main purpose of this project to make it easier to implement services that are accessed via pvAccess. -This project supplies is a complete implementation of the server side of pvAccess. -All that a service has to provide is a top level PVStructure and a process method. -A service can be run as a main process or can be part of a V3 IOC. -Thus services can be developed that interact with V3 records, asynDriver, -areaDetector, etc. -

-

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also provides a C++ implementation - of the monitor and pvCopy components from pvIOCJava
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In the later case the IOC has both a database of V3 Records - and a pvDatabase.
-
-

Base classes make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resources used by the implementation.
-
-

Getting started

-

The first step is to build pvDatabaseCPP as described in the next section.

-

One of the examples is exampleServer. -It can be started either via a main program or as part of a V3 IOC. -

-

To start it as a main program do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> bin/linux-x86_64/exampleServerMain 
-
-

You should see something like the following:

-
-result of addRecord exampleServer 1
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-exampleServer
-Type exit to stop: 
-
-

Then in another window execute a pvput and pvget as follows:

-
-mrk> pvput -r "field(argument.value)" exampleServer World
-...
-mrk> pvget -r "record[process=true]field(result.value)" exampleServer
-exampleServer
-structure 
-    string value Hello World
-mrk> 
-
-

To run the example as part of a V3 IOC do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../bin/linux-x86_64/exampleServer st.cmd 
-
-

You will see the following:

-
-> envPaths
-epicsEnvSet("ARCH","linux-x86_64")
-epicsEnvSet("IOC","exampleServer")
-epicsEnvSet("TOP","/home/hg/pvDatabaseCPP/exampleServer")
-epicsEnvSet("EPICS_BASE","/home/install/epics/base")
-epicsEnvSet("EPICSV4HOME","/home/hg")
-cd /home/hg/pvDatabaseCPP/exampleServer
-## Register all support components
-dbLoadDatabase("dbd/exampleServer.dbd")
-exampleServer_registerRecordDeviceDriver(pdbbase)
-## Load record instances
-dbLoadRecords("db/dbScalar.db","name=pvdouble,type=ao")
-dbLoadRecords("db/dbArray.db","name=pvdoubleArray,type=DOUBLE")
-dbLoadRecords("db/dbStringArray.db","name=pvstringArray")
-dbLoadRecords("db/dbEnum.db","name=pvenum")
-dbLoadRecords("db/dbCounter.db","name=pvcounter");
-cd /home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-iocInit()
-Starting iocInit
-############################################################################
-## EPICS R3.14.12.3 $Date: Mon 2012-12-17 14:11:47 -0600$
-## EPICS Base built Dec 21 2013
-############################################################################
-iocRun: All initialization complete
-dbl
-pvdouble
-pvcounter
-pvenum
-pvdoubleArray
-pvstringArray
-epicsThreadSleep(1.0)
-exampleServerCreateRecord pvaServer
-startPVAServer
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : dbPv local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-pvdbl
-pvaServer
-epics> 
-
-

Just like previously you can then execute a pvput and pvget and see Hello World. -

-

The examples, i. e. exampleServer, exampleLink, examplePowerSupply, -and exampleDatabase, are described in separate sections below. -In addition arrayPerformance can be used to measure that performance of big -arrays. It is also described in a later section.

-

Reading section exampleServer and looking at it's code is a good way -to learn how to implement a service.

- -

Relationship with pvIOCJava.

-

This document descibes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP implements the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
copy and monitor
-
pvDataCPP provides facilities copy and monitor. - This facilities allow a client to access an arbitrary subset - of the fields in the top level structure associated with a channel, - and to monitor changes in the top level structure. - pvDatabaseCPP uses what pvDataCPP provides and has code that - associates these facilities with a PVRecord. -
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the C++ implementation of package pvAccess in pvIOCJava. - The localChannelProvider accesses data from PVRecords. - It implements all channel methods except channelRPC, which is implemented by pvAccessCPP.
-
-

Minumum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for implementing many services. -The following are the minimium features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

Building pvDatabaseCPP

-

To build pvDatabaseCPP You must provide a file RELEASE.local -in directory configure. -Thus do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product pvDatabaseCPP requires. -Than at the top level just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> make
-
-

This builds pvDatabaseCPP and also the tests and all examples.

-

Each example and arrayPerformance is a completely separate top, -but is also built when make is run in pvDatabaseCPP itself.

-

-Each is a separate top for the following reasons:

-
    -
  1. - It is easier to understand each example including how it is built so that - it can be run as a main or as part of a V3 IOC. -
  2. -
  3. - Each example can be copied somewhere else and used as the basis - for creating a new service. -
  4. -
-

-If it is desired to build an example all by itself, -just follow the same instructions as for building pvDatabaseCPP itself. -For example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product the example requires. -Than at the top level of the example just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> make
-
-

This builds the example.

- -

iocshell commands

-

Shell commands are made available via the standard DBD include mechanism -provided by iocCore. -The following provide EPICS V4 shell commands:

-
-
pvAccessCPP
-
PVAClientRegister.dbd and PVAServerRegister.dbd
-
pvaSrv
-
dbPv.dbd
-
pvDatabaseCPP
-
registerChannelProviderLocal.dbd
-
-

-Look at exampleServer/ioc/src/exampleServerInclude.dbd for an example -of how an application can make the shell commands available. -

-

Commands From pvAccessCPP

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider

-
stopPVAServer
-
Stop the local channel provider
-
-

-

Commands implemented by pvDatabaseCPP

-

The following iocsh commands are provided for a V3IOC:

-
-
pvdbl
-
Provides a list of all the pvRecords in database master -
-
-

In addition any code that implements a PVRecord must implement an ioc command.

-

Look at any of the examples to see how to implement shell commands.

-

Commands implemented by pvaSrv

-

pvaSrv provides a pvAccess server that provides access to iocCore records.

-

pvaSrv does not provide any shell commands but it can be part of an IOC. -Just make sure your application configures pvaSrv and then include the following file: -

-include "dbPv.dbd"
-
-

- -

database

-

src/database

-

This Directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
-

src/special

-

This directory has the following files:

-
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The exampleDatabase creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-if(pvRecord==NULL) {
-      cout << "RecordListRecord::create failed" << endl;
-} else {
-    result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-}
-      
-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

pvDatabase.h

-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that acceses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void start() {}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    std::string getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
start
-
Virtual method. - Optional method for derived class. - It is called before record is added to database. - The base method does nothing. -
-
process
-
Virtual method. - Derived classes usually implement this method. - It implements the semantics for the record. - The base implementation does nothing. -
-
destroy
-
Virtual method. - Optional method for derived class. - If the derived class implements this it - must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it's own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If falseclient can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    std::string getFullFieldName();
-    std::string getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordStructurePtr const & parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(std::string const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-    virtual std::string getRequesterName();
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is accessed by the server side of remote pvAccess. -It uses the copy and monitor facilities from pvDataCPP and connects -them to a PVRecord. -

-

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure argument
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generted - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

-

recordList

-

This implements a PVRecord that allows a client to -get the names of all the PVRecords in the PVDatabase. -It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure argument
-        string database master
-        string regularExpression .*
-    structure result
-        string status 
-        string[] name
-
-where: -
-
database
-
The name of the datbase. The default is "master"
-
regularExpression
-
For now this is ignored and the complete list of names is always - returned.
-
status
-
The status of a putGet request.
-
name
-
The array of record names.
-
-

Note that swtshell, which is a Java GUI tool, has a command channelList that -requires that a record of this type is present and calls it. -Thus user code does not have to use a channelGetPut to get the list -of record names.

-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-recordName = "recordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

- -

exampleServer

-

Overview

-

The example implements a simple service that has a top level pvStructure: -

-structure
-    structure argument
-        string value
-    structure result
-        string value
-        time_t timeStamp
-            long secondsPastEpoch
-            int nanoSeconds
-            int userTag
-
-

It is designed to be accessed via a channelPutGet request. -The client sets argument.value -When the record processes it sets result.value to "Hello " -concatenated with argument.value. -Thus if the client sets argument.value equal to "World" -result.value will be "Hello World". -In addition the timeStamp is set to the time when process is called.

-

-The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleService
-mrk> bin/linux-x86_64/exampleService
-
-

Directory Layout

-

-The directory layout is: -

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerInclude.dbd
-       exampleServerMain.cpp
-       exampleServerRegister.cpp
-       exampleServerRegister.dbd
-    ioc
-       Db
-          ...
-       src
-          exampleServerInclude.dbd
-          exampleServerMain.cpp
-   iocBoot
-      exampleServer
-         st.cmd
-         ...
-
-where -
-
ExampleRELEASE.local
-
- If you make a copy of exampleServer and use it - to create a new server, - This is the file that must be copied to RELEASE.local - and edited.
-
exampleServer.h
-
The header file for the service.
-
exampleServer.cpp
-
The service implementation.
-
exampleServerMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. -
-
exampleServerInclude.dbd
-
This has a register command so that the service can be started - on a V3 IOC via iocsh. -
-
exampleServerRegister.cpp
-
This has the code to start the service via the following iocsh - command. -
exampleServerRegister.dbd
-
This is the file that is used to create the shell command - exampleServerCreateRecord.
-
-exampleServerCreateRecord exampleServer
-
- Multiple commands can be issued to create multiple service records. - -
ioc
-
This is for building a V3 IOC application.
-
ioc/Db
-
This has template files for creating V3 records.
-
ioc/src
-
The files for running a V3 IOC.
-
iocBoot/exampleServer
-
A place to start exampleServer as part of a V3IOC. - It has a st.cmd file that starts the ioc and also starts pvAccess - and the example.
-
-

If only a main program is desired then the directory layout is:

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerMain.cpp
-
-

Thus if only a main program is required the directory layout is simple.

-

Also many sites will want to build the src directory in an area -separate from where the iocs are build.

-

exampleServer.h

-

The example resides in src -The implementation is in exampleServer.cpp. -

-

The description consists of

-
-class ExampleServer;
-typedef std::tr1::shared_ptr<ExampleServer> ExampleServerPtr;
-
-class ExampleServer :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleServer);
-    static ExampleServerPtr create(
-        std::string const & recordName);
-    virtual ~ExampleServer();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleServer(std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-
-    epics::pvData::PVStringPtr pvArgumentValue;
-    epics::pvData::PVStringPtr pvResultValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleServer
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleServer
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleServerPtr ExampleServer::create(
-    std::string const & recordName)
-{
-    StandardFieldPtr standardField = getStandardField();
-    FieldCreatePtr fieldCreate = getFieldCreate();
-    PVDataCreatePtr pvDataCreate = getPVDataCreate();
-    StructureConstPtr  topStructure = fieldCreate->createFieldBuilder()->
-        addNestedStructure("argument")->
-            add("value",pvString)->
-            endNested()->
-        addNestedStructure("result") ->
-            add("value",pvString) ->
-            add("timeStamp",standardField->timeStamp()) ->
-            endNested()->
-        createStructure();
-    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
-
-    ExampleServerPtr pvRecord(
-        new ExampleServer(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleServerPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleServer::ExampleServer(
-    std::string const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleServer::~ExampleServer()
-{
-}
-
-void ExampleServer::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleServer::init()
-{
-    initPVRecord();
-    PVFieldPtr pvField;
-    pvArgumentValue = getPVStructure()->getStringField("argument.value");
-    if(pvArgumentValue.get()==NULL) return false;
-    pvResultValue = getPVStructure()->getStringField("result.value");
-    if(pvResultValue.get()==NULL) return false;
-    pvTimeStamp.attach(getPVStructure()->getSubField("result.timeStamp"));
-    return true;
-}
-
-

The implementation of process is:

-
-void ExampleServer::process()
-{
-    pvResultValue->put(String("Hello ") + pvArgumentValue->get());
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It gives a value to result.value and -then sets the timeStamp to the current time. -

src/exampleServerMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleServer");
-    PVRecordPtr pvRecord = ExampleServer::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    recordName = "traceRecordPGRPC";
-    pvRecord = TraceRecord::create(recordName);
-    result = master->r;addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    recordName = "recordListPGRPC";
-    pvRecord = RecordListRecord::create(recordName);
-    result = master->r;addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    ServerContext::shared_pointer pvaServer =
-        startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    PVStringArrayPtr pvNames = master->r;getRecordNames();
-    shared_vector<const string>r; names = pvNames->r;view();
-    for(size_t i=0; i<names.size(); ++i) cout << names[i] << endl;
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates record exampleServer
  • -
  • creates records traceRecordPGRPC and recordListPGRPC
  • -
  • lists all the records
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleServer

-

To start exampleServer as part of a V3IOC: -

-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../../bin/linux-x86_64/exampleServer st.cmd
-

-

You can then issue the commands dbl and pvdbl: -

-epics> dbl
-pvdouble
-pvcounter
-pvenum
-pvdoubleArray
-pvstringArray
-epics> pvdbl
-exampleServer
-epics> 
-
-dbl shows the V3 records. -pvdbl shows the pvRecords. -

-

-It starts pvaSrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

- -

exampleDatabase

-

The exampleServer pvDatabase has many records including the following:

-
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

exampleDatabase can be started as a main program or as a V3 IOIC. -If started as a V3 IOC it also has a number of V3 records, -and starts pvaSrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

-

exampleLink

-

This example show how a service can access other PVRecords. -This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - cause deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all locking issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transfered between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelProvider::shared_pointer provider =
-     getChannelProviderRegistry()->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
- -

Directory Layout

-
-exampleLink
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleLink.h
-       exampleLink.cpp
-       exampleLinkInclude.dbd
-       exampleLinkRegister.cpp
-    ioc
-       Db
-       src
-          exampleLinkInclude.dbd
-          exampleLinkMain.cpp
-   iocBoot
-      exampleLink
-         st.local
-         st.remote
-         ...
-
-

This example is only built to be run as part of a V3 IOC. -Note that two startup files are available: st.local and st.remote. -st.local has two records: doubleArray and exampleLink. -doubleArray is a record that can be changed via a call to pvput. -exampleLink is a record that, when processed, gets the value from doubleArray and sets its value equal -to the value read. -st.local has both records. -st.remote has only one record named exampleLinkRemote. -

-

To start the example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleLink/iocBoot/exampleLink
-mrk> ../../bin/linux-x86_64/exampleLink st.local 
-
-

then in another window:

-
-mrk> pvput doubleArray 4 100 200 300 400
-Old : doubleArray 0
-New : doubleArray 4 100 200 300 400
-mrk> pvget -r "record[process=true]field(value)" exampleLink
-exampleLink
-structure 
-    double[] value [100,200,300,400]
-mrk> 
-
-

exampleLink Implementation

-

exampleLink.h contains the following:

-
-...
-class ExampleLink :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExampleLink);
-    static ExampleLinkPtr create(
-        std::string const & recordName,
-        std::string const & providerName,
-        std::string const & channelName
-        );
-    virtual ~ExampleLink() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExampleLinkPtr ExampleLink::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExampleLinkPtr pvRecord(
-        new ExampleLink(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExampleLink instance, -and then calls the init method and the returns a ExampleLinkPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExampleLink::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

examplePowerSupply

-

This is an example of creating a service that requires a somewhat complicated -top level PVStructure. -It is similar to the powerSupply example that is provided with pvIOCJava. -Look at the code for details. -

-

Array Performance and Memory Example

-

This section describes main programs that demonstrate performance -of large arrays and can also be used to check for memory leaks. -Checking for memory leaks can be accomplished by running the programs with valgrind -or some other memory check program. -

-

Brief Summary

-

The programs are:

-
-
arrayPerformanceMain
-
This is server and also a configurable number of longArrayMonitor clients. - The clients can use either the local or - remote providers. The moitor code is the same code that is used by longArrayMonitorMain. -
-
longArrayMonitorMain
-
Remote client that monitors the array served by arrayPerformanceMain.
-
longArrayGetMain
-
Remote client that uses channelGet to access the array served by arrayPerformanceMain.
-
longArrayPutMain
-
Remote client that uses channelPut to access the array served by arrayPerformanceMain.
-
-

Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
-default
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0.0
-
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName queueSize waitTime
-default
-longArrayMonitorMain arrayPerformance 2 0.0
-
-mrk> bin/linux-x86_64/longArrayGetMain -help
-longArrayGetMain channelName iterBetweenCreateChannel iterBetweenCreateChannelGet delayTime
-default
-longArrayGetMain arrayPerformance 0 0 1
-
-mrk> bin/linux-x86_64/longArrayPutMain -help
-longArrayPutMain channelName arraySize iterBetweenCreateChannel iterBetweenCreateChannelPut delayTime
-default
-longArrayPutMain arrayPerformance 10 0 0 1
-
-mrk> 
-
-

Example output

-

Note: These may fail if run on a platform that does not have sufficent memory,

-

To see an example just execute the following commands in four different terminal windows:

-
-bin/linux/<arch>/arrayPerformanceMain
-bin/linux/<arch>/longArrayMonitorMain
-bin/linux/<arch>/longArrayGetMain
-bin/linux/<arch>/longArrayPutMain
-
-

Each program generates a report every second when it has somthing to report. -Examples are: -

-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0
-...
- monitors/sec 66 first 131 last 131 changed {1, 2} overrun {} megaElements/sec 656.999
-arrayPerformance value 132 time 1.00486 Iterations/sec 65.681 megaElements/sec 656.81
- monitors/sec 66 first 197 last 197 changed {1, 2} overrun {} megaElements/sec 656.304
-arrayPerformance value 198 time 1.00563 Iterations/sec 65.6307 megaElements/sec 656.307
- monitors/sec 66 first 263 last 263 changed {1, 2} overrun {} megaElements/sec 654.824
-...
-
-
-mrk> bin/linux-x86_64/longArrayMonitorMain
-longArrayMonitorMain arrayPerformance 2 0
-...
- monitors/sec 6 first 2357 last 2357 changed {1, 2} overrun {} megaElements/sec 68.6406
- monitors/sec 13 first 2385 last 2385 changed {1, 2} overrun {} megaElements/sec 118.72
- monitors/sec 9 first 2418 last 2418 changed {1, 2} overrun {1, 2} megaElements/sec 85.0984
-...
-
-
-mrk> bin/linux-x86_64/longArrayPutMain
-longArrayPutMain arrayPerformance 10 0 0 1
-...
-put numChannelPut 0 time 1.00148 Elements/sec 79.8819
-put numChannelPut 1 time 1.00176 Elements/sec 79.8598
-...
-
-
-mrk> bin/linux-x86_64/longArrayGetMain
-longArrayGetMain arrayPerformance 0 0 1
-...
-get kiloElements/sec 7384.61
-get kiloElements/sec 8726.34
-...
-
- -

arrayPerformance

-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
queueSize
-
The queueSize for the element queue. - A value less than 1 will become 1. -
-
waitTime
-
The time that longArrayMonitor will sleep after poll returns a monitorElement.
-
-

-arrayPerformance creates a PVRecord that has the structure:. -

-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers.

-

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
Once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the interation count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report every second showing how many elements has received. -For every monitor it also checks that the number of alements is >0 and the -the first element equals the last element. It reports an error if either -of these conditions is not true.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
queueSize
-
The queueSize. Note that any size <2 is made 2.
-
waitTime
-
The time to wait after a poll request returns a monitorElement. - This can be used to force an overrun of the client even if there is no - overrun on the server.
-
-

longArrayGet

-

This is a pvAccess client that uses channelGet to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayGetMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelGet
-
The number of iterations between destroying and recreating the channelGet. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to dalay between gets.
-
-

longArrayPut

-

This is a pvAccess client that uses channelPut to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayPutMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
arraySize
-
The capacity and length of the array to put to the server.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelPut
-
The number of iterations between destroying and recreating the channelPut. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to dalay between gets.
-
- -

Some results

-

array performance

-

The results were from my laptop. -It has a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system is linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 10 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 50 iterations per second -and is putting about 500million elements per second. -Since each element is an int64 this means about 4gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-

-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 25 interations per second and 250 million elements per second. -The next section has an example that demonstrates what happens. -Note that if the array size is small enough to fit in the local cache then running longArrayMonitor -has almost no effect of arrayPerforance. -

-

memory leaks

-

Running longArrayMonitorMain, longArrayPutMain, and longArrayGetMain -under valgrind shows no memory leaks.

-

arrayPerformanceMain shows the following:

-
-==9125== LEAK SUMMARY:
-==9125==    definitely lost: 0 bytes in 0 blocks
-==9125==    indirectly lost: 0 bytes in 0 blocks
-==9125==      possibly lost: 576 bytes in 2 blocks
-
-

The possibly leaked is either 1 or 2 blocks. -It seems to be the same if clients are connected. -

-

Vector Performance

-

This example demonstrates how array size effects performance. -The example is run as:

-
-bin/linux-x86_64/vectorPerformanceMain -help
-vectorPerformanceMain size delay nThread
-default
-vectorPerformance 50000000 0.01 1 
-
-

Consider the following:

-
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
-...
-thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
-thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
-thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
-...
-thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
-thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
-thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
-thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
-thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
-thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
-thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
-thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
-thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
-thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
-thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
-thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
-thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
-thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
-thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
-thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
-thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
-thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
-
-

As more threads are running the slower each thread runs.

-

But now consider a size that fits in a local cache.

-

-bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
-...
-thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
-thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
-thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
-... 
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
-...
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-...
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
-...
-thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
-thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
-thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
-thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
-thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
-thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
-
-

Now the number of threads has a far smaller effect on the performance of each thread. -

- -
- - diff --git a/documentation/pvDatabaseCPP_20140811.html b/documentation/pvDatabaseCPP_20140811.html deleted file mode 100644 index a3141ee..0000000 --- a/documentation/pvDatabaseCPP_20140811.html +++ /dev/null @@ -1,2111 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 11-August-2014

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20140811.html -
-
Previous version:
-
pvDatabaseCPP20140710.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessible database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extension must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 11-August-2014 version of of pvDatabaseCPP.

-

-

This version is a complete implementation of what is described in this manual. -

- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

The main purpose of this project to make it easier to implement services that are accessed via pvAccess. -This project supplies is a complete implementation of the server side of pvAccess. -All that a service has to provide is a top level PVStructure and a process method. -A service can be run as a main process or can be part of a V3 IOC. -Thus services can be developed that interact with V3 records, asynDriver, -areaDetector, etc. -

- -

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features: -

-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. - This component also provides a C++ implementation - of the monitor and pvCopy components from pvIOCJava
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In the later case the IOC has both a database of V3 Records - and a pvDatabase.
-
-

Base classes make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resources used by the implementation.
-
-

Doxygen documentation is available at doxygenDoc

-

Getting started

-

The first step is to build pvDatabaseCPP as described in the next section.

-

One of the examples is exampleServer. -It can be started either via a main program or as part of a V3 IOC. -

-

To start it as a main program do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> bin/linux-x86_64/exampleServerMain 
-
-

You should see something like the following:

-
-result of addRecord exampleServer 1
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-exampleServer
-Type exit to stop: 
-
-

Then in another window execute a pvput and pvget as follows:

-
-mrk> pvput -r "field(argument.value)" exampleServer World
-...
-mrk> pvget -r "record[process=true]field(result.value)" exampleServer
-exampleServer
-structure 
-    string value Hello World
-mrk> 
-
-

To run the example as part of a V3 IOC do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../bin/linux-x86_64/exampleServer st.cmd 
-
-

You will see the following:

-
-> envPaths
-epicsEnvSet("ARCH","linux-x86_64")
-epicsEnvSet("IOC","exampleServer")
-epicsEnvSet("TOP","/home/hg/pvDatabaseCPP/exampleServer")
-epicsEnvSet("EPICS_BASE","/home/install/epics/base")
-epicsEnvSet("EPICSV4HOME","/home/hg")
-cd /home/hg/pvDatabaseCPP/exampleServer
-## Register all support components
-dbLoadDatabase("dbd/exampleServer.dbd")
-exampleServer_registerRecordDeviceDriver(pdbbase)
-## Load record instances
-dbLoadRecords("db/dbScalar.db","name=pvdouble,type=ao")
-dbLoadRecords("db/dbArray.db","name=pvdoubleArray,type=DOUBLE")
-dbLoadRecords("db/dbStringArray.db","name=pvstringArray")
-dbLoadRecords("db/dbEnum.db","name=pvenum")
-dbLoadRecords("db/dbCounter.db","name=pvcounter");
-cd /home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-iocInit()
-Starting iocInit
-############################################################################
-## EPICS R3.14.12.3 $Date: Mon 2012-12-17 14:11:47 -0600$
-## EPICS Base built Dec 21 2013
-############################################################################
-iocRun: All initialization complete
-dbl
-pvdouble
-pvcounter
-pvenum
-pvdoubleArray
-pvstringArray
-epicsThreadSleep(1.0)
-exampleServerCreateRecord pvaServer
-startPVAServer
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : dbPv local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-pvdbl
-pvaServer
-epics> 
-
-

Just like previously you can then execute a pvput and pvget and see Hello World. -

-

The examples, i. e. exampleServer, exampleLink, examplePowerSupply, -and exampleDatabase, are described in separate sections below. -In addition arrayPerformance can be used to measure that performance of big -arrays. It is also described in a later section.

-

Reading section exampleServer and looking at it's code is a good way -to learn how to implement a service.

- -

Relationship with pvIOCJava.

-

This document describes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP implements the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficiently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

-

Phased Development

-

This documentation describes the first phase of a phased implementation of pvDatabaseCPP: -

-
pvRecord -
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase -
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without writing any code.
-
-

The completion of each phase provides useful features that can be used without waiting for the -completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
copy and monitor
-
pvDataCPP provides facilities copy and monitor. - This facilities allow a client to access an arbitrary subset - of the fields in the top level structure associated with a channel, - and to monitor changes in the top level structure. - pvDatabaseCPP uses what pvDataCPP provides and has code that - associates these facilities with a PVRecord. -
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the C++ implementation of package pvAccess in pvIOCJava. - The localChannelProvider accesses data from PVRecords. - It implements all channel methods except channelRPC, which is implemented by pvAccessCPP.
-
-

Minimum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for implementing many services. -The following are the minimum features required

-
-
PVDatabase
-
This holds a set of PVRecords. It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed.
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

Building pvDatabaseCPP

-

To build pvDatabaseCPP You must provide a file RELEASE.local -in directory configure. -Thus do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product pvDatabaseCPP requires. -Than at the top level just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> make
-
-

This builds pvDatabaseCPP and also the tests and all examples.

-

Each example and arrayPerformance is a completely separate top, -but is also built when make is run in pvDatabaseCPP itself.

-

-Each is a separate top for the following reasons:

-
    -
  1. - It is easier to understand each example including how it is built so that - it can be run as a main or as part of a V3 IOC. -
  2. -
  3. - Each example can be copied somewhere else and used as the basis - for creating a new service. -
  4. -
-

-If it is desired to build an example all by itself, -just follow the same instructions as for building pvDatabaseCPP itself. -For example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product the example requires. -Than at the top level of the example just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> make
-
-

This builds the example.

- -

iocshell commands

-

Shell commands are made available via the standard DBD include mechanism -provided by iocCore. -The following provide EPICS V4 shell commands:

-
-
pvAccessCPP
-
PVAClientRegister.dbd and PVAServerRegister.dbd
-
pvaSrv
-
dbPv.dbd
-
pvDatabaseCPP
-
registerChannelProviderLocal.dbd
-
-

-Look at exampleServer/ioc/src/exampleServerInclude.dbd for an example -of how an application can make the shell commands available. -

-

Commands From pvAccessCPP

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider

-
stopPVAServer
-
Stop the local channel provider
-
-

-

Commands implemented by pvDatabaseCPP

-

The following iocsh commands are provided for a V3IOC:

-
-
pvdbl
-
Provides a list of all the pvRecords in database master -
-
-

In addition any code that implements a PVRecord must implement an ioc command.

-

Look at any of the examples to see how to implement shell commands.

-

Commands implemented by pvaSrv

-

pvaSrv provides a pvAccess server that provides access to iocCore records.

-

pvaSrv does not provide any shell commands but it can be part of an IOC. -Just make sure your application configures pvaSrv and then include the following file: -

-include "dbPv.dbd"
-
-

- -

database

-

src/database

-

This Directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
-

src/special

-

This directory has the following files:

-
-
recordList.h
-
This implements a PVRecord that provides a list of the names - of the records in the PVDatabase. - It also serves as an example of how to implement a service. - The exampleDatabase creates an instance via the following code: -
-recordName = "laptoprecordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-if(pvRecord==NULL) {
-      cout << "RecordListRecord::create failed" << endl;
-} else {
-    result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-}
-      
-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

pvDatabase.h

-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that accesses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class RecordPutRequester;
-typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES: -

    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
-
PVRecord Methods -
-class PVRecord
-     public epics::pvData::Requester,
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() {initPVRecord(); return true;}
-    virtual void start() {}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    std::string getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
start
-
Virtual method. - Optional method for derived class. - It is called before record is added to database. - The base method does nothing. -
-
process
-
Virtual method. - Derived classes usually implement this method. - It implements the semantics for the record. - The base implementation does nothing. -
-
destroy
-
Virtual method. - Optional method for derived class. - If the derived class implements this it - must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it's own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If false client can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    std::string getFullFieldName();
-    std::string getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordStructurePtr const & parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(std::string const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-    virtual std::string getRequesterName();
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
getRequesterName
-
Virtual method of Requester
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is accessed by the server side of remote pvAccess. -It uses the copy and monitor facilities from pvDataCPP and connects -them to a PVRecord. -

-

The implementation is a complete implementation of channelProvider -and channel except for channelRPC, which is implement by pvAccess as a separate -channel provider.

-

The following provides a brief description of each channel method that -is implemented.

-

channelProcessLocal

-

Needs to be described.

-

channelGetLocal

-

Needs to be described.

-

channelPutLocal

-

Needs to be described.

-

channelPutGetLocal

-

Needs to be described.

-

channelArrayLocal

-

Needs to be described.

-

MonitorLocal

-

This is the code that implements monitors on changes to fields of a PVRecord. -Because it is called by pvAccess client (monitor methods) and by -PVRecord (when postPut is called), it must be careful to prevent deadlocks. -The implementation is via class MonitorLocal (implemented in monitorFactory.cpp) -and PVCopyMonitor. -MonitorLocal is the interface between pvAccess and PVCopyMonitor. -PVCopyMonitor is the interface between MonitorLocal and PVRecord. -MonitorLocal manages a MonitorElement queue. -While monitoring is active (between start and stop) it keeps an active element -for use by PVCopyMonitor. -While monitoring is active PVCopyMonitor updates the active monitor element whenever -a postPut is issued to any field being monitored. -

-

The following two sections provide a few more details about MonitorLocal -and PVCopyMonitor.

-

MonitorLocal

-

MonitorLocal implements the following abstract base classes:

-
-
Monitor
-
This is described by pvDataCPP. - It has methods start, stop, poll, and release. - These methods are called by the pvAccess client -
-
PVCopyMonitorRequester
-
This has methods releaseActiveElement and unlisten. - These methods are called by PVCopyMonitor. -
-
-MonitorLocal manages the following: -
-
MonitorElementQueue
-
This is a queue of monitor elements. - A Queue is implemented by pvDataCPP and used by MonitorLocal. - It is a finite queue. - A monitor element is described by pvDataCPP. - It has fields pvStructure, changedBitSet, and overrunBitSet. - The pvStructure holds data for a subset of the fields in a PVRecord. - The changedBitSet and overrunBitSet describe changes between - monitor event. - MonitorLocal creates an instance of PVCopy (implemented by pvDataCPP), - which manages the interaction between the set of fields being - monitored and the fields in the top level PVStructure of the PVRecord. - pvCopy is also used to create the pvStructure for each monitor element. -
-
activeElement
-
Whenever monitoring is active monitorLocal - keeps an active element for use by pvCopyMonitor. - It changes the active element based on calls to poll (by the - client) and calls to releaseActiveElement (by pvCopyMonitor). - If there are no free element when releaseActiveElement is - called the current active element is returned. - If a free element is available the client is notified that a new - monitor element is available and the free element becomes the - active element. -
-
-

A brief description on each method in MonitorLocal is:

-
-
start
-
- Called by client. - With a lock held it clears the monitorElement queue - and allocates an active element. - With no lock held calls pvCopyMonitor->startMonitoring(activeElement) -
-
stop
-
- Called by client. - With no lock held calls pvCopyMonitor->stopMonitoring(activeElement) -
-
poll
-
- Called by client. - With a lock held it calls queue->getUsed(); -
-
release
-
- Called by client. - With a lock held it calls queue->releaseUsed(); -
-
releaseActiveElement
-
- Called by PVCopyMonitor with no locks held. - With a lock held it tries to get a new free element. - If it can't it just returns the current active element. - Otherwise it does the following. - Using the activeElement it updates the pvStructure - and compresses the changed and overrun bitSet. - It then calls queue->setUsed(activeElement); - It then sets the active element to the new free element. - With no lock held it calls monitorRequester->monitorEvent(getPtrSelf()) - and finally returns the new active element, -
-
unlisten
-
- With no lock held it calls monitorRequester->unlisten(getPtrSelf()); -
-
-

PVCopyMonitor

-

-pvCopyMonitor is the code that manages changes to -fields in the record. -It is called by PVRecord whenever a postPut is issued to a field. -pvCopyMonitor uses the active monitor element provided by monitorFactory. -Note that this method is called with the record locked. -It only modifies the changedBitSet and overrunBitSet of the -active element but never modifies the pvStructure. -

-

A brief description of the pvCopyMonitor methods is:

-
-
startMonitoring
-
With no lock held it sets its monitorElement to the - startElement passed by monitorLocal and calls pvRecord->addListener(getPtrSelf()). - It locks the pvRecord. - It calls calls addListener for every field in the record that is being - monitored. - It clears the overrun and changed bit sets. - It sets bit 0 of the changed bit set and calls - pvCopyMonitorRequester->releaseActiveElement(); - Thus the client will get the initial values for every field being monitored. - The record is unlocked and the method returns to the caller. -
-
stopMonitoring
-
- With no lock held it calls pvRecord->removeListener(getPtrSelf()); -
-
dataPut
-
- This is called because of a call to postPut. - It is called with the record locked. - It updates the changed and overrun bitSets. - It isGroupPut is false it calls - pvCopyMonitorRequester->releaseActiveElement(). - Otherwise it sets dataChanged true. -
-
beginGroupPut
-
- With a lock held it - sets isGroupPut true and dataChanged false. -
-
endGroupPut
-
- With a lock held it sets isGroupPut false. - With no lock held and dataChanged true it calls - pvCopyMonitorRequester->releaseActiveElement() -
-
unlisten
-
- Just calls pvCopyMonitorRequester->unlisten(); -
-
- -

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure argument
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generated - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

-

recordList

-

This implements a PVRecord that allows a client to -get the names of all the PVRecords in the PVDatabase. -It follows the pattern of a channelPutGet -record: -

-traceRecord
-    structure argument
-        string database master
-        string regularExpression .*
-    structure result
-        string status 
-        string[] name
-
-where: -
-
database
-
The name of the database. The default is "master"
-
regularExpression
-
For now this is ignored and the complete list of names is always - returned.
-
status
-
The status of a putGet request.
-
name
-
The array of record names.
-
-

Note that swtshell, which is a Java GUI tool, has a command channelList that -requires that a record of this type is present and calls it. -Thus user code does not have to use a channelGetPut to get the list -of record names.

-

testExampleServerMain.cpp has an example of how to create a traceRecord: -

-recordName = "recordListPGRPC";
-pvRecord = RecordListRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
-

- -

exampleServer

-

Overview

-

The example implements a simple service that has a top level pvStructure: -

-structure
-    structure argument
-        string value
-    structure result
-        string value
-        time_t timeStamp
-            long secondsPastEpoch
-            int nanoseconds
-            int userTag
-
-

It is designed to be accessed via a channelPutGet request. -The client sets argument.value -When the record processes it sets result.value to "Hello " -concatenated with argument.value. -Thus if the client sets argument.value equal to "World" -result.value will be "Hello World". -In addition the timeStamp is set to the time when process is called.

-

-The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleService
-mrk> bin/linux-x86_64/exampleService
-
-

Directory Layout

-

-The directory layout is: -

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerInclude.dbd
-       exampleServerMain.cpp
-       exampleServerRegister.cpp
-       exampleServerRegister.dbd
-    ioc
-       Db
-          ...
-       src
-          exampleServerInclude.dbd
-          exampleServerMain.cpp
-   iocBoot
-      exampleServer
-         st.cmd
-         ...
-
-where -
-
ExampleRELEASE.local
-
- If you make a copy of exampleServer and use it - to create a new server, - This is the file that must be copied to RELEASE.local - and edited.
-
exampleServer.h
-
The header file for the service.
-
exampleServer.cpp
-
The service implementation.
-
exampleServerMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. -
-
exampleServerInclude.dbd
-
This has a register command so that the service can be started - on a V3 IOC via iocsh. -
-
exampleServerRegister.cpp
-
This has the code to start the service via the following iocsh - command. -
exampleServerRegister.dbd
-
This is the file that is used to create the shell command - exampleServerCreateRecord.
-
-exampleServerCreateRecord exampleServer
-
- Multiple commands can be issued to create multiple service records. - -
ioc
-
This is for building a V3 IOC application.
-
ioc/Db
-
This has template files for creating V3 records.
-
ioc/src
-
The files for running a V3 IOC.
-
iocBoot/exampleServer
-
A place to start exampleServer as part of a V3IOC. - It has a st.cmd file that starts the ioc and also starts pvAccess - and the example.
-
-

If only a main program is desired then the directory layout is:

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerMain.cpp
-
-

Thus if only a main program is required the directory layout is simple.

-

Also many sites will want to build the src directory in an area -separate from where the iocs are build.

-

exampleServer.h

-

The example resides in src -The implementation is in exampleServer.cpp. -

-

The description consists of

-
-class ExampleServer;
-typedef std::tr1::shared_ptr<ExampleServer> ExampleServerPtr;
-
-class ExampleServer :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleServer);
-    static ExampleServerPtr create(
-        std::string const & recordName);
-    virtual ~ExampleServer();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleServer(std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-
-    epics::pvData::PVStringPtr pvArgumentValue;
-    epics::pvData::PVStringPtr pvResultValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleServer
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleServer
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleServerPtr ExampleServer::create(
-    std::string const & recordName)
-{
-    StandardFieldPtr standardField = getStandardField();
-    FieldCreatePtr fieldCreate = getFieldCreate();
-    PVDataCreatePtr pvDataCreate = getPVDataCreate();
-    StructureConstPtr  topStructure = fieldCreate->createFieldBuilder()->
-        addNestedStructure("argument")->
-            add("value",pvString)->
-            endNested()->
-        addNestedStructure("result") ->
-            add("value",pvString) ->
-            add("timeStamp",standardField->timeStamp()) ->
-            endNested()->
-        createStructure();
-    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
-
-    ExampleServerPtr pvRecord(
-        new ExampleServer(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleServerPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleServer::ExampleServer(
-    std::string const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleServer::~ExampleServer()
-{
-}
-
-void ExampleServer::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleServer::init()
-{
-    initPVRecord();
-    PVFieldPtr pvField;
-    pvArgumentValue = getPVStructure()->getStringField("argument.value");
-    if(pvArgumentValue.get()==NULL) return false;
-    pvResultValue = getPVStructure()->getStringField("result.value");
-    if(pvResultValue.get()==NULL) return false;
-    pvTimeStamp.attach(getPVStructure()->getSubField("result.timeStamp"));
-    return true;
-}
-
-

The implementation of process is:

-
-void ExampleServer::process()
-{
-    pvResultValue->put(String("Hello ") + pvArgumentValue->get());
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It gives a value to result.value and -then sets the timeStamp to the current time. -

src/exampleServerMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleServer");
-    PVRecordPtr pvRecord = ExampleServer::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    recordName = "traceRecordPGRPC";
-    pvRecord = TraceRecord::create(recordName);
-    result = master->r;addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    recordName = "recordListPGRPC";
-    pvRecord = RecordListRecord::create(recordName);
-    result = master->r;addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    ServerContext::shared_pointer pvaServer =
-        startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    PVStringArrayPtr pvNames = master->r;getRecordNames();
-    shared_vector<const string>r; names = pvNames->r;view();
-    for(size_t i=0; i<names.size(); ++i) cout << names[i] << endl;
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates record exampleServer
  • -
  • creates records traceRecordPGRPC and recordListPGRPC
  • -
  • lists all the records
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleServer

-

To start exampleServer as part of a V3IOC: -

-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../../bin/linux-x86_64/exampleServer st.cmd
-

-

You can then issue the commands dbl and pvdbl: -

-epics> dbl
-pvdouble
-pvcounter
-pvenum
-pvdoubleArray
-pvstringArray
-epics> pvdbl
-exampleServer
-epics> 
-
-dbl shows the V3 records. -pvdbl shows the pvRecords. -

-

-It starts pvaSrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

- -

exampleDatabase

-

The exampleServer pvDatabase has many records including the following:

-
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

exampleDatabase can be started as a main program or as a V3 IOIC. -If started as a V3 IOC it also has a number of V3 records, -and starts pvaSrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

-

exampleLink

-

This example show how a service can access other PVRecords. -This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - cause deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all locking issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transferred between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelProvider::shared_pointer provider =
-     getChannelProviderRegistry()->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
- -

Directory Layout

-
-exampleLink
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleLink.h
-       exampleLink.cpp
-       exampleLinkInclude.dbd
-       exampleLinkRegister.cpp
-    ioc
-       Db
-       src
-          exampleLinkInclude.dbd
-          exampleLinkMain.cpp
-   iocBoot
-      exampleLink
-         st.local
-         st.remote
-         ...
-
-

This example is only built to be run as part of a V3 IOC. -Note that two startup files are available: st.local and st.remote. -st.local has two records: doubleArray and exampleLink. -doubleArray is a record that can be changed via a call to pvput. -exampleLink is a record that, when processed, gets the value from doubleArray and sets its value equal -to the value read. -st.local has both records. -st.remote has only one record named exampleLinkRemote. -

-

To start the example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleLink/iocBoot/exampleLink
-mrk> ../../bin/linux-x86_64/exampleLink st.local 
-
-

then in another window:

-
-mrk> pvput doubleArray 4 100 200 300 400
-Old : doubleArray 0
-New : doubleArray 4 100 200 300 400
-mrk> pvget -r "record[process=true]field(value)" exampleLink
-exampleLink
-structure 
-    double[] value [100,200,300,400]
-mrk> 
-
-

exampleLink Implementation

-

exampleLink.h contains the following:

-
-...
-class ExampleLink :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExampleLink);
-    static ExampleLinkPtr create(
-        std::string const & recordName,
-        std::string const & providerName,
-        std::string const & channelName
-        );
-    virtual ~ExampleLink() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExampleLinkPtr ExampleLink::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExampleLinkPtr pvRecord(
-        new ExampleLink(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExampleLink instance, -and then calls the init method and the returns a ExampleLinkPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExampleLink::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

examplePowerSupply

-

This is an example of creating a service that requires a somewhat complicated -top level PVStructure. -It is similar to the powerSupply example that is provided with pvIOCJava. -Look at the code for details. -

-

Array Performance and Memory Example

-

This section describes main programs that demonstrate performance -of large arrays and can also be used to check for memory leaks. -Checking for memory leaks can be accomplished by running the programs with valgrind -or some other memory check program. -

-

Brief Summary

-

The programs are:

-
-
arrayPerformanceMain
-
This is server and also a configurable number of longArrayMonitor clients. - The clients can use either the local or - remote providers. The monitor code is the same code that is used by longArrayMonitorMain. -
-
longArrayMonitorMain
-
Remote client that monitors the array served by arrayPerformanceMain.
-
longArrayGetMain
-
Remote client that uses channelGet to access the array served by arrayPerformanceMain.
-
longArrayPutMain
-
Remote client that uses channelPut to access the array served by arrayPerformanceMain.
-
-

Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
-default
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0.0
-
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName queueSize waitTime
-default
-longArrayMonitorMain arrayPerformance 2 0.0
-
-mrk> bin/linux-x86_64/longArrayGetMain -help
-longArrayGetMain channelName iterBetweenCreateChannel iterBetweenCreateChannelGet delayTime
-default
-longArrayGetMain arrayPerformance 0 0 1
-
-mrk> bin/linux-x86_64/longArrayPutMain -help
-longArrayPutMain channelName arraySize iterBetweenCreateChannel iterBetweenCreateChannelPut delayTime
-default
-longArrayPutMain arrayPerformance 10 0 0 1
-
-mrk> 
-
-

Example output

-

Note: These may fail if run on a platform that does not have sufficient memory,

-

To see an example just execute the following commands in four different terminal windows:

-
-bin/linux/<arch>/arrayPerformanceMain
-bin/linux/<arch>/longArrayMonitorMain
-bin/linux/<arch>/longArrayGetMain
-bin/linux/<arch>/longArrayPutMain
-
-

Each program generates a report every second when it has something to report. -Examples are: -

-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0
-...
- monitors/sec 66 first 131 last 131 changed {1, 2} overrun {} megaElements/sec 656.999
-arrayPerformance value 132 time 1.00486 Iterations/sec 65.681 megaElements/sec 656.81
- monitors/sec 66 first 197 last 197 changed {1, 2} overrun {} megaElements/sec 656.304
-arrayPerformance value 198 time 1.00563 Iterations/sec 65.6307 megaElements/sec 656.307
- monitors/sec 66 first 263 last 263 changed {1, 2} overrun {} megaElements/sec 654.824
-...
-
-
-mrk> bin/linux-x86_64/longArrayMonitorMain
-longArrayMonitorMain arrayPerformance 2 0
-...
- monitors/sec 6 first 2357 last 2357 changed {1, 2} overrun {} megaElements/sec 68.6406
- monitors/sec 13 first 2385 last 2385 changed {1, 2} overrun {} megaElements/sec 118.72
- monitors/sec 9 first 2418 last 2418 changed {1, 2} overrun {1, 2} megaElements/sec 85.0984
-...
-
-
-mrk> bin/linux-x86_64/longArrayPutMain
-longArrayPutMain arrayPerformance 10 0 0 1
-...
-put numChannelPut 0 time 1.00148 Elements/sec 79.8819
-put numChannelPut 1 time 1.00176 Elements/sec 79.8598
-...
-
-
-mrk> bin/linux-x86_64/longArrayGetMain
-longArrayGetMain arrayPerformance 0 0 1
-...
-get kiloElements/sec 7384.61
-get kiloElements/sec 8726.34
-...
-
- -

arrayPerformance

-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
queueSize
-
The queueSize for the element queue. - A value less than 1 will become 1. -
-
waitTime
-
The time that longArrayMonitor will sleep after poll returns a monitorElement.
-
-

-arrayPerformance creates a PVRecord that has the structure:. -

-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers.

-

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
Once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the iteration count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report every second showing how many elements has received. -For every monitor it also checks that the number of elements is >0 and the -the first element equals the last element. It reports an error if either -of these conditions is not true.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
queueSize
-
The queueSize. Note that any size <2 is made 2.
-
waitTime
-
The time to wait after a poll request returns a monitorElement. - This can be used to force an overrun of the client even if there is no - overrun on the server.
-
-

longArrayGet

-

This is a pvAccess client that uses channelGet to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayGetMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelGet
-
The number of iterations between destroying and recreating the channelGet. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to delay between gets.
-
-

longArrayPut

-

This is a pvAccess client that uses channelPut to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayPutMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
arraySize
-
The capacity and length of the array to put to the server.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelPut
-
The number of iterations between destroying and recreating the channelPut. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to delay between gets.
-
- -

Some results

-

array performance

-

The results were from my laptop. -It has a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system is linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 10 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 50 iterations per second -and is putting about 500million elements per second. -Since each element is an int64 this means about 4gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-

-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 25 iterations per second and 250 million elements per second. -The next section has an example that demonstrates what happens. -Note that if the array size is small enough to fit in the local cache then running longArrayMonitor -has almost no effect of arrayPerforance. -

-

memory leaks

-

Running longArrayMonitorMain, longArrayPutMain, and longArrayGetMain -under valgrind shows no memory leaks.

-

arrayPerformanceMain shows the following:

-
-==9125== LEAK SUMMARY:
-==9125==    definitely lost: 0 bytes in 0 blocks
-==9125==    indirectly lost: 0 bytes in 0 blocks
-==9125==      possibly lost: 576 bytes in 2 blocks
-
-

The possibly leaked is either 1 or 2 blocks. -It seems to be the same if clients are connected. -

-

Vector Performance

-

This example demonstrates how array size effects performance. -The example is run as:

-
-bin/linux-x86_64/vectorPerformanceMain -help
-vectorPerformanceMain size delay nThread
-default
-vectorPerformance 50000000 0.01 1 
-
-

Consider the following:

-
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
-...
-thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
-thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
-thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
-...
-thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
-thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
-thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
-thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
-thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
-thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
-thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
-thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
-thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
-thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
-thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
-thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
-thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
-thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
-thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
-thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
-thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
-thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
-
-

As more threads are running the slower each thread runs.

-

But now consider a size that fits in a local cache.

-

-bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
-...
-thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
-thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
-thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
-... 
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
-...
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-...
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
-...
-thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
-thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
-thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
-thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
-thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
-thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
-
-

Now the number of threads has a far smaller effect on the performance of each thread. -

- -
- - diff --git a/documentation/pvDatabaseCPP_20151002.html b/documentation/pvDatabaseCPP_20151002.html deleted file mode 100644 index c151993..0000000 --- a/documentation/pvDatabaseCPP_20151002.html +++ /dev/null @@ -1,2052 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 02-October-2015

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20151002.html -
-
Previous version:
-
pvDatabaseCPP20140811.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessible database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extension must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 02-October-2015 version of of pvDatabaseCPP.

-

This version is a complete implementation of what is described in this manual. -

-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

The main purpose of this project to make it easier to implement services that are accessed via pvAccess. -This project supplies a complete implementation of the server side of pvAccess. -All that a service has to provide is a top level PVStructure and a process method. -A service can be run as a main process or can be part of a V3 IOC. -Thus services can be developed that interact with V3 records, asynDriver, -areaDetector, etc. -

- -

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features:

-
-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. -
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In the later case the IOC has both a database of V3 Records - and a pvDatabase.
-
-

Base classes make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resources used by the implementation.
-
-

Doxygen documentation is available at doxygenDoc

-

Getting started

-

The first step is to build pvDatabaseCPP as described in the next section.

-

One of the examples is exampleServer. -It can be started either via a main program or as part of a V3 IOC. -

-

To start it as a main program do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> bin/linux-x86_64/exampleServerMain 
-
-

You should see something like the following:

-
-result of addRecord exampleServer 1
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-exampleServer
-Type exit to stop: 
-
-

Then in another window execute a pvput and pvget as follows:

-
-mrk> pvput -r "field(argument.value)" exampleServer World
-...
-mrk> pvget -r "record[process=true]field(result.value)" exampleServer
-exampleServer
-structure 
-    string value Hello World
-mrk> 
-
-

To run the example as part of a V3 IOC do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../bin/linux-x86_64/exampleServer st.cmd 
-
-

You will see the following:

-
-> envPaths
-epicsEnvSet("ARCH","linux-x86_64")
-epicsEnvSet("IOC","exampleServer")
-epicsEnvSet("TOP","/home/hg/pvDatabaseCPP/exampleServer")
-epicsEnvSet("EPICS_BASE","/home/install/epics/base")
-epicsEnvSet("EPICSV4HOME","/home/hg")
-cd /home/hg/pvDatabaseCPP/exampleServer
-## Register all support components
-dbLoadDatabase("dbd/exampleServer.dbd")
-exampleServer_registerRecordDeviceDriver(pdbbase)
-## Load record instances
-dbLoadRecords("db/dbScalar.db","name=pvdouble,type=ao")
-dbLoadRecords("db/dbArray.db","name=pvdoubleArray,type=DOUBLE")
-dbLoadRecords("db/dbStringArray.db","name=pvstringArray")
-dbLoadRecords("db/dbEnum.db","name=pvenum")
-dbLoadRecords("db/dbCounter.db","name=pvcounter");
-cd /home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-iocInit()
-Starting iocInit
-############################################################################
-## EPICS R3.14.12.3 $Date: Mon 2012-12-17 14:11:47 -0600$
-## EPICS Base built Dec 21 2013
-############################################################################
-iocRun: All initialization complete
-dbl
-pvdouble
-pvcounter
-pvenum
-pvdoubleArray
-pvstringArray
-epicsThreadSleep(1.0)
-exampleServerCreateRecord pvaServer
-startPVAServer
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : dbPv local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-pvdbl
-pvaServer
-epics> 
-
-

Just like previously you can then execute a pvput and pvget and see Hello World. -

-

The examples, i. e. exampleServer, exampleLink, examplePowerSupply, -and exampleDatabase, are described in separate sections below. -In addition arrayPerformance can be used to measure that performance of big -arrays. It is also described in a later section.

-

Reading section exampleServer and looking at it's code is a good way -to learn how to implement a service.

- -

Relationship with pvIOCJava.

-

This document describes a C++ implementation of some of the components in pvIOCJava, -which also implements a pvDatabase. -PVDatabaseCPP implements the core components required to create a network accessible database of smart -memory resident records. -pvDatabaseCPP does not implement any of the specialized support that pvIOCJava -provides. -It is expected that many services will be created that do not require the full features provided -by pvIOCJava. In the future pvIOCJava should be split into multiple projects with one of -them named pvDatabaseJava.

-

Similar to epics base, pvIOCJava implements the concept of synchronous and asynchronous record processing. -For pvDatabaseCPP the process method is allowed to block. -Until a need is demonstrated this will remain true. -The main user of a pvDatabase is pvAccess, and in particular, remote pvAccess. -The server side of remote pvAccess creates two threads for each client and always accesses -a record via these threads. -It is expected that these threads will be sufficient to efficiently handle all channel methods except -channelRPC. For channelRPC pvAccess provides (or will provide) a thread pool for channelRPC requests. -If, in the future, a scanning facility is provided by pvDatabaseCPP or some other facility, -then the scanning facility will have to provide some way of handling process requests that block.

-

Phased Development

-

This documentation describes the first phase of a phased implementation -of pvDatabaseCPP:

-
-
pvRecord
-
Wrapper on PVStructure that implements methods required by Local Channel Provider.
-
pvDatabase
-
Database of PVRecords. Has methods find, add, and remove.
-
Local Channel Provider
-
Complete implementation of ChannelProvider and Channel. - This means that pvCopy and monitor are also implemented.
-
-

Future phases of pvDatabaseCPP might include:

-
-
Install
-
This provides complete support for on-line add and delete - of sets of records. - With the first phase each "service" is responsible for it's own implementation. - All that is provided is addRecord and removeRecord. -
-
Field support
-
Add the ability to optionally add support to fields. - In addition some of the basic support defined in pvIOCJava could also be implemented.
-
XML parser
-
This provides the ability to create record instances without - writing any code.
-
-

The completion of each phase provides useful features that can be used -without waiting for the completion of later phases. -The rest of this document discusses only the first phase.

-

Features Required for localChannelProvider

-
-
copy and monitor
-
pvDataCPP provides facilities copy and monitor. - This facilities allow a client to access an arbitrary subset - of the fields in the top level structure associated with a channel, - and to monitor changes in the top level structure. - pvDatabaseCPP uses what pvDataCPP provides and has code that - associates these facilities with a PVRecord. -
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
This is the C++ implementation of package pvAccess in pvIOCJava. - The localChannelProvider accesses data from PVRecords. - It implements all channel methods except channelRPC, - which is implemented by pvAccessCPP. -
-
-

Minimum Features Required for pvRecord

-

The first phase will only implement record processing, i. e. -the process method has to do everything itself without any generic field support. -This will be sufficient for implementing many services. -The following are the minimum features required

-
-
PVDatabase
-
This holds a set of PVRecords. - It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed. -
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure - is modified. - It can do this on a field by field basis.
-
-
-
-

The following sections describes the classes required for the first phase.

- -

Building pvDatabaseCPP

-

To build pvDatabaseCPP You must provide a file RELEASE.local -in directory configure. -Thus do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product pvDatabaseCPP requires. -Than at the top level just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> make
-
-

This builds pvDatabaseCPP and also the tests and all examples.

-

Each example and arrayPerformance is a completely separate top, -but is also built when make is run in pvDatabaseCPP itself.

-

-Each is a separate top for the following reasons:

-
    -
  1. - It is easier to understand each example including how it is built so that - it can be run as a main or as part of a V3 IOC. -
  2. -
  3. - Each example can be copied somewhere else and used as the basis - for creating a new service. -
  4. -
-

-If it is desired to build an example all by itself, -just follow the same instructions as for building pvDatabaseCPP itself. -For example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product the example requires. -Than at the top level of the example just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> make
-
-

This builds the example.

- -

iocshell commands

-

Shell commands are made available via the standard DBD include mechanism -provided by iocCore. -The following provide EPICS V4 shell commands:

-
-
pvAccessCPP
-
PVAClientRegister.dbd and PVAServerRegister.dbd
-
pvaSrv
-
dbPv.dbd
-
pvDatabaseCPP
-
registerChannelProviderLocal.dbd
-
-

-Look at exampleServer/ioc/src/exampleServerInclude.dbd for an example -of how an application can make the shell commands available. -

-

Commands From pvAccessCPP

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider
-
stopPVAServer
-
Stop the local channel provider
-
- -

Commands implemented by pvDatabaseCPP

-

The following iocsh commands are provided for a V3IOC:

-
-
pvdbl
-
Provides a list of all the pvRecords in database master -
-
-

In addition any code that implements a PVRecord must implement an ioc command. -Look at any of the examples to see how to implement shell commands.

-

Commands implemented by pvaSrv

-

pvaSrv provides a pvAccess server that provides access to iocCore records.

-

pvaSrv does not provide any shell commands but it can be part of an IOC. -Just make sure your application configures pvaSrv and then include the following file: -

-
-include "dbPv.dbd"
-
- -

database

-

src/database

-

This Directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
-

src/special

-

This directory has the following files:

-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

pvDatabase.h

-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that accesses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES:

-
    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
  • -
-

PVRecord Methods

-
-class PVRecord
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() ;
-    virtual void start() {}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    std::string getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
start
-
Virtual method. - Optional method for derived class. - It is called before record is added to database. - The base method does nothing. -
-
process
-
Virtual method. - Derived classes usually implement this method. - It implements the semantics for the record. - The base implementation does nothing. -
-
destroy
-
Virtual method. - Optional method for derived class. - If the derived class implements this it - must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it's own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If false client can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    std::string getFullFieldName();
-    std::string getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordStructurePtr const & parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(std::string const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is accessed by the server side of remote pvAccess. -It uses the copy and monitor facilities from pvDataCPP and connects -them to a PVRecord. -

-

The implementation is a complete implementation of channelProvider -and channel except for channelRPC, which is implement by pvAccess as a separate -channel provider.

-

The following provides a brief description of each channel method that -is implemented.

-

channelProcessLocal

-

Needs to be described.

-

channelGetLocal

-

Needs to be described.

-

channelPutLocal

-

Needs to be described.

-

channelPutGetLocal

-

Needs to be described.

-

channelArrayLocal

-

Needs to be described.

-

MonitorLocal

-

This is the code that implements monitors on changes to fields of a PVRecord. -Because it is called by pvAccess client (monitor methods) and by -PVRecord (when postPut is called), it must be careful to prevent deadlocks. -The implementation is via class MonitorLocal (implemented in monitorFactory.cpp) -and PVCopyMonitor. -MonitorLocal is the interface between pvAccess and PVCopyMonitor. -PVCopyMonitor is the interface between MonitorLocal and PVRecord. -MonitorLocal manages a MonitorElement queue. -While monitoring is active (between start and stop) it keeps an active element -for use by PVCopyMonitor. -While monitoring is active PVCopyMonitor updates the active monitor element whenever -a postPut is issued to any field being monitored. -

-

The following two sections provide a few more details about MonitorLocal -and PVCopyMonitor.

-

MonitorLocal

-

MonitorLocal implements the following abstract base classes:

-
-
Monitor
-
This is described by pvDataCPP. - It has methods start, stop, poll, and release. - These methods are called by the pvAccess client -
-
PVCopyMonitorRequester
-
This has methods releaseActiveElement and unlisten. - These methods are called by PVCopyMonitor. -
-
-MonitorLocal manages the following: -
-
MonitorElementQueue
-
This is a queue of monitor elements. - A Queue is implemented by pvDataCPP and used by MonitorLocal. - It is a finite queue. - A monitor element is described by pvDataCPP. - It has fields pvStructure, changedBitSet, and overrunBitSet. - The pvStructure holds data for a subset of the fields in a PVRecord. - The changedBitSet and overrunBitSet describe changes between - monitor event. - MonitorLocal creates an instance of PVCopy (implemented by pvDataCPP), - which manages the interaction between the set of fields being - monitored and the fields in the top level PVStructure of the PVRecord. - pvCopy is also used to create the pvStructure for each monitor element. -
-
activeElement
-
Whenever monitoring is active monitorLocal - keeps an active element for use by pvCopyMonitor. - It changes the active element based on calls to poll (by the - client) and calls to releaseActiveElement (by pvCopyMonitor). - If there are no free element when releaseActiveElement is - called the current active element is returned. - If a free element is available the client is notified that a new - monitor element is available and the free element becomes the - active element. -
-
-

A brief description on each method in MonitorLocal is:

-
-
start
-
- Called by client. - With a lock held it clears the monitorElement queue - and allocates an active element. - With no lock held calls pvCopyMonitor->startMonitoring(activeElement) -
-
stop
-
- Called by client. - With no lock held calls pvCopyMonitor->stopMonitoring(activeElement) -
-
poll
-
- Called by client. - With a lock held it calls queue->getUsed(); -
-
release
-
- Called by client. - With a lock held it calls queue->releaseUsed(); -
-
releaseActiveElement
-
- Called by PVCopyMonitor with no locks held. - With a lock held it tries to get a new free element. - If it can't it just returns the current active element. - Otherwise it does the following. - Using the activeElement it updates the pvStructure - and compresses the changed and overrun bitSet. - It then calls queue->setUsed(activeElement); - It then sets the active element to the new free element. - With no lock held it calls monitorRequester->monitorEvent(getPtrSelf()) - and finally returns the new active element, -
-
unlisten
-
- With no lock held it calls monitorRequester->unlisten(getPtrSelf()); -
-
-

PVCopyMonitor

-

-pvCopyMonitor is the code that manages changes to -fields in the record. -It is called by PVRecord whenever a postPut is issued to a field. -pvCopyMonitor uses the active monitor element provided by monitorFactory. -Note that this method is called with the record locked. -It only modifies the changedBitSet and overrunBitSet of the -active element but never modifies the pvStructure. -

-

A brief description of the pvCopyMonitor methods is:

-
-
startMonitoring
-
With no lock held it sets its monitorElement to the - startElement passed by monitorLocal and calls pvRecord->addListener(getPtrSelf()). - It locks the pvRecord. - It calls calls addListener for every field in the record that is being - monitored. - It clears the overrun and changed bit sets. - It sets bit 0 of the changed bit set and calls - pvCopyMonitorRequester->releaseActiveElement(); - Thus the client will get the initial values for every field being monitored. - The record is unlocked and the method returns to the caller. -
-
stopMonitoring
-
- With no lock held it calls pvRecord->removeListener(getPtrSelf()); -
-
dataPut
-
- This is called because of a call to postPut. - It is called with the record locked. - It updates the changed and overrun bitSets. - It isGroupPut is false it calls - pvCopyMonitorRequester->releaseActiveElement(). - Otherwise it sets dataChanged true. -
-
beginGroupPut
-
- With a lock held it - sets isGroupPut true and dataChanged false. -
-
endGroupPut
-
- With a lock held it sets isGroupPut false. - With no lock held and dataChanged true it calls - pvCopyMonitorRequester->releaseActiveElement() -
-
unlisten
-
- Just calls pvCopyMonitorRequester->unlisten(); -
-
- -

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record:

-
-traceRecord
-    structure argument
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generated - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord:

-
-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
- -

exampleServer

-

Overview

-

The example implements a simple service that has a top level pvStructure: -

-
-structure
-    structure argument
-        string value
-    structure result
-        string value
-        time_t timeStamp
-            long secondsPastEpoch
-            int nanoseconds
-            int userTag
-
-

It is designed to be accessed via a channelPutGet request. -The client sets argument.value -When the record processes it sets result.value to "Hello " -concatenated with argument.value. -Thus if the client sets argument.value equal to "World" -result.value will be "Hello World". -In addition the timeStamp is set to the time when process is called.

-

-The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleService
-mrk> bin/linux-x86_64/exampleService
-
-

Directory Layout

-

-The directory layout is: -

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerInclude.dbd
-       exampleServerMain.cpp
-       exampleServerRegister.cpp
-       exampleServerRegister.dbd
-    ioc
-       Db
-          ...
-       src
-          exampleServerInclude.dbd
-          exampleServerMain.cpp
-   iocBoot
-      exampleServer
-         st.cmd
-         ...
-
-where -
-
ExampleRELEASE.local
-
- If you make a copy of exampleServer and use it - to create a new server, - This is the file that must be copied to RELEASE.local - and edited.
-
exampleServer.h
-
The header file for the service.
-
exampleServer.cpp
-
The service implementation.
-
exampleServerMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. -
-
exampleServerInclude.dbd
-
This has a register command so that the service can be started - on a V3 IOC via iocsh. -
-
exampleServerRegister.cpp
-
This has the code to start the service via the following iocsh - command. -
-
exampleServerRegister.dbd
-
This is the file that is used to create the shell command - exampleServerCreateRecord. -
-exampleServerCreateRecord exampleServer
-
- Multiple commands can be issued to create multiple service records. -
-
ioc
-
This is for building a V3 IOC application.
-
ioc/Db
-
This has template files for creating V3 records.
-
ioc/src
-
The files for running a V3 IOC.
-
iocBoot/exampleServer
-
A place to start exampleServer as part of a V3IOC. - It has a st.cmd file that starts the ioc and also starts pvAccess - and the example.
-
-

If only a main program is desired then the directory layout is:

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerMain.cpp
-
-

Thus if only a main program is required the directory layout is simple.

-

Also many sites will want to build the src directory in an area -separate from where the iocs are build.

-

exampleServer.h

-

The example resides in src -The implementation is in exampleServer.cpp. -

-

The description consists of

-
-class ExampleServer;
-typedef std::tr1::shared_ptr<ExampleServer> ExampleServerPtr;
-
-class ExampleServer :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleServer);
-    static ExampleServerPtr create(
-        std::string const & recordName);
-    virtual ~ExampleServer();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleServer(std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-
-    epics::pvData::PVStringPtr pvArgumentValue;
-    epics::pvData::PVStringPtr pvResultValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleServer
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
-
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleServer
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleServerPtr ExampleServer::create(
-    std::string const & recordName)
-{
-    StandardFieldPtr standardField = getStandardField();
-    FieldCreatePtr fieldCreate = getFieldCreate();
-    PVDataCreatePtr pvDataCreate = getPVDataCreate();
-    StructureConstPtr  topStructure = fieldCreate->createFieldBuilder()->
-        addNestedStructure("argument")->
-            add("value",pvString)->
-            endNested()->
-        addNestedStructure("result") ->
-            add("value",pvString) ->
-            add("timeStamp",standardField->timeStamp()) ->
-            endNested()->
-        createStructure();
-    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
-
-    ExampleServerPtr pvRecord(
-        new ExampleServer(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleServerPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleServer::ExampleServer(
-    std::string const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleServer::~ExampleServer()
-{
-}
-
-void ExampleServer::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleServer::init()
-{
-    initPVRecord();
-    PVFieldPtr pvField;
-    pvArgumentValue = getPVStructure()->getStringField("argument.value");
-    if(pvArgumentValue.get()==NULL) return false;
-    pvResultValue = getPVStructure()->getStringField("result.value");
-    if(pvResultValue.get()==NULL) return false;
-    pvTimeStamp.attach(getPVStructure()->getSubField("result.timeStamp"));
-    return true;
-}
-
-

The implementation of process is:

-
-void ExampleServer::process()
-{
-    pvResultValue->put(String("Hello ") + pvArgumentValue->get());
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It gives a value to result.value and -then sets the timeStamp to the current time. -

src/exampleServerMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleServer");
-    PVRecordPtr pvRecord = ExampleServer::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    recordName = "traceRecordPGRPC";
-    pvRecord = TraceRecord::create(recordName);
-    result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    ServerContext::shared_pointer pvaServer =
-        startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    PVStringArrayPtr pvNames = master->getRecordNames();
-    shared_vector<const string> names = pvNames->view();
-    for(size_t i=0; i<names.size(); ++i) cout << names[i] << endl;
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates record exampleServer
  • -
  • creates record traceRecordPGRPC
  • -
  • lists all the records
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleServer

-

To start exampleServer as part of a V3IOC:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../../bin/linux-x86_64/exampleServer st.cmd
-
-

You can then issue the commands dbl and pvdbl:

-
-epics> dbl
-pvdouble
-pvcounter
-pvenum
-pvdoubleArray
-pvstringArray
-epics> pvdbl
-exampleServer
-epics> 
-
-dbl shows the V3 records. -pvdbl shows the pvRecords. -

-It starts pvaSrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

- -

exampleDatabase

-

The exampleServer pvDatabase has many records including the following:

-
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

exampleDatabase can be started as a main program or as a V3 IOIC. -If started as a V3 IOC it also has a number of V3 records, -and starts pvaSrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

-

exampleLink

-

This example show how a service can access other PVRecords. -This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - cause deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all locking issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transferred between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext =
-     startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelProvider::shared_pointer provider =
-     getChannelProviderRegistry()->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
- -

Directory Layout

-
-exampleLink
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleLink.h
-       exampleLink.cpp
-       exampleLinkInclude.dbd
-       exampleLinkRegister.cpp
-    ioc
-       Db
-       src
-          exampleLinkInclude.dbd
-          exampleLinkMain.cpp
-   iocBoot
-      exampleLink
-         st.local
-         st.remote
-         ...
-
-

This example is only built to be run as part of a V3 IOC. -Note that two startup files are available: st.local and st.remote. -st.local has two records: doubleArray and exampleLink. -doubleArray is a record that can be changed via a call to pvput. -exampleLink is a record that, when processed, gets the value from doubleArray and sets its value equal -to the value read. -st.local has both records. -st.remote has only one record named exampleLinkRemote. -

-

To start the example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleLink/iocBoot/exampleLink
-mrk> ../../bin/linux-x86_64/exampleLink st.local 
-
-

then in another window:

-
-mrk> pvput doubleArray 4 100 200 300 400
-Old : doubleArray 0
-New : doubleArray 4 100 200 300 400
-mrk> pvget -r "record[process=true]field(value)" exampleLink
-exampleLink
-structure 
-    double[] value [100,200,300,400]
-mrk> 
-
-

exampleLink Implementation

-

exampleLink.h contains the following:

-
-...
-class ExampleLink :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExampleLink);
-    static ExampleLinkPtr create(
-        std::string const & recordName,
-        std::string const & providerName,
-        std::string const & channelName
-        );
-    virtual ~ExampleLink() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExampleLinkPtr ExampleLink::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExampleLinkPtr pvRecord(
-        new ExampleLink(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExampleLink instance, -and then calls the init method and the returns a ExampleLinkPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExampleLink::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

examplePowerSupply

-

This is an example of creating a service that requires a somewhat complicated -top level PVStructure. -It is similar to the powerSupply example that is provided with pvIOCJava. -Look at the code for details. -

-

Array Performance and Memory Example

-

This section describes main programs that demonstrate performance -of large arrays and can also be used to check for memory leaks. -Checking for memory leaks can be accomplished by running the programs with valgrind -or some other memory check program. -

-

Brief Summary

-

The programs are:

-
-
arrayPerformanceMain
-
This is server and also a configurable number of longArrayMonitor clients. - The clients can use either the local or - remote providers. The monitor code is the same code that is used by longArrayMonitorMain. -
-
longArrayMonitorMain
-
Remote client that monitors the array served by arrayPerformanceMain.
-
longArrayGetMain
-
Remote client that uses channelGet to access the array served by arrayPerformanceMain.
-
longArrayPutMain
-
Remote client that uses channelPut to access the array served by arrayPerformanceMain.
-
-

Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
-default
-arrayPerformanceMain arrayPerformance 10000000 0.0001 local 1 2 0.0
-
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName queueSize waitTime
-default
-longArrayMonitorMain arrayPerformance 2 0.0
-
-mrk> bin/linux-x86_64/longArrayGetMain -help
-longArrayGetMain channelName iterBetweenCreateChannel iterBetweenCreateChannelGet delayTime
-default
-longArrayGetMain arrayPerformance 0 0 1
-
-mrk> bin/linux-x86_64/longArrayPutMain -help
-longArrayPutMain channelName arraySize iterBetweenCreateChannel iterBetweenCreateChannelPut delayTime
-default
-longArrayPutMain arrayPerformance 10 0 0 1
-
-mrk> 
-
-

Example output

-

Note: These may fail if run on a platform that does not have sufficient memory,

-

To see an example just execute the following commands in four different terminal windows:

-
-bin/linux/<arch>/arrayPerformanceMain
-bin/linux/<arch>/longArrayMonitorMain
-bin/linux/<arch>/longArrayGetMain
-bin/linux/<arch>/longArrayPutMain
-
-

Each program generates a report every second when it has something to report. -Examples are:

-
-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0
-...
- monitors/sec 66 first 131 last 131 changed {1, 2} overrun {} megaElements/sec 656.999
-arrayPerformance value 132 time 1.00486 Iterations/sec 65.681 megaElements/sec 656.81
- monitors/sec 66 first 197 last 197 changed {1, 2} overrun {} megaElements/sec 656.304
-arrayPerformance value 198 time 1.00563 Iterations/sec 65.6307 megaElements/sec 656.307
- monitors/sec 66 first 263 last 263 changed {1, 2} overrun {} megaElements/sec 654.824
-...
-
-
-mrk> bin/linux-x86_64/longArrayMonitorMain
-longArrayMonitorMain arrayPerformance 2 0
-...
- monitors/sec 6 first 2357 last 2357 changed {1, 2} overrun {} megaElements/sec 68.6406
- monitors/sec 13 first 2385 last 2385 changed {1, 2} overrun {} megaElements/sec 118.72
- monitors/sec 9 first 2418 last 2418 changed {1, 2} overrun {1, 2} megaElements/sec 85.0984
-...
-
-
-mrk> bin/linux-x86_64/longArrayPutMain
-longArrayPutMain arrayPerformance 10 0 0 1
-...
-put numChannelPut 0 time 1.00148 Elements/sec 79.8819
-put numChannelPut 1 time 1.00176 Elements/sec 79.8598
-...
-
-
-mrk> bin/linux-x86_64/longArrayGetMain
-longArrayGetMain arrayPerformance 0 0 1
-...
-get kiloElements/sec 7384.61
-get kiloElements/sec 8726.34
-...
-
- -

arrayPerformance

-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
queueSize
-
The queueSize for the element queue. - A value less than 1 will become 1. -
-
waitTime
-
The time that longArrayMonitor will sleep after poll returns a monitorElement.
-
-

-arrayPerformance creates a PVRecord that has the structure:. -

-
-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers. -

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
Once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the iteration count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report every second showing how many elements has received. -For every monitor it also checks that the number of elements is >0 and the -the first element equals the last element. It reports an error if either -of these conditions is not true.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
queueSize
-
The queueSize. Note that any size <2 is made 2.
-
waitTime
-
The time to wait after a poll request returns a monitorElement. - This can be used to force an overrun of the client even if there is no - overrun on the server.
-
-

longArrayGet

-

This is a pvAccess client that uses channelGet to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayGetMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelGet
-
The number of iterations between destroying and recreating the channelGet. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to delay between gets.
-
-

longArrayPut

-

This is a pvAccess client that uses channelPut to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayPutMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
arraySize
-
The capacity and length of the array to put to the server.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelPut
-
The number of iterations between destroying and recreating the channelPut. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to delay between gets.
-
- -

Some results

-

array performance

-

The results were from my laptop in 2013 -It had a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system was linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 10 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 50 iterations per second -and is putting about 500million elements per second. -Since each element is an int64 this means about 4gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-
-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 25 iterations per second and 250 million elements per second. -The next section has an example that demonstrates what happens. -Note that if the array size is small enough to fit in the local cache then running longArrayMonitor -has almost no effect of arrayPerforance. -

-

memory leaks

-

Running longArrayMonitorMain, longArrayPutMain, and longArrayGetMain -under valgrind shows no memory leaks.

-

arrayPerformanceMain shows the following:

-
-==9125== LEAK SUMMARY:
-==9125==    definitely lost: 0 bytes in 0 blocks
-==9125==    indirectly lost: 0 bytes in 0 blocks
-==9125==      possibly lost: 576 bytes in 2 blocks
-
-

The possibly leaked is either 1 or 2 blocks. -It seems to be the same if clients are connected. -

-

Vector Performance

-

This example demonstrates how array size effects performance. -The example is run as:

-
-bin/linux-x86_64/vectorPerformanceMain -help
-vectorPerformanceMain size delay nThread
-default
-vectorPerformance 50000000 0.01 1 
-
-

Consider the following:

-
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
-...
-thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
-thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
-thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
-...
-thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
-thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
-thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
-thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
-thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
-thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
-thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
-thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
-thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
-thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
-thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
-thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
-thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
-thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
-thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
-thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
-thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
-thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
-
-

As more threads are running the slower each thread runs.

-

But now consider a size that fits in a local cache.

-
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
-...
-thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
-thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
-thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
-... 
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
-...
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-...
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
-...
-thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
-thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
-thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
-thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
-thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
-thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
-
-

Now the number of threads has a far smaller effect on the performance of each thread. -

- -
- - diff --git a/documentation/pvDatabaseCPP_20151023.html b/documentation/pvDatabaseCPP_20151023.html deleted file mode 100644 index e84aff8..0000000 --- a/documentation/pvDatabaseCPP_20151023.html +++ /dev/null @@ -1,2004 +0,0 @@ - - - - - - pvDatabaseCPP - - - - - - - - - -
-

pvDatabaseCPP

- - -

EPICS v4 Working Group, Working Draft, 23-October-2015

-
-
Latest version:
-
pvDatabaseCPP.html -
-
This version:
-
pvDatabaseCPP20151023.html -
-
Previous version:
-
pvDatabaseCPP20151002.html -
-
Editors:
-
Marty Kraimer, BNL
-
- - -
- -

Abstract

- -

This document describes pvDatabaseCPP, -which is a framework for implementing a network accessible database of smart memory resident -records. Network access is via pvAccess. The data in each record is a top level PVStructure as defined by -pvData. The framework includes a complete implementation of ChannelProvider as defined by pvAccess. -The framework can be extended in order to create record instances that implements services. -The minimum that an extension must provide is a top level PVStructure and a process method. -

- -

EPICS version 4 is a set of related products in the EPICS -V4 control system programming environment:
-relatedDocumentsV4.html -

- - -

Status of this Document

- -

This is the 23-October-2015 version of of pvDatabaseCPP.

-

This version is a complete implementation of what is described in this manual. -

-
- -
-

Table of Contents

-
-
- - -

Introduction

-

Overview

-

The main purpose of this project to make it easier to implement services that are accessed via pvAccess. -This project supplies a complete implementation of the server side of pvAccess. -All that a service has to provide is a top level PVStructure and a process method. -A service can be run as a main process or can be part of a V3 IOC. -Thus services can be developed that interact with V3 records, asynDriver, -areaDetector, etc. -

- -

A brief description of a pvDatabase is that it is a set of network accessible, smart, -memory resident records. -Each record has data composed of a top level PVStructure. -Each record has a name which is the channelName for pvAccess. -A local Channel Provider implements the complete ChannelProvider and -Channel interfaces as defined by pvAccess. -The local provider provides access to the records in the pvDatabase. -This local provider is accessed by the remote pvAccess server. -A record is smart because code can be attached to a record, which is accessed via a method named process.

- -

This document describes components that provide the following features:

-
-
database
-
This encapsulates the concept of a database of memory resident smart records. - The two main components are: -
-
pvRecord
-
This encapsulates the concept of a smart record. It can be processed. - Changes to field values can be trapped. A record can be locked.
-
pvDatabase
-
This is a database of pvRecords. - Records can be added and removed from a database.
-
-
-
pvAccess
-
This is a complete implementation of ChannelProvider and Channel - as defined by pvAccess. - It is used by the server side of pvAccess to attach to pvRecords. -
-
Main and V3IOC
-
The pvDatabase can be provided via a Main program or can be part - of a V3IOC. In the later case the IOC has both a database of V3 Records - and a pvDatabase.
-
-

Base classes make it easy to create record instances. -The code attached to each record must create the top -level PVStructure and the following three methods:

-
-
init
-
This is a method for initializing the support. - It returns true if successful and false otherwise. -
-
process
-
This is what makes a record smart. -
-
destroy
-
This releases and resources used by the implementation.
-
-

Doxygen documentation is available at doxygenDoc

-

Getting started

-

The first step is to build pvDatabaseCPP as described in the next section.

-

One of the examples is exampleServer. -It can be started either via a main program or as part of a V3 IOC. -

-

To start it as a main program do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> bin/linux-x86_64/exampleServerMain 
-
-

You should see something like the following:

-
-result of addRecord exampleServer 1
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-exampleServer
-Type exit to stop: 
-
-

Then in another window execute a pvput and pvget as follows:

-
-mrk> pvput -r "field(argument.value)" exampleServer World
-...
-mrk> pvget -r "record[process=true]field(result.value)" exampleServer
-exampleServer
-structure 
-    string value Hello World
-mrk> 
-
-

To run the example as part of a V3 IOC do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../bin/linux-x86_64/exampleServer st.cmd 
-
-

You will see the following:

-
-> envPaths
-epicsEnvSet("ARCH","linux-x86_64")
-epicsEnvSet("IOC","exampleServer")
-epicsEnvSet("TOP","/home/hg/pvDatabaseCPP/exampleServer")
-epicsEnvSet("EPICS_BASE","/home/install/epics/base")
-epicsEnvSet("EPICSV4HOME","/home/hg")
-cd /home/hg/pvDatabaseCPP/exampleServer
-## Register all support components
-dbLoadDatabase("dbd/exampleServer.dbd")
-exampleServer_registerRecordDeviceDriver(pdbbase)
-## Load record instances
-dbLoadRecords("db/dbScalar.db","name=pvdouble,type=ao")
-dbLoadRecords("db/dbArray.db","name=pvdoubleArray,type=DOUBLE")
-dbLoadRecords("db/dbStringArray.db","name=pvstringArray")
-dbLoadRecords("db/dbEnum.db","name=pvenum")
-dbLoadRecords("db/dbCounter.db","name=pvcounter");
-cd /home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-iocInit()
-Starting iocInit
-############################################################################
-## EPICS R3.14.12.3 $Date: Mon 2012-12-17 14:11:47 -0600$
-## EPICS Base built Dec 21 2013
-############################################################################
-iocRun: All initialization complete
-dbl
-pvdouble
-pvcounter
-pvenum
-pvdoubleArray
-pvstringArray
-epicsThreadSleep(1.0)
-exampleServerCreateRecord pvaServer
-startPVAServer
-VERSION : pvAccess Server v3.0.5-SNAPSHOT
-PROVIDER_NAMES : dbPv local
-BEACON_ADDR_LIST : 
-AUTO_BEACON_ADDR_LIST : 1
-BEACON_PERIOD : 15
-BROADCAST_PORT : 5076
-SERVER_PORT : 5075
-RCV_BUFFER_SIZE : 16384
-IGNORE_ADDR_LIST: 
-STATE : INITIALIZED
-pvdbl
-pvaServer
-epics> 
-
-

Just like previously you can then execute a pvput and pvget and see Hello World. -

-

The examples, i. e. exampleServer, exampleLink, examplePowerSupply, -and exampleDatabase, are described in separate sections below. -In addition arrayPerformance can be used to measure that performance of big -arrays. It is also described in a later section.

-

Reading section exampleServer and looking at it's code is a good way -to learn how to implement a service.

- -

Features Required for localChannelProvider

-
-
copy and monitor
-
pvDataCPP provides facilities copy and monitor. - This facilities allow a client to access an arbitrary subset - of the fields in the top level structure associated with a channel, - and to monitor changes in the top level structure. - pvDatabaseCPP uses what pvDataCPP provides and has code that - associates these facilities with a PVRecord. -
-
PVRecord and PVDatabase
-
Defined below.
-
The localChannelProvider itself
-
- The localChannelProvider accesses data from PVRecords. - It implements all channel methods except channelRPC, - which is implemented by pvAccessCPP. -
-
-

Features Required for pvRecord

-
-
PVDatabase
-
This holds a set of PVRecords. - It has methods to find, add, and remove records.
-
PVRecord
-
This, and a set of related interfaces, provides the following: -
-
Access to top level PVStructure
-
PVRecord is a wrapper on a top level pvStructure.
-
Record locking
-
A record can be locked and unlocked. - A record must be locked whenever data in the pvStructure is accessed. -
-
Trapping data changes
-
A client can request to be notified when data in the pvStructure - is modified. - It can do this on a field by field basis.
-
A method named process
-
-

The process method is called when a pvAccess client requests that a record be processed. - If a top level timeStamp field exists, - the default process method just sets the timeStamp to the currect time. - A service is created by implementing process and providing a top level PVStructure. -

-
-
-
-
- -

Building pvDatabaseCPP

-

To build pvDatabaseCPP You must provide a file RELEASE.local -in directory configure. -Thus do the following:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product pvDatabaseCPP requires. -Than at the top level just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP
-mrk> make
-
-

This builds pvDatabaseCPP and also the tests and all examples.

-

Each example and arrayPerformance is a completely separate top, -but is also built when make is run in pvDatabaseCPP itself.

-

-Each is a separate top for the following reasons:

-
    -
  1. - It is easier to understand each example including how it is built so that - it can be run as a main or as part of a V3 IOC. -
  2. -
  3. - Each example can be copied somewhere else and used as the basis - for creating a new service. -
  4. -
-

-If it is desired to build an example all by itself, -just follow the same instructions as for building pvDatabaseCPP itself. -For example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/configure
-mrk> cp ExampleRELEASE.local RELEASE.local
-
-

Then edit RELEASE.local so that it has the correct location of each -product the example requires. -Than at the top level of the example just execute make:

-
-mrk> cd ..
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer
-mrk> make
-
-

This builds the example.

- -

iocshell commands

-

Shell commands are made available via the standard DBD include mechanism -provided by iocCore. -The following provide EPICS V4 shell commands:

-
-
pvAccessCPP
-
PVAClientRegister.dbd and PVAServerRegister.dbd
-
pvaSrv
-
dbPv.dbd
-
pvDatabaseCPP
-
registerChannelProviderLocal.dbd
-
-

-Look at exampleServer/ioc/src/exampleServerInclude.dbd for an example -of how an application can make the shell commands available. -

-

Commands From pvAccessCPP

-

The following iocsh commands are provided for a V3IOC:

-
-
startPVAClient
-
Starts the client side of pvAccess.s - It makes channel provider pvAccess available. - After startPVAServer is called the channel provider local will - also be available. -
-
stopPVAClient
-
Stops pvAccess.
-
startPVAServer
-
Starts the local channel provider
-
stopPVAServer
-
Stop the local channel provider
-
- -

Commands implemented by pvDatabaseCPP

-

The following iocsh commands are provided for a V3IOC:

-
-
pvdbl
-
Provides a list of all the pvRecords in database master -
-
-

In addition any code that implements a PVRecord must implement an ioc command. -Look at any of the examples to see how to implement shell commands.

-

Commands implemented by pvaSrv

-

pvaSrv provides a pvAccess server that provides access to iocCore records.

-

pvaSrv does not provide any shell commands but it can be part of an IOC. -Just make sure your application configures pvaSrv and then include the following file: -

-
-include "dbPv.dbd"
-
- -

database

-

src/database

-

This Directory has the following files:

-
-
pvDatabase.h
-
- This is what is described in this section. -
-
pvDatabase.cpp
-
- The implementation of PVDatabase. -
-
pvRecord.cpp
-
- The implementation of the base class for PVRecord. - It can also implement record instances with a process - method does nothing. - This can be used to create a "dumb" record where all changes are - done by clients. -
-
-

src/special

-

This directory has the following files:

-
-
traceRecord.h
-
This implements a PVRecord that can set the trace level for - another record. See below for a discussion of trace level.
- -
-

pvDatabase.h

-

The classes in pvDatabase.h describe a database of memory resident -smart records. -It describes the following classes:

-
-
PVRecord
-
This provides the methods required by localChannelProvider to implement Channel.
-
PVRecordField
-
PVRecordStructure
-
These wrap PVField and PVStructure so that pvCopy and monitor - can be implemented.
-
PVRecordClient
-
This is called by anything that accesses PVRecord.
-
PVListener
-
This is implemented by anything that wants to trap calls to PVRecord::message.
-
PVDatabase
-
This is a database of PVRecords.
-
-

Each class is described in a separate subsection.

-

C++ namespace and typedefs

-
-namespace epics { namespace pvDatabase {
-
-class PVRecord;
-typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
-typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;
-
-class PVRecordField;
-typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
-typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
-typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;
-
-class PVRecordStructure;
-typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;
-
-class PVRecordClient;
-typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;
-
-class PVListener;
-typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;
-
-class PVDatabase;
-typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;
-
- -

class PVRecord

-

NOTES:

-
    -
  • This section uses the name record instead of "an instance of PVRecord".
  • -
  • Most clients will access a record via the local channel provider, - i. e. via pvAccess. - Thus this section is mainly of interest to - the local channel provider and record implementers.
  • -
  • Most readers will not care about most of the PVRecord methods. - Most of the methods are used by the pvAccess code. - Service implementers will mostly be interested in methods init and process. - These are described first. -
  • -
-

PVRecord Methods

-
-class PVRecord
-     public std::tr1::enable_shared_from_this<PVRecord>
-{
-public:
-    POINTER_DEFINITIONS(PVRecord);
-
-    virtual bool init() ;
-    virtual void start() {}
-    virtual void process() {}
-    virtual void destroy();
-
-    static PVRecordPtr create(
-        std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    virtual ~PVRecord();
-    std::string getRecordName();
-    PVRecordStructurePtr getPVRecordStructure();
-    PVRecordFieldPtr findPVRecordField(
-        epics::pvData::PVFieldPtr const & pvField);
-    void lock();
-    void unlock();
-    bool tryLock();
-    void lockOtherRecord(PVRecordPtr const & otherRecord);
-    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
-    void detachClients();
-    bool addListener(PVListenerPtr const & pvListener);
-    bool removeListener(PVListenerPtr const & pvListener);
-    void beginGroupPut();
-    void endGroupPut();
-    int getTraceLevel();
-    void setTraceLevel(int level);
-protected:
-    PVRecord(
-        std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-    void initPVRecord();
-    epics::pvData::PVStructurePtr getPVStructure();
-    PVRecordPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-private:
-...
-}
-
-

The methods are:

-
-
init
-
Virtual method. - Derived classes must implement this method. - This method Must call initPVRecord. -
-
start
-
Virtual method. - Optional method for derived class. - It is called before record is added to database. - The base method does nothing. -
-
process
-
Virtual method. - Derived classes usually implement this method. - It implements the semantics for the record. - The base implementation does nothing. -
-
destroy
-
Virtual method. - Optional method for derived class. - If the derived class implements this it - must call the base class destroy method after it - has released any resources it uses.
-
create
-
Static method to create dumb records, - i.e. records with a process method that does nothing. - A derived class should have it's own static create method. -
-
~PVRecord
-
The destructor which must be virtual. A derived class must also have - a virtual destructor.
-
getRecordName
-
Return the recordName.
-
getPVRecordStructure
-
Get the top level PVStructure.
-
findPVRecordField
-
Given a PVFieldPtr return the PVRecordFieldPtr for the field.
-
lock
-
unlock
-
Lock and Unlock the record. - Any code accessing the data in the record or calling other PVRecord methods - must have the record locked.
-
tryLock
-
If true then just like lock. - If false client can not access record. - A client can try to simultaneously hold the lock for more than two records - by calling this method. But must be willing to accept failure. -
-
lockOtherRecord
-
A client that holds the lock for one record can lock one other record. - A client must not call this if the client already has the lock for - more then one record. -
-
addPVRecordClient
-
Every client that accesses the record must call this so that the client can be notified when the record is deleted.
-
removePVRecordClient
-
Client is no longer accessing the record.
-
detachClients
-
Ask all clients to detach from the record
-
addListener
-
Add a PVListener. This must be called before calling pvRecordField.addListener.
-
removeListener
-
Removes a listener. The listener will also be removed from all fields to which it is attached.
-
beginGroupPut
-
Begin a group of puts. - This results in all registered PVListeners being called
-
endGroupPut
-
End a group of puts. - This results in all registered PVListeners being called.
-
getTraceLevel
-
This can be used for debugging. There are currently three - levels that are used by existing code. -
-
0
-
Produce no trace messages.
-
1
-
Issue a message to std::cout whenever anything is created - or destroyed.
-
2
-
In addition to lifetime messages also issue a message - whenever the record is accessed by pvAccess client.
-
-
-
setTraceLevel
-
Set the trace level. Note that special, described below. - provides a record support that allows a pvAccess client - to set the trace level of a record.
-
-

The protected methods are:

-
-
PVRecord
-
The constructor. It requires a recordName and a top level PVStructure.
-
initPVRecord
-
This method must be called by derived class.
-
getPVStructure
-
Called by derived class.
-
-

class PVRecordField

-
-class PVRecordField {
-     public virtual epics::pvData::PostHandler,
-     public std::tr1::enable_shared_from_this<PVRecordField>
-public:
-    POINTER_DEFINITIONS(PVRecordField);
-    PVRecordField(
-        epics::pvData::PVFieldPtr const & pvField,
-        PVRecordStructurePtr const &parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordField();
-    virtual void destroy();
-    PVRecordStructurePtr getParent();
-    epics::pvData::PVFieldPtr getPVField();
-    std::string getFullFieldName();
-    std::string getFullName();
-    PVRecordPtr getPVRecord();
-    bool addListener(PVListenerPtr const & pvListener);
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    PVRecordFieldPtr getPtrSelf()
-    {
-        return shared_from_this();
-    }
-    virtual void init();
-    virtual void postParent(PVRecordFieldPtr const & subField);
-    virtual void postSubField();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordField for every field in the PVStructure -that holds the data. It has the following methods: -

- -
-
PVRecordField
-
The constructor.
-
~PVRecordField
-
The destructor.
-
destroy
-
Called by PVRecordStructure when it's destroy method is called.
-
getParent
-
Get the parent PVRecordStructure for this field.
-
getPVField
-
Get the PVField associated with this PVRecordField.
-
getFullFieldName
-
This gets the full name of the field, i.e. field,field,..
-
getFullName
-
This gets recordName plus the full name of the field, i.e. recordName.field,field,..
-
getPVRecord
-
Returns the PVRecord to which this field belongs.
-
addListener
-
Add A PVListener to this field. - Whenever this field or any subfield if this field is modified the listener will be notified. - PVListener is described below. - Before a listener can call addListener it must first call PVRecord.registerListener.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
-

class PVRecordStructure

-
-class PVRecordStructure : public PVRecordField {
-public:
-    POINTER_DEFINITIONS(PVRecordStructure);
-    PVRecordStructure(
-        epics::pvData::PVStructurePtr const & pvStructure,
-        PVRecordStructurePtr const & parent,
-        PVRecordPtr const & pvRecord);
-    virtual ~PVRecordStructure();
-    virtual void destroy();
-    PVRecordFieldPtrArrayPtr getPVRecordFields();
-    epics::pvData::PVStructurePtr getPVStructure();
-    virtual void removeListener(PVListenerPtr const & pvListener);
-    virtual void postPut();
-protected:
-    virtual void init();
-private:
-...
-};
-
-

When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure -that holds the data. It has the following methods: -

-
-
PVRecordStructure
-
The constructor.
-
~PVRecordStructure
-
The destructor.
-
getPVRecordFields
-
Get the PVRecordField array for the subfields
-
getPVStructure
-
Get the PVStructure for this field.
-
removeListener
-
Remove a PVListener.
-
postPut
-
This is called by the code that implements the data interface. - It is called whenever the put method is called.
-
-

class PVRecordClient

-
-class PVRecordClient {
-    POINTER_DEFINITIONS(PVRecordClient);
-    virtual ~PVRecordClient();
-    virtual void detach(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVRecordClient
-
The destructor.
-
detach
-
The record is being removed from the master database,
-
-

class PVListener

-
-class PVListener {
-    virtual public PVRecordClient
-public:
-    POINTER_DEFINITIONS(PVListener);
-    virtual ~PVListener();
-    virtual void dataPut(PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void dataPut(
-        PVRecordStructurePtr const &
-        requested,PVRecordFieldPtr const & pvRecordField) = 0;
-    virtual void beginGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void endGroupPut(PVRecordPtr const & pvRecord) = 0;
-    virtual void unlisten(PVRecordPtr const & pvRecord);
-};
-
-

where

-
-
~PVListener
-
The destructor.
-
dataPut(PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - This is called if the listener has called PVRecordField::addListener for pvRecordField.
-
dataPut( - PVRecordStructurePtr const & - requested,PVRecordFieldPtr const & pvRecordField)
-
pvField has been modified. - Requested is the field to which the requester issued a pvField-&addListener. - This is called if the listener has called PVRecordField-&addListener for requested.
-
beginGroupPut
-
A related set of changes is being started.
-
endGroupPut
-
A related set of changes is done.
-
unlisten
-
The record is being destroyed. The listener must release all - access to the record.
-
-

class PVDatabase

-
-class PVDatabase : virtual public epics::pvData::Requester {
-public:
-    POINTER_DEFINITIONS(PVDatabase);
-    static PVDatabasePtr getMaster();
-    virtual ~PVDatabase();
-    virtual void destroy();
-    PVRecordPtr findRecord(std::string const& recordName);
-    bool addRecord(PVRecordPtr const & record);
-    epics::pvData::PVStringArrayPtr getRecordNames();
-    bool removeRecord(PVRecordPtr const & record);
-private:
-    PVDatabase();
-};
-
-

where

-
-
getMaster
-
Get the master database. This is the database that localChannelProvider access.
-
~PVDatabase
-
The destructor.
-
destroy
-
This is called by remote channelAccess when process exits. - This destroys and removes all records in the PVDatabase.
-
findRecord
-
Find a record. An empty pointer is returned if the record is not in the database.
-
addRecord
-
Add a record to the database. - If the record already exists it is not modified and false is returned.
-
getRecordNames
-
Returns an array of all the record names.
-
removeRecord
-
Remove a record from the database. - If the record was not in the database false is returned.
-
-

pvAccess

-

This is code that provides an implementation of channelProvider as -defined by pvAccess. -It provides access to PVRecords and is accessed by the server side of remote pvAccess. -It uses the copy and monitor facilities from pvDataCPP and connects -them to a PVRecord. -

-

The implementation is a complete implementation of channelProvider -and channel except for channelRPC, which is implement by pvAccess as a separate -channel provider.

-

The following provides a brief description of each channel method that -is implemented.

-

channelProcessLocal

-

Needs to be described.

-

channelGetLocal

-

Needs to be described.

-

channelPutLocal

-

Needs to be described.

-

channelPutGetLocal

-

Needs to be described.

-

channelArrayLocal

-

Needs to be described.

-

MonitorLocal

-

This is the code that implements monitors on changes to fields of a PVRecord. -Because it is called by pvAccess client (monitor methods) and by -PVRecord (when postPut is called), it must be careful to prevent deadlocks. -The implementation is via class MonitorLocal (implemented in monitorFactory.cpp) -and PVCopyMonitor. -MonitorLocal is the interface between pvAccess and PVCopyMonitor. -PVCopyMonitor is the interface between MonitorLocal and PVRecord. -MonitorLocal manages a MonitorElement queue. -While monitoring is active (between start and stop) it keeps an active element -for use by PVCopyMonitor. -While monitoring is active PVCopyMonitor updates the active monitor element whenever -a postPut is issued to any field being monitored. -

-

The following two sections provide a few more details about MonitorLocal -and PVCopyMonitor.

-

MonitorLocal

-

MonitorLocal implements the following abstract base classes:

-
-
Monitor
-
This is described by pvDataCPP. - It has methods start, stop, poll, and release. - These methods are called by the pvAccess client -
-
PVCopyMonitorRequester
-
This has methods releaseActiveElement and unlisten. - These methods are called by PVCopyMonitor. -
-
-MonitorLocal manages the following: -
-
MonitorElementQueue
-
This is a queue of monitor elements. - A Queue is implemented by pvDataCPP and used by MonitorLocal. - It is a finite queue. - A monitor element is described by pvDataCPP. - It has fields pvStructure, changedBitSet, and overrunBitSet. - The pvStructure holds data for a subset of the fields in a PVRecord. - The changedBitSet and overrunBitSet describe changes between - monitor event. - MonitorLocal creates an instance of PVCopy (implemented by pvDataCPP), - which manages the interaction between the set of fields being - monitored and the fields in the top level PVStructure of the PVRecord. - pvCopy is also used to create the pvStructure for each monitor element. -
-
activeElement
-
Whenever monitoring is active monitorLocal - keeps an active element for use by pvCopyMonitor. - It changes the active element based on calls to poll (by the - client) and calls to releaseActiveElement (by pvCopyMonitor). - If there are no free element when releaseActiveElement is - called the current active element is returned. - If a free element is available the client is notified that a new - monitor element is available and the free element becomes the - active element. -
-
-

A brief description on each method in MonitorLocal is:

-
-
start
-
- Called by client. - With a lock held it clears the monitorElement queue - and allocates an active element. - With no lock held calls pvCopyMonitor->startMonitoring(activeElement) -
-
stop
-
- Called by client. - With no lock held calls pvCopyMonitor->stopMonitoring(activeElement) -
-
poll
-
- Called by client. - With a lock held it calls queue->getUsed(); -
-
release
-
- Called by client. - With a lock held it calls queue->releaseUsed(); -
-
releaseActiveElement
-
- Called by PVCopyMonitor with no locks held. - With a lock held it tries to get a new free element. - If it can't it just returns the current active element. - Otherwise it does the following. - Using the activeElement it updates the pvStructure - and compresses the changed and overrun bitSet. - It then calls queue->setUsed(activeElement); - It then sets the active element to the new free element. - With no lock held it calls monitorRequester->monitorEvent(getPtrSelf()) - and finally returns the new active element, -
-
unlisten
-
- With no lock held it calls monitorRequester->unlisten(getPtrSelf()); -
-
-

PVCopyMonitor

-

-pvCopyMonitor is the code that manages changes to -fields in the record. -It is called by PVRecord whenever a postPut is issued to a field. -pvCopyMonitor uses the active monitor element provided by monitorFactory. -Note that this method is called with the record locked. -It only modifies the changedBitSet and overrunBitSet of the -active element but never modifies the pvStructure. -

-

A brief description of the pvCopyMonitor methods is:

-
-
startMonitoring
-
With no lock held it sets its monitorElement to the - startElement passed by monitorLocal and calls pvRecord->addListener(getPtrSelf()). - It locks the pvRecord. - It calls calls addListener for every field in the record that is being - monitored. - It clears the overrun and changed bit sets. - It sets bit 0 of the changed bit set and calls - pvCopyMonitorRequester->releaseActiveElement(); - Thus the client will get the initial values for every field being monitored. - The record is unlocked and the method returns to the caller. -
-
stopMonitoring
-
- With no lock held it calls pvRecord->removeListener(getPtrSelf()); -
-
dataPut
-
- This is called because of a call to postPut. - It is called with the record locked. - It updates the changed and overrun bitSets. - It isGroupPut is false it calls - pvCopyMonitorRequester->releaseActiveElement(). - Otherwise it sets dataChanged true. -
-
beginGroupPut
-
- With a lock held it - sets isGroupPut true and dataChanged false. -
-
endGroupPut
-
- With a lock held it sets isGroupPut false. - With no lock held and dataChanged true it calls - pvCopyMonitorRequester->releaseActiveElement() -
-
unlisten
-
- Just calls pvCopyMonitorRequester->unlisten(); -
-
- -

special

-

This section provides two useful record support modules -and one that is used for testing.

-

traceRecord

-

This implements a PVRecord that allows a client to set -the trace level of a record. It follows the pattern of a channelPutGet -record:

-
-traceRecord
-    structure argument
-        string recordName 
-        int level 0
-    structure result
-        string status 
-
-where: -
-
recordName
-
The name of the record to set the trace level.
-
level
-
The level to set. The meaning is: -
-
0
-
No trace messages generated
-
1
-
Lifecycle messages will be generated. - This all channel create and destroy instances will be shown.
-
2
-
In addition to lifecycle messages a message will be generated - for each get and put request.
-
>2
-
Currently no definition
-
-
-
result
-
The result of a cannelPutGet request
-
-

testExampleServerMain.cpp has an example of how to create a traceRecord:

-
-PVDatabasePtr master = PVDatabase::getMaster();
-PVRecordPtr pvRecord;
-String recordName;
-bool result(false);
-recordName = "traceRecordPGRPC";
-pvRecord = TraceRecord::create(recordName);
-result = master->addRecord(pvRecord);
-if(!result) cout<< "record " << recordName << " not added" << endl;
-
- -

exampleServer

-

Overview

-

The example implements a simple service that has a top level pvStructure: -

-
-structure
-    structure argument
-        string value
-    structure result
-        string value
-        time_t timeStamp
-            long secondsPastEpoch
-            int nanoseconds
-            int userTag
-
-

It is designed to be accessed via a channelPutGet request. -The client sets argument.value -When the record processes it sets result.value to "Hello " -concatenated with argument.value. -Thus if the client sets argument.value equal to "World" -result.value will be "Hello World". -In addition the timeStamp is set to the time when process is called.

-

-The example can be run on linux as follows:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleService
-mrk> bin/linux-x86_64/exampleService
-
-

Directory Layout

-

-The directory layout is: -

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerInclude.dbd
-       exampleServerMain.cpp
-       exampleServerRegister.cpp
-       exampleServerRegister.dbd
-    ioc
-       Db
-          ...
-       src
-          exampleServerInclude.dbd
-          exampleServerMain.cpp
-   iocBoot
-      exampleServer
-         st.cmd
-         ...
-
-where -
-
ExampleRELEASE.local
-
- If you make a copy of exampleServer and use it - to create a new server, - This is the file that must be copied to RELEASE.local - and edited.
-
exampleServer.h
-
The header file for the service.
-
exampleServer.cpp
-
The service implementation.
-
exampleServerMain.cpp
-
A main program that runs the example so that it can be accessed - by a pvAccess client. -
-
exampleServerInclude.dbd
-
This has a register command so that the service can be started - on a V3 IOC via iocsh. -
-
exampleServerRegister.cpp
-
This has the code to start the service via the following iocsh - command. -
-
exampleServerRegister.dbd
-
This is the file that is used to create the shell command - exampleServerCreateRecord. -
-exampleServerCreateRecord exampleServer
-
- Multiple commands can be issued to create multiple service records. -
-
ioc
-
This is for building a V3 IOC application.
-
ioc/Db
-
This has template files for creating V3 records.
-
ioc/src
-
The files for running a V3 IOC.
-
iocBoot/exampleServer
-
A place to start exampleServer as part of a V3IOC. - It has a st.cmd file that starts the ioc and also starts pvAccess - and the example.
-
-

If only a main program is desired then the directory layout is:

-
-exampleServer
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleServer.h
-       exampleServer.cpp
-       exampleServerMain.cpp
-
-

Thus if only a main program is required the directory layout is simple.

-

Also many sites will want to build the src directory in an area -separate from where the iocs are build.

-

exampleServer.h

-

The example resides in src -The implementation is in exampleServer.cpp. -

-

The description consists of

-
-class ExampleServer;
-typedef std::tr1::shared_ptr<ExampleServer> ExampleServerPtr;
-
-class ExampleServer :
-    public PVRecord
-{
-public:
-    POINTER_DEFINITIONS(ExampleServer);
-    static ExampleServerPtr create(
-        std::string const & recordName);
-    virtual ~ExampleServer();
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-private:
-    ExampleServer(std::string const & recordName,
-        epics::pvData::PVStructurePtr const & pvStructure);
-
-    epics::pvData::PVStringPtr pvArgumentValue;
-    epics::pvData::PVStringPtr pvResultValue;
-    epics::pvData::PVTimeStamp pvTimeStamp;
-    epics::pvData::TimeStamp timeStamp;
-};
-
-

where

-
-
create
-
This is example specific but each support could provide - a similar static method. -
-
~ExampleServer
-
The destructor must be declared virtual.
-
destroy
-
Called when the record is being destroyed. - This must call the base class destroy method. -
-
init
-
A method to initialize the support. It returns true - if initialization is successful and false if not. - NOTE that this is a virtual method of PVRecord itself.
-
process
-
- This again is a virtual method of PVRecord. -
-
ExampleServer
-
For the example this is private.
-
pvValue
-
This is the field of the top level structure that process - accesses. -
-
-

The implementation of create method is:

-
-ExampleServerPtr ExampleServer::create(
-    std::string const & recordName)
-{
-    StandardFieldPtr standardField = getStandardField();
-    FieldCreatePtr fieldCreate = getFieldCreate();
-    PVDataCreatePtr pvDataCreate = getPVDataCreate();
-    StructureConstPtr  topStructure = fieldCreate->createFieldBuilder()->
-        addNestedStructure("argument")->
-            add("value",pvString)->
-            endNested()->
-        addNestedStructure("result") ->
-            add("value",pvString) ->
-            add("timeStamp",standardField->timeStamp()) ->
-            endNested()->
-        createStructure();
-    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
-
-    ExampleServerPtr pvRecord(
-        new ExampleServer(recordName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-This: -
    -
  • Creates the top level structure.
  • -
  • Creates a ExampleServerPtr via the constructor.
  • -
  • Calls init and if it fails resets the shared pointer.
  • -
  • Returns the shared pointer to the newly created record.
  • -
-

The private constructor method is:

-
-ExampleServer::ExampleServer(
-    std::string const & recordName,
-    epics::pvData::PVStructurePtr const & pvStructure)
-: PVRecord(recordName,pvStructure)
-{
-}
-
-The example is very simple. Note that it calls the base class constructor. -

The destructor and destroy methods are:

-
-ExampleServer::~ExampleServer()
-{
-}
-
-void ExampleServer::destroy()
-{
-    PVRecord::destroy();
-}
-
-The destructor has nothing to do. -The destroy method, which is virtual, just calls the destroy method of the base class. -A more complicated example can clean up any resources it used but must call the base -class destroy method. -

The implementation of init is:

-
-bool ExampleServer::init()
-{
-    initPVRecord();
-    PVFieldPtr pvField;
-    pvArgumentValue = getPVStructure()->getStringField("argument.value");
-    if(pvArgumentValue.get()==NULL) return false;
-    pvResultValue = getPVStructure()->getStringField("result.value");
-    if(pvResultValue.get()==NULL) return false;
-    pvTimeStamp.attach(getPVStructure()->getSubField("result.timeStamp"));
-    return true;
-}
-
-

The implementation of process is:

-
-void ExampleServer::process()
-{
-    pvResultValue->put(String("Hello ") + pvArgumentValue->get());
-    timeStamp.getCurrent();
-    pvTimeStamp.set(timeStamp);
-}
-
-It gives a value to result.value and -then sets the timeStamp to the current time. -

src/exampleServerMain.cpp

-

NOTE: -This is a shorter version of the actual code. -It shows the essential code. -The actual example shows how create an additional record. -

-

The main program is:

-
-int main(int argc,char *argv[])
-{
-    PVDatabasePtr master = PVDatabase::getMaster();
-    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
-    String recordName("exampleServer");
-    PVRecordPtr pvRecord = ExampleServer::create(recordName);
-    bool result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    recordName = "traceRecordPGRPC";
-    pvRecord = TraceRecord::create(recordName);
-    result = master->addRecord(pvRecord);
-    if(!result) cout<< "record " << recordName << " not added" << endl;
-    ServerContext::shared_pointer pvaServer =
-        startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-    PVStringArrayPtr pvNames = master->getRecordNames();
-    shared_vector<const string> names = pvNames->view();
-    for(size_t i=0; i<names.size(); ++i) cout << names[i] << endl;
-    string str;
-    while(true) {
-        cout << "Type exit to stop: \n";
-
-    }
-    return 0;
-}
-
-This: -
    -
  • Gets a pointer to the master database.
  • -
  • Creates the local Channel Provider. This starts the pvAccess server.
  • -
  • Creates record exampleServer
  • -
  • creates record traceRecordPGRPC
  • -
  • lists all the records
  • -
  • Runs forever until the user types exit on standard in.
  • -
-

V3IOC exampleServer

-

To start exampleServer as part of a V3IOC:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleServer/iocBoot/exampleServer
-mrk> ../../../bin/linux-x86_64/exampleServer st.cmd
-
-

You can then issue the commands dbl and pvdbl:

-
-epics> dbl
-pvdouble
-pvcounter
-pvenum
-pvdoubleArray
-pvstringArray
-epics> pvdbl
-exampleServer
-epics> 
-
-dbl shows the V3 records. -pvdbl shows the pvRecords. -

-It starts pvaSrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

- -

exampleDatabase

-

The exampleServer pvDatabase has many records including the following:

-
-
exampleDouble
-
A record that is an instance of a record with a process method - that does nothing. To test it start a channelPut and a channelGet and/or monitor.
-
exampleDoubleArray
-
An array record that is an instance of a record with a process method - that does nothing. It can be tested like exampleDouble. In addition channelArray can - also be used.
-
traceRecordPGRPC
-
This can be used via channelPutGet to set the trace level of another record.
-
-

It also has a number of other scalar and array records.

-

exampleDatabase can be started as a main program or as a V3 IOIC. -If started as a V3 IOC it also has a number of V3 records, -and starts pvaSrv so that the V3 records can be accessed via Channel Access -or via PVAccess.

-

exampleLink

-

This example show how a service can access other PVRecords. -This section 1) starts with a discussion of accessing data via pvAccess -and 2) gives a brief description of an example that gets data for an array of doubles.

-

Discussion

-

Access Alternatives

-

The process routine of a PVRecord can access other PVRecords in two ways:

-
-
Directly accessing local pvDatabase
-
- If the other PVRecord is accessed via the master PVDatabase then - threading issues are up to the implementation. - For now this method will not be discussed.
-
Access via pvAccess
-
- If access is via pvAccess then locking is handled by pvAccess.
-
-

Access via pvAccess can be done either by local or remote channel provider.

-
-
Access via channelProviderLocal
-
- If the local pvAccess server is used the implementation must be careful that it does not - cause deadlocks. - When the process method is called the pvRecord for the process method is locked. - When it makes a pvAccess get, put, etc request the other record is locked. - Thus if a set of pvAccess links are implemented the possibility of deadlocks - exists. A simple example is two records that have links to each other. - More complex sets are easily created. - Unless the developer has complete control of the set of records then remote pvAccess should - be used. - But this results in more context switches. -
-
Access via remote pvAccess
-
If remote pvAccess is used then all locking issues are handled by pvAccess. - The linked channel can be a pvRecord in the local pvDatabase or can be implemented - by a remote pvAccess server.
-
-

Data synchronization

-

If pvAccess is used then it handles data synchronization. -This is done by making a copy of the data that is transferred between the two pvRecords. -This is true if either remote or local pvAccess is used. -Each get, put, etc request results in data being copied between the two records.

-

-If the linked channel is a local pvRecord then, -for scalar and structure arrays, -raw data is NOT copied for gets. -This is because pvData uses shared_vector to hold the raw data. -Instead of copying the raw data the reference count is incremented.

-

For puts the linked array will force a new allocation of the raw data in the linked record, -i. e. copy on write semantics are enforced. This is done automatically -by pvData and not by pvDatabase.

-

Some details

-

As mentioned before a pvDatabase server can be either a separate process, -i. e. a main program, or can be part of a V3IOC.

-

A main pvDatabase server issues the following calls:

-
- ClientFactory::start();
- ChannelProviderLocalPtr channelProvider = getChannelProviderLocal();
- ...
- ServerContext::shared_pointer serverContext =
-     startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
-
-

The first call is only necessary if some of the pvRecords -have pvAccess links. -These must be called before any code that uses links is initialized. -After these two calls there will be two channel providers: local, and pvAccess. - -

-

A pvDatabase that is part of a V3IOC has the following in the st.cmd file.

-
-...
-iocInit()
-startPVAClient
-startPVAServer
-## commands to create pvRecords
-
-

-Once the client and local provider code has started then the following creates a channel access link. -

-
-PVDatabasePtr master = PVDatabase::getMaster();
-ChannelProvider::shared_pointer provider =
-     getChannelProviderRegistry()->getProvider(providerName);
-Channel::shared_pointer channel = provider->createChannel(channelName,channelRequester);
-
- -

Directory Layout

-
-exampleLink
-    configure
-       ExampleRELEASE.local
-       ...
-    src
-       exampleLink.h
-       exampleLink.cpp
-       exampleLinkInclude.dbd
-       exampleLinkRegister.cpp
-    ioc
-       Db
-       src
-          exampleLinkInclude.dbd
-          exampleLinkMain.cpp
-   iocBoot
-      exampleLink
-         st.local
-         st.remote
-         ...
-
-

This example is only built to be run as part of a V3 IOC. -Note that two startup files are available: st.local and st.remote. -st.local has two records: doubleArray and exampleLink. -doubleArray is a record that can be changed via a call to pvput. -exampleLink is a record that, when processed, gets the value from doubleArray and sets its value equal -to the value read. -st.local has both records. -st.remote has only one record named exampleLinkRemote. -

-

To start the example:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP/exampleLink/iocBoot/exampleLink
-mrk> ../../bin/linux-x86_64/exampleLink st.local 
-
-

then in another window:

-
-mrk> pvput doubleArray 4 100 200 300 400
-Old : doubleArray 0
-New : doubleArray 4 100 200 300 400
-mrk> pvget -r "record[process=true]field(value)" exampleLink
-exampleLink
-structure 
-    double[] value [100,200,300,400]
-mrk> 
-
-

exampleLink Implementation

-

exampleLink.h contains the following:

-
-...
-class ExampleLink :
-    public PVRecord,
-    public epics::pvAccess::ChannelRequester,
-    public epics::pvAccess::ChannelGetRequester
-{
-public:
-    POINTER_DEFINITIONS(ExampleLink);
-    static ExampleLinkPtr create(
-        std::string const & recordName,
-        std::string const & providerName,
-        std::string const & channelName
-        );
-    virtual ~ExampleLink() {}
-    virtual void destroy();
-    virtual bool init();
-    virtual void process();
-    virtual void channelCreated(
-        const epics::pvData::Status& status,
-        epics::pvAccess::Channel::shared_pointer const & channel);
-    virtual void channelStateChange(
-        epics::pvAccess::Channel::shared_pointer const & channel,
-        epics::pvAccess::Channel::ConnectionState connectionState);
-    virtual void channelGetConnect(
-        const epics::pvData::Status& status,
-        epics::pvAccess::ChannelGet::shared_pointer const & channelGet,
-        epics::pvData::PVStructure::shared_pointer const & pvStructure,
-        epics::pvData::BitSet::shared_pointer const & bitSet);
-    virtual void getDone(const epics::pvData::Status& status);
-private:
-...
-
-

All the non-static methods are either PVRecord, PVChannel, or PVChannelGet methods -and will not be discussed further. -The create method is called to create a new PVRecord instance with code that will issue -a ChannelGet::get request every time the process method of the instance is called. -Some other pvAccess client can issue a channelGet, to the record instance, with a request -to process in order to test the example.

-

All of the initialization is done by a combination of the create and init methods so -lets look at them:

-
-ExampleLinkPtr ExampleLink::create(
-    String const & recordName,
-    String const & providerName,
-    String const & channelName)
-{
-    PVStructurePtr pvStructure = getStandardPVField()->scalarArray(
-        pvDouble,"alarm.timeStamp");
-    ExampleLinkPtr pvRecord(
-        new ExampleLink(
-           recordName,providerName,channelName,pvStructure));
-    if(!pvRecord->init()) pvRecord.reset();
-    return pvRecord;
-}
-
-

This first creates a new ExampleLink instance, -and then calls the init method and the returns a ExampleLinkPtr. -Note that if init returns false it returns a pointer to NULL.

-

The init method is:

-
-bool ExampleLink::init()
-{
-    initPVRecord();
-
-    PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure();
-    pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
-    pvAlarm.attach(pvStructure->getSubField("alarm"));
-    pvValue = static_pointer_cast<PVDoubleArray>(
-        pvStructure->getScalarArrayField("value",pvDouble));
-    if(pvValue==NULL) {
-        return false;
-    }
-    ChannelAccess::shared_pointer channelAccess = getChannelAccess();
-    ChannelProvider::shared_pointer provider =
-        channelAccess->getProvider(providerName);
-    if(provider==NULL) {
-         cout << getRecordName() << " provider "
-              << providerName << " does not exist" << endl;
-        return false;
-    }
-    ChannelRequester::shared_pointer channelRequester =
-        dynamic_pointer_cast<ChannelRequester>(getPtrSelf());
-    channel = provider->createChannel(channelName,channelRequester);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannel failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    ChannelGetRequester::shared_pointer channelGetRequester =
-        dynamic_pointer_cast<ChannelGetRequester>(getPtrSelf());
-    PVStructurePtr pvRequest = getCreateRequest()->createRequest(
-        "value,alarm,timeStamp",getPtrSelf());
-    channelGet = channel->createChannelGet(channelGetRequester,pvRequest);
-    event.wait();
-    if(!status.isOK()) {
-        cout << getRecordName() << " createChannelGet failed "
-             << status.getMessage() << endl;
-        return false;
-    }
-    getPVValue = static_pointer_cast<PVDoubleArray>(
-        getPVStructure->getScalarArrayField("value",pvDouble));
-    if(getPVValue==NULL) {
-        cout << getRecordName() << " get value not  PVDoubleArray" << endl;
-        return false;
-    }
-    return true;
-}
-
-

This first makes sure the pvStructure has the fields it requires:

-
-
timeStamp
-
A timeStamp structure. This will be set to the current time when process is called.
-
alarm
-
An alarm structure. This will be used to pass status information to the client when - process is called.
-
value
-
This must be a scalarArray of type double. - It is where data is copied when the channelGet is issued.
-
-

Next it makes sure the channelProvider exists.

-

Next it creates the channel and waits until it connects.

-

Next it creates the channelGet and waits until it is created.

-

Next it makes sure it has connected to a double array field.

-

If anything goes wrong during initialization it returns false. -This a return of true means that it has successfully created a channelGet and is ready -to issue gets when process is called.

-

Look at the code for more details.

-

examplePowerSupply

-

This is an example of creating a service that requires a somewhat complicated -top level PVStructure. -Look at the code for details. -

-

Array Performance and Memory Example

-

This section describes main programs that demonstrate performance -of large arrays and can also be used to check for memory leaks. -Checking for memory leaks can be accomplished by running the programs with valgrind -or some other memory check program. -

-

Brief Summary

-

The programs are:

-
-
arrayPerformanceMain
-
This is server and also a configurable number of longArrayMonitor clients. - The clients can use either the local or - remote providers. The monitor code is the same code that is used by longArrayMonitorMain. -
-
longArrayMonitorMain
-
Remote client that monitors the array served by arrayPerformanceMain.
-
longArrayGetMain
-
Remote client that uses channelGet to access the array served by arrayPerformanceMain.
-
longArrayPutMain
-
Remote client that uses channelPut to access the array served by arrayPerformanceMain.
-
-

Each has support for -help.

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain -help
-arrayPerformanceMain recordName size delay providerName nMonitor queueSize waitTime
-default
-arrayPerformanceMain arrayPerformance 10000000 0.0001 local 1 2 0.0
-
-mrk> bin/linux-x86_64/longArrayMonitorMain -help
-longArrayMonitorMain channelName queueSize waitTime
-default
-longArrayMonitorMain arrayPerformance 2 0.0
-
-mrk> bin/linux-x86_64/longArrayGetMain -help
-longArrayGetMain channelName iterBetweenCreateChannel iterBetweenCreateChannelGet delayTime
-default
-longArrayGetMain arrayPerformance 0 0 1
-
-mrk> bin/linux-x86_64/longArrayPutMain -help
-longArrayPutMain channelName arraySize iterBetweenCreateChannel iterBetweenCreateChannelPut delayTime
-default
-longArrayPutMain arrayPerformance 10 0 0 1
-
-mrk> 
-
-

Example output

-

Note: These may fail if run on a platform that does not have sufficient memory,

-

To see an example just execute the following commands in four different terminal windows:

-
-bin/linux/<arch>/arrayPerformanceMain
-bin/linux/<arch>/longArrayMonitorMain
-bin/linux/<arch>/longArrayGetMain
-bin/linux/<arch>/longArrayPutMain
-
-

Each program generates a report every second when it has something to report. -Examples are:

-
-mrk> bin/linux-x86_64/arrayPerformanceMain
-arrayPerformance arrayPerformance 10000000 0.0001 local 1 2 0
-...
- monitors/sec 66 first 131 last 131 changed {1, 2} overrun {} megaElements/sec 656.999
-arrayPerformance value 132 time 1.00486 Iterations/sec 65.681 megaElements/sec 656.81
- monitors/sec 66 first 197 last 197 changed {1, 2} overrun {} megaElements/sec 656.304
-arrayPerformance value 198 time 1.00563 Iterations/sec 65.6307 megaElements/sec 656.307
- monitors/sec 66 first 263 last 263 changed {1, 2} overrun {} megaElements/sec 654.824
-...
-
-
-mrk> bin/linux-x86_64/longArrayMonitorMain
-longArrayMonitorMain arrayPerformance 2 0
-...
- monitors/sec 6 first 2357 last 2357 changed {1, 2} overrun {} megaElements/sec 68.6406
- monitors/sec 13 first 2385 last 2385 changed {1, 2} overrun {} megaElements/sec 118.72
- monitors/sec 9 first 2418 last 2418 changed {1, 2} overrun {1, 2} megaElements/sec 85.0984
-...
-
-
-mrk> bin/linux-x86_64/longArrayPutMain
-longArrayPutMain arrayPerformance 10 0 0 1
-...
-put numChannelPut 0 time 1.00148 Elements/sec 79.8819
-put numChannelPut 1 time 1.00176 Elements/sec 79.8598
-...
-
-
-mrk> bin/linux-x86_64/longArrayGetMain
-longArrayGetMain arrayPerformance 0 0 1
-...
-get kiloElements/sec 7384.61
-get kiloElements/sec 8726.34
-...
-
- -

arrayPerformance

-

The arguments for arrayPerforamanceMain are:

-
-
recordName
-
The name for the arrayPerform record.
-
size
-
The size for the long array of the value field.
-
delay
-
The time in seconds to sleep after each iteration.
-
providerName
-
The name of the channel provider for the longArrayMonitors - created by the main program. This must be either local - or pvAccess. -
-
nMonitor
-
The number of longArrayMonitors to create.
-
queueSize
-
The queueSize for the element queue. - A value less than 1 will become 1. -
-
waitTime
-
The time that longArrayMonitor will sleep after poll returns a monitorElement.
-
-

-arrayPerformance creates a PVRecord that has the structure:. -

-
-recordName
-    long[] value
-    timeStamp timeStamp
-    alarm alarm
-
-Thus it holds an array of 64 bit integers. -

The record has support that consists of a separate thread that runs -until the record is destroyed executing the following algorithm:

-
-
report
-
Once a second it produces a report. - In the above example output each line starting with - ArrayPerformance is an arrayPerformance report. -
-
create array
-
A new shared_vector is created and each element is set equal - to the iteration count.
-
lock
-
The arrayPerformance record is locked.
-
Begin group put
-
beginGroupReport is called.
-
replace
-
The value field of the record is replaced - with the newly created shared_vector.
-
process
-
The record is then processed. This causes the timeStamp to - be set to the current time.
-
End group put
-
endGroupPut is called.
-
unlock
-
The arrayPerformance record is unlocked.
-
delay
-
If delay is greater than zero epicsThreadSleep is called.
-
-

longArrayMonitor

-

This is a pvAccess client that monitors an arrayPerformance record. -It generates a report every second showing how many elements has received. -For every monitor it also checks that the number of elements is >0 and the -the first element equals the last element. It reports an error if either -of these conditions is not true.

-

The arguments for longArrayMonitorMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
queueSize
-
The queueSize. Note that any size <2 is made 2.
-
waitTime
-
The time to wait after a poll request returns a monitorElement. - This can be used to force an overrun of the client even if there is no - overrun on the server.
-
-

longArrayGet

-

This is a pvAccess client that uses channelGet to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayGetMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelGet
-
The number of iterations between destroying and recreating the channelGet. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to delay between gets.
-
-

longArrayPut

-

This is a pvAccess client that uses channelPut to access an arrayPerformance record. -Every second it produces a report.

- -

The arguments for longArrayPutMain are:

-
-
channelName
-
The name for the arrayPerform record.
-
arraySize
-
The capacity and length of the array to put to the server.
-
iterBetweenCreateChannel
-
The number of iterations between destroying and recreating the channel. - A value of 0 means never destroy and recreate. -
-
iterBetweenCreateChannelPut
-
The number of iterations between destroying and recreating the channelPut. - A value of 0 means never destroy and recreate. -
-
delayTime
-
The time to delay between gets.
-
- -

Some results

-

array performance

-

The results were from my laptop in 2013 -It had a 2.2Ghz intel core i7 with 4Gbytes of memory. -The operating system was linux fedora 16.

-

When test are performed with large arrays it is a good idea to also -run a system monitor facility and check memory and swap history. -If a test configuration causes physical memory to be exhausted -then performance becomes very poor. -You do not want to do this.

-

arrayPerformance results

-

The simplest test to run arrayPerformance with the defaults:

-
-mrk> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/arrayPerformanceMain
-
-

This means that the array will hold 10 million elements. -The delay will be a millisecond. -There will be a single monitor and it will connect directly -to the local channelProvider, i. e. it will not use any network -connection.

-

The report shows that arrayPerformance can perform about 50 iterations per second -and is putting about 500million elements per second. -Since each element is an int64 this means about 4gigaBytes per second. -

-

When no monitors are requested and a remote longArrayMonitorMain is run:

-
-mr> pwd
-/home/hg/pvDatabaseCPP-md
-mrk> bin/linux-x86_64/longArrayMonitorMain
-
-

The performance drops to about 25 iterations per second and 250 million elements per second. -The next section has an example that demonstrates what happens. -Note that if the array size is small enough to fit in the local cache then running longArrayMonitor -has almost no effect of arrayPerforance. -

-

memory leaks

-

Running longArrayMonitorMain, longArrayPutMain, and longArrayGetMain -under valgrind shows no memory leaks.

-

arrayPerformanceMain shows the following:

-
-==9125== LEAK SUMMARY:
-==9125==    definitely lost: 0 bytes in 0 blocks
-==9125==    indirectly lost: 0 bytes in 0 blocks
-==9125==      possibly lost: 576 bytes in 2 blocks
-
-

The possibly leaked is either 1 or 2 blocks. -It seems to be the same if clients are connected. -

-

Vector Performance

-

This example demonstrates how array size effects performance. -The example is run as:

-
-bin/linux-x86_64/vectorPerformanceMain -help
-vectorPerformanceMain size delay nThread
-default
-vectorPerformance 50000000 0.01 1 
-
-

Consider the following:

-
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 1
-...
-thread0 value 20 time 1.01897 iterations/sec 19.6277 elements/sec 981.383million
-thread0 value 40 time 1.01238 iterations/sec 19.7554 elements/sec 987.772million
-thread0 value 60 time 1.00878 iterations/sec 19.826 elements/sec 991.299million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 2
-...
-thread0 value 21 time 1.00917 iterations/sec 9.90911 elements/sec 495.455million
-thread1 value 31 time 1.05659 iterations/sec 9.46443 elements/sec 473.221million
-thread0 value 31 time 1.07683 iterations/sec 9.28648 elements/sec 464.324million
-thread1 value 41 time 1.0108 iterations/sec 9.89312 elements/sec 494.656million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 3
-thread0 value 7 time 1.0336 iterations/sec 6.77244 elements/sec 338.622million
-thread1 value 7 time 1.03929 iterations/sec 6.73534 elements/sec 336.767million
-thread2 value 7 time 1.04345 iterations/sec 6.70852 elements/sec 335.426million
-thread0 value 14 time 1.03335 iterations/sec 6.77406 elements/sec 338.703million
-thread1 value 14 time 1.03438 iterations/sec 6.76734 elements/sec 338.367million
-thread2 value 14 time 1.04197 iterations/sec 6.71805 elements/sec 335.903million
-...
-bin/linux-x86_64/vectorPerformanceMain 50000000 0.00 4
-thread2 value 5 time 1.00746 iterations/sec 4.96298 elements/sec 248.149million
-thread1 value 5 time 1.02722 iterations/sec 4.86751 elements/sec 243.376million
-thread3 value 5 time 1.032 iterations/sec 4.84496 elements/sec 242.248million
-thread0 value 6 time 1.18882 iterations/sec 5.04703 elements/sec 252.351million
-thread2 value 10 time 1.00388 iterations/sec 4.98068 elements/sec 249.034million
-thread3 value 10 time 1.02755 iterations/sec 4.86592 elements/sec 243.296million
-thread1 value 10 time 1.04836 iterations/sec 4.76936 elements/sec 238.468million
-thread0 value 11 time 1.01575 iterations/sec 4.92249 elements/sec 246.124million
-
-

As more threads are running the slower each thread runs.

-

But now consider a size that fits in a local cache.

-
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00n/linux-x86_64/vectorPerformanceMain 5000 0.00 1
-...
-thread0 value 283499 time 1 iterations/sec 283498 elements/sec 1417.49million
-thread0 value 569654 time 1 iterations/sec 286154 elements/sec 1430.77million
-thread0 value 856046 time 1 iterations/sec 286392 elements/sec 1431.96million
-... 
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 2
-...
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-thread0 value 541790 time 1 iterations/sec 271513 elements/sec 1357.56million
-thread1 value 541798 time 1 iterations/sec 271418 elements/sec 1357.09million
-thread0 value 813833 time 1 iterations/sec 272043 elements/sec 1360.21million
-thread1 value 813778 time 1 iterations/sec 271979 elements/sec 1359.89million
-...
-bin/linux-x86_64/vectorPerformanceMain 5000 0.00 3
-...
-thread0 value 257090 time 1 iterations/sec 257089 elements/sec 1285.45million
-thread1 value 256556 time 1 iterations/sec 256556 elements/sec 1282.78million
-thread2 value 514269 time 1 iterations/sec 257839 elements/sec 1289.19million
-thread0 value 514977 time 1 iterations/sec 257887 elements/sec 1289.43million
-thread1 value 514119 time 1 iterations/sec 257563 elements/sec 1287.81million
-thread2 value 770802 time 1 iterations/sec 256532 elements/sec 1282.66million
-
-

Now the number of threads has a far smaller effect on the performance of each thread. -

- -
- - diff --git a/exampleDatabase/Makefile b/exampleDatabase/Makefile deleted file mode 100644 index 81fbed6..0000000 --- a/exampleDatabase/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# Makefile at top of application tree - -TOP = . -include $(TOP)/configure/CONFIG - -DIRS += configure - -DIRS += src -src_DEPEND_DIRS = configure - -DIRS += ioc -ioc_DEPEND_DIRS = src - -DIRS += iocBoot -iocBoot_DEPEND_DIRS = src - -include $(TOP)/configure/RULES_TOP - - diff --git a/exampleDatabase/configure/CONFIG b/exampleDatabase/configure/CONFIG deleted file mode 100644 index c1a4703..0000000 --- a/exampleDatabase/configure/CONFIG +++ /dev/null @@ -1,29 +0,0 @@ -# CONFIG - Load build configuration data -# -# Do not make changes to this file! - -# Allow user to override where the build rules come from -RULES = $(EPICS_BASE) - -# RELEASE files point to other application tops -include $(TOP)/configure/RELEASE --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common -ifdef T_A --include $(TOP)/configure/RELEASE.Common.$(T_A) --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) -endif - -CONFIG = $(RULES)/configure -include $(CONFIG)/CONFIG - -# Override the Base definition: -INSTALL_LOCATION = $(TOP) - -# CONFIG_SITE files contain other build configuration settings -include $(TOP)/configure/CONFIG_SITE --include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common -ifdef T_A - -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A) - -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) -endif - diff --git a/exampleDatabase/configure/CONFIG_SITE b/exampleDatabase/configure/CONFIG_SITE deleted file mode 100644 index 6648852..0000000 --- a/exampleDatabase/configure/CONFIG_SITE +++ /dev/null @@ -1,27 +0,0 @@ -# CONFIG_SITE - -# Make any application-specific changes to the EPICS build -# configuration variables in this file. -# -# Host/target specific settings can be specified in files named -# CONFIG_SITE.$(EPICS_HOST_ARCH).Common -# CONFIG_SITE.Common.$(T_A) -# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) - -# CHECK_RELEASE controls the consistency checking of the support -# applications pointed to by the RELEASE* files. -# Normally CHECK_RELEASE should be set to YES. -# Set CHECK_RELEASE to NO to disable checking completely. -# Set CHECK_RELEASE to WARN to perform consistency checking but -# continue building anyway if conflicts are found. -CHECK_RELEASE = WARN - -# To install files into a location other than $(TOP) define -# INSTALL_LOCATION here. -#INSTALL_LOCATION= - -INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv -USR_INCLUDES += -I $(INSTALL_LOCATION)/include - --include $(TOP)/../../CONFIG_SITE.local --include $(TOP)/../configure/CONFIG_SITE.local diff --git a/exampleDatabase/configure/ExampleRELEASE.local b/exampleDatabase/configure/ExampleRELEASE.local deleted file mode 100644 index 7f6ee6d..0000000 --- a/exampleDatabase/configure/ExampleRELEASE.local +++ /dev/null @@ -1,9 +0,0 @@ -EPICS_BASE=/home/install/epics/base -TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top -EPICSV4HOME=/home/hg -PVCOMMON=${EPICSV4HOME}/pvCommonCPP -PVDATA=${EPICSV4HOME}/pvDataCPP -PVACCESS=${EPICSV4HOME}/pvAccessCPP -PVDATABASE=${EPICSV4HOME}/pvDatabaseCPP -PVDATABASETEST=${EPICSV4HOME}/pvDatabaseCPP/test -PVASRV=${EPICSV4HOME}/pvaSrv diff --git a/exampleDatabase/configure/Makefile b/exampleDatabase/configure/Makefile deleted file mode 100644 index 9254309..0000000 --- a/exampleDatabase/configure/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -TOP=.. - -include $(TOP)/configure/CONFIG - -TARGETS = $(CONFIG_TARGETS) -CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS))) - -include $(TOP)/configure/RULES diff --git a/exampleDatabase/configure/RELEASE b/exampleDatabase/configure/RELEASE deleted file mode 100644 index 1bf1093..0000000 --- a/exampleDatabase/configure/RELEASE +++ /dev/null @@ -1,43 +0,0 @@ -# pvDatabaseCPP/example RELEASE - Location of external support modules -# -# IF YOU CHANGE this file or any file it includes you must -# subsequently do a "gnumake rebuild" in the application's -# top level directory. -# -# The build process does not check dependencies against files -# that are outside this application, thus you should also do a -# "gnumake rebuild" in the top level directory after EPICS_BASE -# or any other external module pointed to below is rebuilt. -# -# Host- or target-specific settings can be given in files named -# RELEASE.$(EPICS_HOST_ARCH).Common -# RELEASE.Common.$(T_A) -# RELEASE.$(EPICS_HOST_ARCH).$(T_A) - -# EPICS V4 Developers: Do not edit the locations in this file! -# -# Create a file RELEASE.local pointing to your PVASRV, PVACCESS, -# PVDATA, PVCOMMON and EPICS_BASE build directories, e.g. -# PVASRV = /path/to/epics/pvaSrvCPP -# PVACCESS = /path/to/epics/pvAccessCPP -# PVDATA = /path/to/epics/pvDataCPP -# PVCOMMON = /path/to/epics/pvCommonCPP -# EPICS_BASE = /path/to/epics/base - -# If this example is built in a directory under pvDatabaseCPP, -# use the following definitions: - -PVDATABASETEST = $(TOP)/../test -PVDATABASE = $(TOP)/.. - --include $(TOP)/../configure/RELEASE.local --include $(TOP)/../../RELEASE.local - -# If you copied this example from pvDatabaseCPP to be built as a -# standalone TOP, adjust and use the following definitions: - -#PVDATABASETEST = /path/to/epics/pvDatabaseCPP/testTop -#PVDATABASE = /path/to/epics/pvDatabaseCPP - -#-include $(TOP)/../RELEASE.local -#-include $(TOP)/configure/RELEASE.local diff --git a/exampleDatabase/configure/RULES b/exampleDatabase/configure/RULES deleted file mode 100644 index 6d56e14..0000000 --- a/exampleDatabase/configure/RULES +++ /dev/null @@ -1,6 +0,0 @@ -# RULES - -include $(CONFIG)/RULES - -# Library should be rebuilt because LIBOBJS may have changed. -$(LIBNAME): ../Makefile diff --git a/exampleDatabase/configure/RULES.ioc b/exampleDatabase/configure/RULES.ioc deleted file mode 100644 index 901987c..0000000 --- a/exampleDatabase/configure/RULES.ioc +++ /dev/null @@ -1,2 +0,0 @@ -#RULES.ioc -include $(CONFIG)/RULES.ioc diff --git a/exampleDatabase/configure/RULES_DIRS b/exampleDatabase/configure/RULES_DIRS deleted file mode 100644 index 3ba269d..0000000 --- a/exampleDatabase/configure/RULES_DIRS +++ /dev/null @@ -1,2 +0,0 @@ -#RULES_DIRS -include $(CONFIG)/RULES_DIRS diff --git a/exampleDatabase/configure/RULES_TOP b/exampleDatabase/configure/RULES_TOP deleted file mode 100644 index d09d668..0000000 --- a/exampleDatabase/configure/RULES_TOP +++ /dev/null @@ -1,3 +0,0 @@ -#RULES_TOP -include $(CONFIG)/RULES_TOP - diff --git a/exampleDatabase/ioc/Db/Makefile b/exampleDatabase/ioc/Db/Makefile deleted file mode 100644 index 7468bbf..0000000 --- a/exampleDatabase/ioc/Db/Makefile +++ /dev/null @@ -1,29 +0,0 @@ -TOP=../.. -include $(TOP)/configure/CONFIG -#---------------------------------------- -# ADD MACRO DEFINITIONS AFTER THIS LINE - -#---------------------------------------------------- -# Optimization of db files using dbst (DEFAULT: NO) -#DB_OPT = YES - -#---------------------------------------------------- -# Create and install (or just install) -# databases, templates, substitutions like this -DB += dbScalar.db -DB += dbInteger.db -DB += dbArray.db -DB += dbString.db -DB += dbStringArray.db -DB += dbEnum.db -DB += dbCounter.db -DB += dbDouble.db - -#---------------------------------------------------- -# If .db template is not named *.template add -# _TEMPLATE = - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/exampleDatabase/ioc/Db/dbArray.db b/exampleDatabase/ioc/Db/dbArray.db deleted file mode 100644 index 76506a7..0000000 --- a/exampleDatabase/ioc/Db/dbArray.db +++ /dev/null @@ -1,9 +0,0 @@ -record(waveform, "$(name)") -{ - field(NELM,"5") - field(NORD,"5") - field(FTVL,"$(type)") - field(EGU, "Counts") - field(HOPR, "10") - field(LOPR, "0") -} diff --git a/exampleDatabase/ioc/Db/dbCounter.db b/exampleDatabase/ioc/Db/dbCounter.db deleted file mode 100644 index 06aec31..0000000 --- a/exampleDatabase/ioc/Db/dbCounter.db +++ /dev/null @@ -1,20 +0,0 @@ -record(calc, "${name}") -{ - field(DESC, "Counter") - field(SCAN,"1 second") - field(CALC, "(A_registerRecordDeviceDriver.cpp will be created from .dbd -exampleDatabase_SRCS += exampleDatabase_registerRecordDeviceDriver.cpp -exampleDatabase_SRCS_DEFAULT += exampleDatabaseMain.cpp -exampleDatabase_SRCS_vxWorks += -nil- - - -# The following adds support from base/src/vxWorks -exampleDatabase_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary - -exampleDatabase_LIBS += exampleDatabase -exampleDatabase_LIBS += powerSupply -exampleDatabase_LIBS += pvDatabase -exampleDatabase_LIBS += pvaSrv -exampleDatabase_LIBS += pvAccess -exampleDatabase_LIBS += pvData -exampleDatabase_LIBS += Com -exampleDatabase_LIBS += $(EPICS_BASE_IOC_LIBS) - -#=========================== - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/exampleDatabase/ioc/src/exampleDatabaseInclude.dbd b/exampleDatabase/ioc/src/exampleDatabaseInclude.dbd deleted file mode 100644 index d91b610..0000000 --- a/exampleDatabase/ioc/src/exampleDatabaseInclude.dbd +++ /dev/null @@ -1,6 +0,0 @@ -include "base.dbd" -include "PVAServerRegister.dbd" -include "registerChannelProviderLocal.dbd" -include "dbPv.dbd" -include "powerSupplyRegister.dbd" -registrar("exampleDatabaseRegister") diff --git a/exampleDatabase/ioc/src/exampleDatabaseMain.cpp b/exampleDatabase/ioc/src/exampleDatabaseMain.cpp deleted file mode 100644 index 677df30..0000000 --- a/exampleDatabase/ioc/src/exampleDatabaseMain.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/*exampleDatabaseMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - */ - -/* Author: Marty Kraimer */ - -#include -#include -#include -#include -#include - -#include "epicsExit.h" -#include "epicsThread.h" -#include "iocsh.h" - -int main(int argc,char *argv[]) -{ - if(argc>=2) { - iocsh(argv[1]); - epicsThreadSleep(.2); - } - iocsh(NULL); - epicsExit(0); - return(0); -} - diff --git a/exampleDatabase/iocBoot/Makefile b/exampleDatabase/iocBoot/Makefile deleted file mode 100644 index 3e914fd..0000000 --- a/exampleDatabase/iocBoot/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -TOP = .. -include $(TOP)/configure/CONFIG -DIRS += $(wildcard *ioc*) -DIRS += $(wildcard as*) -DIRS += $(wildcard example*) -include $(CONFIG)/RULES_DIRS - diff --git a/exampleDatabase/iocBoot/exampleDatabase/Makefile b/exampleDatabase/iocBoot/exampleDatabase/Makefile deleted file mode 100644 index e1b9aa4..0000000 --- a/exampleDatabase/iocBoot/exampleDatabase/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -TOP = ../.. -include $(TOP)/configure/CONFIG -ARCH = $(EPICS_HOST_ARCH) -TARGETS = envPaths -include $(TOP)/configure/RULES.ioc diff --git a/exampleDatabase/iocBoot/exampleDatabase/st.cmd b/exampleDatabase/iocBoot/exampleDatabase/st.cmd deleted file mode 100644 index 053423a..0000000 --- a/exampleDatabase/iocBoot/exampleDatabase/st.cmd +++ /dev/null @@ -1,29 +0,0 @@ -< envPaths - -cd ${TOP} - -## Register all support components -dbLoadDatabase("dbd/exampleDatabase.dbd") -exampleDatabase_registerRecordDeviceDriver(pdbbase) - -## Load record instances -dbLoadRecords("db/dbDouble.db","name=double00") -dbLoadRecords("db/dbDouble.db","name=double01") -dbLoadRecords("db/dbDouble.db","name=double02") -dbLoadRecords("db/dbDouble.db","name=double03") -dbLoadRecords("db/dbDouble.db","name=double04") -dbLoadRecords("db/dbDouble.db","name=double05") -dbLoadRecords("db/dbStringArray.db","name=stringArray01") -dbLoadRecords("db/dbEnum.db","name=enum01") -dbLoadRecords("db/dbCounter.db","name=counter01"); -dbLoadRecords("db/dbArray.db","name=doubleArray,type=DOUBLE"); - - -cd ${TOP}/iocBoot/${IOC} -iocInit() -dbl -epicsThreadSleep(2.0) -exampleDatabase -exampleMonitorPlugin -startPVAServer -pvdbl diff --git a/exampleDatabase/src/Makefile b/exampleDatabase/src/Makefile deleted file mode 100644 index a54eb26..0000000 --- a/exampleDatabase/src/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -TOP=.. - -include $(TOP)/configure/CONFIG -#---------------------------------------- -# ADD MACRO DEFINITIONS AFTER THIS LINE - -DBD += exampleDatabase.dbd - -INC += exampleDatabase.h - -LIBRARY += exampleDatabase -exampleDatabase_SRCS += exampleDatabase.cpp -exampleDatabase_SRCS += exampleDatabaseRegister.cpp -exampleDatabase_LIBS += powerSupply -exampleDatabase_LIBS += pvDatabase -exampleDatabase_LIBS += pvAccess -exampleDatabase_LIBS += pvData -exampleDatabase_LIBS += Com -exampleDatabase_LIBS += $(EPICS_BASE_IOC_LIBS) - -PROD_HOST += exampleDatabaseMain -exampleDatabaseMain_SRCS += exampleDatabaseMain.cpp -exampleDatabaseMain_LIBS += exampleDatabase -exampleDatabaseMain_LIBS += powerSupply -exampleDatabaseMain_LIBS += pvDatabase -exampleDatabaseMain_LIBS += pvAccess -exampleDatabaseMain_LIBS += pvData -exampleDatabaseMain_LIBS += Com - -#=========================== - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/exampleDatabase/src/exampleDatabase.cpp b/exampleDatabase/src/exampleDatabase.cpp deleted file mode 100644 index 6396186..0000000 --- a/exampleDatabase/src/exampleDatabase.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/*exampleDatabase.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.07.24 - */ - -/* Author: Marty Kraimer */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#define epicsExportSharedSymbols -#include - -using namespace std; -using std::tr1::static_pointer_cast; -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; - -static FieldCreatePtr fieldCreate = getFieldCreate(); -static StandardFieldPtr standardField = getStandardField(); -static PVDataCreatePtr pvDataCreate = getPVDataCreate(); -static StandardPVFieldPtr standardPVField = getStandardPVField(); - -static void createStructureArrayRecord( - PVDatabasePtr const &master, - string const &recordName) -{ - StringArray names(2); - FieldConstPtrArray fields(2); - names[0] = "name"; - names[1] = "value"; - fields[0] = fieldCreate->createScalar(pvString); - fields[1] = fieldCreate->createScalar(pvString); - - StringArray topNames(1); - FieldConstPtrArray topFields(1); - topNames[0] = "value"; - topFields[0] = fieldCreate->createStructureArray( - fieldCreate->createStructure(names, fields)); - StructureConstPtr top = fieldCreate->createStructure(topNames,topFields); - PVStructurePtr pvStructure = pvDataCreate->createPVStructure(top); - PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure); - bool result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; -} - -static void createRegularUnionArrayRecord( - PVDatabasePtr const &master, - string const &recordName) -{ - StringArray unionNames(2); - FieldConstPtrArray unionFields(2); - unionNames[0] = "string"; - unionNames[1] = "stringArray"; - unionFields[0] = fieldCreate->createScalar(pvString); - unionFields[1] = fieldCreate->createScalarArray(pvString); - - StringArray names(1); - FieldConstPtrArray fields(1); - fields[0] = fieldCreate->createUnionArray(fieldCreate->createUnion(unionNames,unionFields)); - names[0] = "value"; - StructureConstPtr top = fieldCreate->createStructure(names,fields); - PVStructurePtr pvStructure = pvDataCreate->createPVStructure(top); - PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure); - bool result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; -} - -static void createVariantUnionArrayRecord( - PVDatabasePtr const &master, - string const &recordName) -{ - StringArray names(1); - FieldConstPtrArray fields(1); - fields[0] = fieldCreate->createVariantUnionArray(); - names[0] = "value"; - StructureConstPtr top = fieldCreate->createStructure(names,fields); - PVStructurePtr pvStructure = pvDataCreate->createPVStructure(top); - PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure); - bool result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; -} - -static void createRecords( - PVDatabasePtr const &master, - ScalarType scalarType, - string const &recordNamePrefix, - string const &properties) -{ - string recordName = recordNamePrefix; - PVStructurePtr pvStructure = standardPVField->scalar(scalarType,properties); - PVRecordPtr pvRecord = PVRecord::create(recordName,pvStructure); - bool result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; - recordName += "Array"; - pvStructure = standardPVField->scalarArray(scalarType,properties); - pvRecord = PVRecord::create(recordName,pvStructure); - result = master->addRecord(pvRecord); -} - -void ExampleDatabase::create() -{ - PVDatabasePtr master = PVDatabase::getMaster(); - PVRecordPtr pvRecord; - string recordName; - bool result(false); - recordName = "traceRecordPGRPC"; - pvRecord = TraceRecord::create(recordName); - result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; - string properties; - properties = "alarm,timeStamp"; - createRecords(master,pvBoolean,"exampleBoolean",properties); - createRecords(master,pvByte,"exampleByte",properties); - createRecords(master,pvShort,"exampleShort",properties); - createRecords(master,pvInt,"exampleInt",properties); - createRecords(master,pvLong,"exampleLong",properties); - createRecords(master,pvFloat,"exampleFloat",properties); - createRecords(master,pvDouble,"exampleDouble",properties); - createRecords(master,pvDouble,"exampleDouble01",properties); - createRecords(master,pvDouble,"exampleDouble02",properties); - createRecords(master,pvDouble,"exampleDouble03",properties); - createRecords(master,pvDouble,"exampleDouble04",properties); - createRecords(master,pvDouble,"exampleDouble05",properties); - createRecords(master,pvString,"exampleString",properties); - createStructureArrayRecord(master,"exampleStructureArray"); - createRegularUnionArrayRecord(master,"exampleRegularUnionArray"); - createVariantUnionArrayRecord(master,"exampleVariantUnionArray"); - recordName = "examplePowerSupply"; - PVStructurePtr pvStructure = createPowerSupply(); - PowerSupplyPtr psr = PowerSupply::create(recordName,pvStructure); - if(!psr) { - cout << "PowerSupply::create failed" << endl; - } else { - result = master->addRecord(psr); - if(!result) cout<< "record " << recordName << " not added" << endl; - } -} - diff --git a/exampleDatabase/src/exampleDatabase.h b/exampleDatabase/src/exampleDatabase.h deleted file mode 100644 index ba4c2ae..0000000 --- a/exampleDatabase/src/exampleDatabase.h +++ /dev/null @@ -1,38 +0,0 @@ -/* exampleDatabase.h */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.07.24 - */ -#ifndef EXAMPLEDATABASE_H -#define EXAMPLEDATABASE_H - -#ifdef epicsExportSharedSymbols -# define exampledatabaseEpicsExportSharedSymbols -# undef epicsExportSharedSymbols -#endif - -#include - -#ifdef exampledatabaseEpicsExportSharedSymbols -# define epicsExportSharedSymbols -# undef exampledatabaseEpicsExportSharedSymbols -#endif - -#include - -namespace epics { namespace pvDatabase { - -class epicsShareClass ExampleDatabase{ -public: - static void create(); -}; - - -}} - -#endif /* EXAMPLEDATABASE_H */ diff --git a/exampleDatabase/src/exampleDatabaseInclude.dbd b/exampleDatabase/src/exampleDatabaseInclude.dbd deleted file mode 100644 index d1f734b..0000000 --- a/exampleDatabase/src/exampleDatabaseInclude.dbd +++ /dev/null @@ -1 +0,0 @@ -registrar("exampleDatabaseRegister") diff --git a/exampleDatabase/src/exampleDatabaseMain.cpp b/exampleDatabase/src/exampleDatabaseMain.cpp deleted file mode 100644 index 7e19987..0000000 --- a/exampleDatabase/src/exampleDatabaseMain.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/*exampleDatabaseMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - */ - -/* Author: Marty Kraimer */ - -#include - -#include - -#include - -using namespace std; -using namespace epics::pvDatabase; - -int main(int argc,char *argv[]) -{ - ExampleDatabase::create(); - - ContextLocal::shared_pointer contextLocal = ContextLocal::create(); - contextLocal->start(); - - cout << "pvAccess server exampleDatabase running..." << endl; - epics::pvData::PVStringArrayPtr pvNames = PVDatabase::getMaster()->getRecordNames(); - cout << "Hosted records: " << endl << *pvNames << endl; - - contextLocal->waitForExit(); - - return 0; -} diff --git a/exampleDatabase/src/exampleDatabaseRegister.cpp b/exampleDatabase/src/exampleDatabaseRegister.cpp deleted file mode 100644 index 55d17c2..0000000 --- a/exampleDatabase/src/exampleDatabaseRegister.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/*exampleDatabase.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.07.24 - */ - - -/* Author: Marty Kraimer */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; - -static const iocshArg testArg0 = { "prefix", iocshArgString }; -static const iocshArg *testArgs[] = { - &testArg0}; - -static const iocshFuncDef exampleDatabaseFuncDef = { - "exampleDatabase", 1, testArgs}; -static void exampleDatabaseCallFunc(const iocshArgBuf *args) -{ - ExampleDatabase::create(); -} - -static void exampleDatabaseRegister(void) -{ - static int firstTime = 1; - if (firstTime) { - firstTime = 0; - iocshRegister(&exampleDatabaseFuncDef, exampleDatabaseCallFunc); - } -} - -extern "C" { - epicsExportRegistrar(exampleDatabaseRegister); -} diff --git a/exampleLink/Makefile b/exampleLink/Makefile deleted file mode 100644 index 14826ca..0000000 --- a/exampleLink/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -#Makefile at top of application tree -TOP = . -include $(TOP)/configure/CONFIG -DIRS += configure - -DIRS += src -src_DEPEND_DIRS = configure - -DIRS += ioc -ioc_DEPEND_DIRS = src - -DIRS += iocBoot - -include $(TOP)/configure/RULES_TOP - - diff --git a/exampleLink/configure/CONFIG b/exampleLink/configure/CONFIG deleted file mode 100644 index c1a4703..0000000 --- a/exampleLink/configure/CONFIG +++ /dev/null @@ -1,29 +0,0 @@ -# CONFIG - Load build configuration data -# -# Do not make changes to this file! - -# Allow user to override where the build rules come from -RULES = $(EPICS_BASE) - -# RELEASE files point to other application tops -include $(TOP)/configure/RELEASE --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common -ifdef T_A --include $(TOP)/configure/RELEASE.Common.$(T_A) --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) -endif - -CONFIG = $(RULES)/configure -include $(CONFIG)/CONFIG - -# Override the Base definition: -INSTALL_LOCATION = $(TOP) - -# CONFIG_SITE files contain other build configuration settings -include $(TOP)/configure/CONFIG_SITE --include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common -ifdef T_A - -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A) - -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) -endif - diff --git a/exampleLink/configure/CONFIG_SITE b/exampleLink/configure/CONFIG_SITE deleted file mode 100644 index 0165b88..0000000 --- a/exampleLink/configure/CONFIG_SITE +++ /dev/null @@ -1,39 +0,0 @@ -# CONFIG_SITE - -# Make any application-specific changes to the EPICS build -# configuration variables in this file. -# -# Host/target specific settings can be specified in files named -# CONFIG_SITE.$(EPICS_HOST_ARCH).Common -# CONFIG_SITE.Common.$(T_A) -# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) - -# CHECK_RELEASE controls the consistency checking of the support -# applications pointed to by the RELEASE* files. -# Normally CHECK_RELEASE should be set to YES. -# Set CHECK_RELEASE to NO to disable checking completely. -# Set CHECK_RELEASE to WARN to perform consistency checking but -# continue building anyway if conflicts are found. -#CHECK_RELEASE = YES - -# Set this when you only want to compile this application -# for a subset of the cross-compiled target architectures -# that Base is built for. -#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 - -# To install files into a location other than $(TOP) define -# INSTALL_LOCATION here. -#INSTALL_LOCATION= - -# Set this when your IOC and the host use different paths -# to access the application. This will be needed to boot -# from a Microsoft FTP server or with some NFS mounts. -# You must rebuild in the iocBoot directory for this to -# take effect. -#IOCS_APPL_TOP = - -INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv -USR_INCLUDES += -I $(INSTALL_LOCATION)/include - --include $(TOP)/../../CONFIG_SITE.local --include $(TOP)/../configure/CONFIG_SITE.local diff --git a/exampleLink/configure/ExampleRELEASE.local b/exampleLink/configure/ExampleRELEASE.local deleted file mode 100644 index 95e3783..0000000 --- a/exampleLink/configure/ExampleRELEASE.local +++ /dev/null @@ -1,8 +0,0 @@ -EPICS_BASE=/home/install/epics/base -TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top -EPICSV4HOME=/home/hg -PVCOMMON=${EPICSV4HOME}/pvCommonCPP -PVDATA=${EPICSV4HOME}/pvDataCPP -PVACCESS=${EPICSV4HOME}/pvAccessCPP -PVDATABASE=${EPICSV4HOME}/pvDatabaseCPP -PVASRV=${EPICSV4HOME}/pvaSrv diff --git a/exampleLink/configure/Makefile b/exampleLink/configure/Makefile deleted file mode 100644 index 9254309..0000000 --- a/exampleLink/configure/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -TOP=.. - -include $(TOP)/configure/CONFIG - -TARGETS = $(CONFIG_TARGETS) -CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS))) - -include $(TOP)/configure/RULES diff --git a/exampleLink/configure/RELEASE b/exampleLink/configure/RELEASE deleted file mode 100644 index 18dd963..0000000 --- a/exampleLink/configure/RELEASE +++ /dev/null @@ -1,40 +0,0 @@ -# pvDatabaseCPP/example RELEASE - Location of external support modules -# -# IF YOU CHANGE this file or any file it includes you must -# subsequently do a "gnumake rebuild" in the application's -# top level directory. -# -# The build process does not check dependencies against files -# that are outside this application, thus you should also do a -# "gnumake rebuild" in the top level directory after EPICS_BASE -# or any other external module pointed to below is rebuilt. -# -# Host- or target-specific settings can be given in files named -# RELEASE.$(EPICS_HOST_ARCH).Common -# RELEASE.Common.$(T_A) -# RELEASE.$(EPICS_HOST_ARCH).$(T_A) - -# EPICS V4 Developers: Do not edit the locations in this file! -# -# Create a file RELEASE.local pointing to your places -# for the dependencies, e.g. -# PVACCESS = /path/to/epics/pvAccessCPP -# PVDATA = /path/to/epics/pvDataCPP -# PVCOMMON = /path/to/epics/pvCommonCPP -# EPICS_BASE = /path/to/epics/base - -# If this example is built in a directory under pvDatabaseCPP, -# use the following definitions: - -PVDATABASE = $(TOP)/.. --include $(TOP)/../../RELEASE.local --include $(TOP)/../configure/RELEASE.local - -# If you copied this example from pvDatabaseCPP to be built as a -# standalone TOP, define -# PVDATABASE = /path/to/epics/pvDatabaseCPP -# in the appropriate RELEASE[.local], -# and use the following definitions instead: - -#-include $(TOP)/../RELEASE.local -#-include $(TOP)/configure/RELEASE.local diff --git a/exampleLink/configure/RULES b/exampleLink/configure/RULES deleted file mode 100644 index 6d56e14..0000000 --- a/exampleLink/configure/RULES +++ /dev/null @@ -1,6 +0,0 @@ -# RULES - -include $(CONFIG)/RULES - -# Library should be rebuilt because LIBOBJS may have changed. -$(LIBNAME): ../Makefile diff --git a/exampleLink/configure/RULES.ioc b/exampleLink/configure/RULES.ioc deleted file mode 100644 index 901987c..0000000 --- a/exampleLink/configure/RULES.ioc +++ /dev/null @@ -1,2 +0,0 @@ -#RULES.ioc -include $(CONFIG)/RULES.ioc diff --git a/exampleLink/configure/RULES_DIRS b/exampleLink/configure/RULES_DIRS deleted file mode 100644 index 3ba269d..0000000 --- a/exampleLink/configure/RULES_DIRS +++ /dev/null @@ -1,2 +0,0 @@ -#RULES_DIRS -include $(CONFIG)/RULES_DIRS diff --git a/exampleLink/configure/RULES_TOP b/exampleLink/configure/RULES_TOP deleted file mode 100644 index d09d668..0000000 --- a/exampleLink/configure/RULES_TOP +++ /dev/null @@ -1,3 +0,0 @@ -#RULES_TOP -include $(CONFIG)/RULES_TOP - diff --git a/exampleLink/ioc/Db/Makefile b/exampleLink/ioc/Db/Makefile deleted file mode 100644 index 10bca52..0000000 --- a/exampleLink/ioc/Db/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -TOP=../.. -include $(TOP)/configure/CONFIG -#---------------------------------------- -# ADD MACRO DEFINITIONS AFTER THIS LINE - -#---------------------------------------------------- -# Optimization of db files using dbst (DEFAULT: NO) -#DB_OPT = YES - -#---------------------------------------------------- -# Create and install (or just install) -# databases, templates, substitutions like this -DB += ai.db -#---------------------------------------------------- -# If .db template is not named *.template add -# _TEMPLATE = - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/exampleLink/ioc/Db/ai.db b/exampleLink/ioc/Db/ai.db deleted file mode 100644 index f440264..0000000 --- a/exampleLink/ioc/Db/ai.db +++ /dev/null @@ -1,13 +0,0 @@ -record(ai, "$(name)") -{ - field(PREC, "1") - field(EGU, "amps") - field(HIHI, "8") - field(HIGH, "6") - field(LOW, "4") - field(LOLO, "2") - field(HHSV, "MAJOR") - field(HSV, "MINOR") - field(LSV, "MINOR") - field(LLSV, "MAJOR") -} diff --git a/exampleLink/ioc/Makefile b/exampleLink/ioc/Makefile deleted file mode 100644 index 10e0126..0000000 --- a/exampleLink/ioc/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -TOP = .. -include $(TOP)/configure/CONFIG -DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *src*)) -DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Src*)) -DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *db*)) -DIRS := $(DIRS) $(filter-out $(DIRS), $(wildcard *Db*)) -include $(TOP)/configure/RULES_DIRS - diff --git a/exampleLink/ioc/src/Makefile b/exampleLink/ioc/src/Makefile deleted file mode 100644 index ceeef9f..0000000 --- a/exampleLink/ioc/src/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -TOP=../.. - -include $(TOP)/configure/CONFIG -#---------------------------------------- -# ADD MACRO DEFINITIONS AFTER THIS LINE -#============================= - -#================================================== -# Build an IOC support library -# - -#============================= -# build an ioc application - - -DBD += exampleLink.dbd - -PROD_IOC += exampleLink - - -# _registerRecordDeviceDriver.cpp will be created from .dbd -exampleLink_SRCS += exampleLink_registerRecordDeviceDriver.cpp -exampleLink_SRCS_DEFAULT += exampleLinkMain.cpp -exampleLink_SRCS_vxWorks += -nil- - - -# The following adds support from base/src/vxWorks -exampleLink_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary - -exampleLink_LIBS += exampleLink -exampleLink_LIBS += pvDatabase -exampleLink_LIBS += pvaSrv -exampleLink_LIBS += pvAccess -exampleLink_LIBS += pvData -exampleLink_LIBS += Com -exampleLink_LIBS += $(EPICS_BASE_IOC_LIBS) - -#=========================== - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/exampleLink/ioc/src/exampleLinkInclude.dbd b/exampleLink/ioc/src/exampleLinkInclude.dbd deleted file mode 100644 index 7ca071a..0000000 --- a/exampleLink/ioc/src/exampleLinkInclude.dbd +++ /dev/null @@ -1,5 +0,0 @@ -include "base.dbd" -include "PVAServerRegister.dbd" -include "PVAClientRegister.dbd" -include "registerChannelProviderLocal.dbd" -registrar("exampleLinkRegister") diff --git a/exampleLink/ioc/src/exampleLinkMain.cpp b/exampleLink/ioc/src/exampleLinkMain.cpp deleted file mode 100644 index ce620a2..0000000 --- a/exampleLink/ioc/src/exampleLinkMain.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* exampleLinkMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.07.24 - */ - -#include -#include -#include -#include -#include - -#include "epicsExit.h" -#include "epicsThread.h" -#include "iocsh.h" - -int main(int argc,char *argv[]) -{ - if(argc>=2) { - iocsh(argv[1]); - epicsThreadSleep(.2); - } - iocsh(NULL); - epicsExit(0); - return(0); -} diff --git a/exampleLink/iocBoot/Makefile b/exampleLink/iocBoot/Makefile deleted file mode 100644 index 3e914fd..0000000 --- a/exampleLink/iocBoot/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -TOP = .. -include $(TOP)/configure/CONFIG -DIRS += $(wildcard *ioc*) -DIRS += $(wildcard as*) -DIRS += $(wildcard example*) -include $(CONFIG)/RULES_DIRS - diff --git a/exampleLink/iocBoot/exampleLink/Makefile b/exampleLink/iocBoot/exampleLink/Makefile deleted file mode 100644 index e1b9aa4..0000000 --- a/exampleLink/iocBoot/exampleLink/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -TOP = ../.. -include $(TOP)/configure/CONFIG -ARCH = $(EPICS_HOST_ARCH) -TARGETS = envPaths -include $(TOP)/configure/RULES.ioc diff --git a/exampleLink/iocBoot/exampleLink/st.local b/exampleLink/iocBoot/exampleLink/st.local deleted file mode 100644 index 736b0d6..0000000 --- a/exampleLink/iocBoot/exampleLink/st.local +++ /dev/null @@ -1,16 +0,0 @@ -< envPaths - -cd ${TOP} - -## Register all support components -dbLoadDatabase("dbd/exampleLink.dbd") -exampleLink_registerRecordDeviceDriver(pdbbase) - -## Load record instances -dbLoadRecords("db/dbArray.db","name=double01,type=DOUBLE") - -cd ${TOP}/iocBoot/${IOC} -iocInit() -startPVAClient -startPVAServer -exampleLinkCreateRecord exampleLink local doubleArray diff --git a/exampleLink/iocBoot/exampleLink/st.remote b/exampleLink/iocBoot/exampleLink/st.remote deleted file mode 100644 index f7a3b23..0000000 --- a/exampleLink/iocBoot/exampleLink/st.remote +++ /dev/null @@ -1,16 +0,0 @@ -< envPaths - -cd ${TOP} - -## Register all support components -dbLoadDatabase("dbd/exampleLink.dbd") -exampleLink_registerRecordDeviceDriver(pdbbase) - -## Load record instances -dbLoadRecords("db/dbArray.db","name=double01,type=DOUBLE") - -cd ${TOP}/iocBoot/${IOC} -iocInit() -startPVAClient -startPVAServer -exampleLinkCreateRecord exampleLink pvAccess doubleArray diff --git a/exampleLink/src/Makefile b/exampleLink/src/Makefile deleted file mode 100644 index 0856c00..0000000 --- a/exampleLink/src/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -TOP=.. - -include $(TOP)/configure/CONFIG -#---------------------------------------- -# ADD MACRO DEFINITIONS AFTER THIS LINE -#============================= - -#================================================== -# Build an IOC support library -# - -DBD += exampleLink.dbd - -INC += exampleLink.h - -LIBRARY_IOC += exampleLink -exampleLink_SRCS += exampleLink.cpp -exampleLink_SRCS += exampleLinkRegister.cpp -exampleLink_LIBS += pvDatabase -exampleLink_LIBS += pvAccess -exampleLink_LIBS += pvData -exampleLink_LIBS += Com -exampleLink_LIBS += $(EPICS_BASE_IOC_LIBS) - -#=========================== - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/exampleLink/src/exampleLink.cpp b/exampleLink/src/exampleLink.cpp deleted file mode 100644 index 1d358cb..0000000 --- a/exampleLink/src/exampleLink.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* exampleLink.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.02 - */ - -#include - -#define epicsExportSharedSymbols -#include - -using namespace epics::pvData; -using namespace epics::pvAccess; -using std::tr1::static_pointer_cast; -using std::tr1::dynamic_pointer_cast; -using std::cout; -using std::endl; -using std::string; - -namespace epics { namespace pvDatabase { - -ExampleLinkPtr ExampleLink::create( - string const & recordName, - string const & providerName, - string const & channelName) -{ - PVStructurePtr pvStructure = getStandardPVField()->scalarArray( - pvDouble,"alarm.timeStamp"); - ExampleLinkPtr pvRecord( - new ExampleLink( - recordName,providerName,channelName,pvStructure)); - if(!pvRecord->init()) pvRecord.reset(); - return pvRecord; -} - -ExampleLink::ExampleLink( - string const & recordName, - string providerName, - string channelName, - PVStructurePtr const & pvStructure) -: PVRecord(recordName,pvStructure), - providerName(providerName), - channelName(channelName) -{ -} - -void ExampleLink::destroy() -{ - PVRecord::destroy(); -} - -bool ExampleLink::init() -{ - initPVRecord(); - - PVStructurePtr pvStructure = getPVRecordStructure()->getPVStructure(); - pvTimeStamp.attach(pvStructure->getSubField("timeStamp")); - pvAlarm.attach(pvStructure->getSubField("alarm")); - pvValue = pvStructure->getSubField("value"); - if(!pvValue) { - return false; - } - ChannelProvider::shared_pointer provider = - getChannelProviderRegistry()->getProvider(providerName); - if(!provider) { - cout << getRecordName() << " provider " - << providerName << " does not exist" << endl; - return false; - } - ChannelRequester::shared_pointer channelRequester = - dynamic_pointer_cast(getPtrSelf()); - channel = provider->createChannel(channelName,channelRequester); - event.wait(); - if(!status.isOK()) { - cout << getRecordName() << " createChannel failed " - << status.getMessage() << endl; - return false; - } - ChannelGetRequester::shared_pointer channelGetRequester = - dynamic_pointer_cast(getPtrSelf()); - PVStructurePtr pvRequest = CreateRequest::create()->createRequest( - "value,alarm,timeStamp"); - channelGet = channel->createChannelGet(channelGetRequester,pvRequest); - event.wait(); - if(!status.isOK()) { - cout << getRecordName() << " createChannelGet failed " - << status.getMessage() << endl; - return false; - } - getPVValue = getPVStructure->getSubField("value"); - if(!getPVValue) { - cout << getRecordName() << " get value not PVDoubleArray" << endl; - return false; - } - return true; -} - -void ExampleLink::process() -{ - status = Status::Ok; - channelGet->get(); - event.wait(); - timeStamp.getCurrent(); - pvTimeStamp.set(timeStamp); - AlarmSeverity severity(noAlarm); - if(!status.isOK()) { - switch(status.getType()) { - case Status::STATUSTYPE_OK: severity = noAlarm; break; - case Status::STATUSTYPE_WARNING: severity = minorAlarm; break; - case Status::STATUSTYPE_ERROR: severity = majorAlarm; break; - case Status::STATUSTYPE_FATAL: severity = invalidAlarm; break; - } - alarm.setSeverity(severity); - } else { - pvValue->copyUnchecked(*getPVValue); - } - alarm.setMessage(status.getMessage()); - pvAlarm.set(alarm); -} - -void ExampleLink::channelCreated( - const Status& status, - Channel::shared_pointer const & channel) -{ - this->status = status; - this->channel = channel; - event.signal(); -} - -void ExampleLink::channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState) -{ -} - -void ExampleLink::channelGetConnect( - const Status& status, - ChannelGet::shared_pointer const & channelGet, - StructureConstPtr const & structure) -{ - this->status = status; - this->channelGet = channelGet; - getPVStructure = getPVDataCreate()->createPVStructure(structure); - event.signal(); -} - -void ExampleLink::getDone( - const Status& status, - ChannelGet::shared_pointer const & channelGet, - PVStructurePtr const & pvStructure, - BitSetPtr const & bitSet) -{ - this->status = status; - getPVStructure->copyUnchecked(*pvStructure); - this->bitSet = bitSet; - event.signal(); -} - -}} diff --git a/exampleLink/src/exampleLink.h b/exampleLink/src/exampleLink.h deleted file mode 100644 index ac0d65f..0000000 --- a/exampleLink/src/exampleLink.h +++ /dev/null @@ -1,102 +0,0 @@ -/* exampleLink.h */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.08.02 - */ -#ifndef EXAMPLEPVADOUBLEARRAYGET_H -#define EXAMPLEPVADOUBLEARRAYGET_H - -#ifdef epicsExportSharedSymbols -# define exampleLinkEpicsExportSharedSymbols -# undef epicsExportSharedSymbols -#endif - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef exampleLinkEpicsExportSharedSymbols -# define epicsExportSharedSymbols -# undef exampleLinkEpicsExportSharedSymbols -#endif - -#include - -namespace epics { namespace pvDatabase { - - -class ExampleLink; -typedef std::tr1::shared_ptr ExampleLinkPtr; - -class epicsShareClass ExampleLink : - public PVRecord, - public epics::pvAccess::ChannelRequester, - public epics::pvAccess::ChannelGetRequester -{ -public: - POINTER_DEFINITIONS(ExampleLink); - static ExampleLinkPtr create( - std::string const & recordName, - std::string const & providerName, - std::string const & channelName - ); - virtual ~ExampleLink() {} - virtual void destroy(); - virtual bool init(); - virtual void process(); - virtual void channelCreated( - const epics::pvData::Status& status, - epics::pvAccess::Channel::shared_pointer const & channel); - virtual void channelStateChange( - epics::pvAccess::Channel::shared_pointer const & channel, - epics::pvAccess::Channel::ConnectionState connectionState); - virtual void channelGetConnect( - const epics::pvData::Status& status, - epics::pvAccess::ChannelGet::shared_pointer const & channelGet, - epics::pvData::StructureConstPtr const & structure); - virtual void getDone( - const epics::pvData::Status& status, - epics::pvAccess::ChannelGet::shared_pointer const & channelGet, - epics::pvData::PVStructurePtr const & pvStructure, - epics::pvData::BitSetPtr const &bitSet); - virtual std::string getRequesterName() {return channelName;} - virtual void message( - std::string const & message, - epics::pvData::MessageType messageType) - { - std::cout << "Why is ExampleLink::message called\n"; - } -private: - ExampleLink(std::string const & recordName, - std::string providerName, - std::string channelName, - epics::pvData::PVStructurePtr const & pvStructure); - std::string providerName; - std::string channelName; - epics::pvData::PVDoubleArrayPtr pvValue; - epics::pvData::PVTimeStamp pvTimeStamp; - epics::pvData::TimeStamp timeStamp; - epics::pvData::PVAlarm pvAlarm; - epics::pvData::Alarm alarm; - epics::pvAccess::Channel::shared_pointer channel; - epics::pvAccess::ChannelGet::shared_pointer channelGet; - epics::pvData::Event event; - epics::pvData::Status status; - epics::pvData::PVStructurePtr getPVStructure; - epics::pvData::BitSetPtr bitSet; - epics::pvData::PVDoubleArrayPtr getPVValue; -}; - -}} - -#endif /* EXAMPLEPVADOUBLEARRAYGET_H */ diff --git a/exampleLink/src/exampleLinkInclude.dbd b/exampleLink/src/exampleLinkInclude.dbd deleted file mode 100644 index 4149d0b..0000000 --- a/exampleLink/src/exampleLinkInclude.dbd +++ /dev/null @@ -1 +0,0 @@ -registrar("exampleLinkRegister") diff --git a/exampleLink/src/exampleLinkRegister.cpp b/exampleLink/src/exampleLinkRegister.cpp deleted file mode 100644 index ed1663a..0000000 --- a/exampleLink/src/exampleLinkRegister.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/*exampleLink.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.07.24 - */ - - -/* Author: Marty Kraimer */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; -using std::cout; -using std::endl; -using std::string; - -static StandardPVFieldPtr standardPVField = getStandardPVField(); - -static const iocshArg testArg0 = { "recordName", iocshArgString }; -static const iocshArg testArg1 = { "providerName", iocshArgString }; -static const iocshArg testArg2 = { "channelName", iocshArgString }; -static const iocshArg *testArgs[] = { - &testArg0,&testArg1,&testArg2}; - -static const iocshFuncDef exampleLinkFuncDef = { - "exampleLinkCreateRecord", 3, testArgs}; -static void exampleLinkCallFunc(const iocshArgBuf *args) -{ - PVDatabasePtr master = PVDatabase::getMaster(); - PVRecordPtr pvRecord; - bool result(false); - string recordName; - PVStructurePtr pvStructure = standardPVField->scalarArray( - pvDouble,"alarm,timeStamp"); - pvRecord = PVRecord::create("doubleArray",pvStructure); - result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; - - recordName = args[0].sval; - char *providerName = args[1].sval; - char *channelName = args[2].sval; - ExampleLinkPtr record = ExampleLink::create(recordName,providerName,channelName); - if(record) - result = master->addRecord(record); - if(!result) cout << "recordname" << " not added" << endl; -} - -static void exampleLinkRegister(void) -{ - static int firstTime = 1; - if (firstTime) { - firstTime = 0; - iocshRegister(&exampleLinkFuncDef, exampleLinkCallFunc); - } -} - -extern "C" { - epicsExportRegistrar(exampleLinkRegister); -} diff --git a/exampleLink/src/exampleLinkRegister.dbd b/exampleLink/src/exampleLinkRegister.dbd deleted file mode 100644 index 4149d0b..0000000 --- a/exampleLink/src/exampleLinkRegister.dbd +++ /dev/null @@ -1 +0,0 @@ -registrar("exampleLinkRegister") diff --git a/examplePowerSupply/Makefile b/examplePowerSupply/Makefile deleted file mode 100644 index 81fbed6..0000000 --- a/examplePowerSupply/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# Makefile at top of application tree - -TOP = . -include $(TOP)/configure/CONFIG - -DIRS += configure - -DIRS += src -src_DEPEND_DIRS = configure - -DIRS += ioc -ioc_DEPEND_DIRS = src - -DIRS += iocBoot -iocBoot_DEPEND_DIRS = src - -include $(TOP)/configure/RULES_TOP - - diff --git a/examplePowerSupply/configure/CONFIG b/examplePowerSupply/configure/CONFIG deleted file mode 100644 index c1a4703..0000000 --- a/examplePowerSupply/configure/CONFIG +++ /dev/null @@ -1,29 +0,0 @@ -# CONFIG - Load build configuration data -# -# Do not make changes to this file! - -# Allow user to override where the build rules come from -RULES = $(EPICS_BASE) - -# RELEASE files point to other application tops -include $(TOP)/configure/RELEASE --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common -ifdef T_A --include $(TOP)/configure/RELEASE.Common.$(T_A) --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) -endif - -CONFIG = $(RULES)/configure -include $(CONFIG)/CONFIG - -# Override the Base definition: -INSTALL_LOCATION = $(TOP) - -# CONFIG_SITE files contain other build configuration settings -include $(TOP)/configure/CONFIG_SITE --include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common -ifdef T_A - -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A) - -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) -endif - diff --git a/examplePowerSupply/configure/CONFIG_SITE b/examplePowerSupply/configure/CONFIG_SITE deleted file mode 100644 index 0165b88..0000000 --- a/examplePowerSupply/configure/CONFIG_SITE +++ /dev/null @@ -1,39 +0,0 @@ -# CONFIG_SITE - -# Make any application-specific changes to the EPICS build -# configuration variables in this file. -# -# Host/target specific settings can be specified in files named -# CONFIG_SITE.$(EPICS_HOST_ARCH).Common -# CONFIG_SITE.Common.$(T_A) -# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) - -# CHECK_RELEASE controls the consistency checking of the support -# applications pointed to by the RELEASE* files. -# Normally CHECK_RELEASE should be set to YES. -# Set CHECK_RELEASE to NO to disable checking completely. -# Set CHECK_RELEASE to WARN to perform consistency checking but -# continue building anyway if conflicts are found. -#CHECK_RELEASE = YES - -# Set this when you only want to compile this application -# for a subset of the cross-compiled target architectures -# that Base is built for. -#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 - -# To install files into a location other than $(TOP) define -# INSTALL_LOCATION here. -#INSTALL_LOCATION= - -# Set this when your IOC and the host use different paths -# to access the application. This will be needed to boot -# from a Microsoft FTP server or with some NFS mounts. -# You must rebuild in the iocBoot directory for this to -# take effect. -#IOCS_APPL_TOP = - -INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv -USR_INCLUDES += -I $(INSTALL_LOCATION)/include - --include $(TOP)/../../CONFIG_SITE.local --include $(TOP)/../configure/CONFIG_SITE.local diff --git a/examplePowerSupply/configure/ExampleRELEASE.local b/examplePowerSupply/configure/ExampleRELEASE.local deleted file mode 100644 index 7f6ee6d..0000000 --- a/examplePowerSupply/configure/ExampleRELEASE.local +++ /dev/null @@ -1,9 +0,0 @@ -EPICS_BASE=/home/install/epics/base -TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top -EPICSV4HOME=/home/hg -PVCOMMON=${EPICSV4HOME}/pvCommonCPP -PVDATA=${EPICSV4HOME}/pvDataCPP -PVACCESS=${EPICSV4HOME}/pvAccessCPP -PVDATABASE=${EPICSV4HOME}/pvDatabaseCPP -PVDATABASETEST=${EPICSV4HOME}/pvDatabaseCPP/test -PVASRV=${EPICSV4HOME}/pvaSrv diff --git a/examplePowerSupply/configure/Makefile b/examplePowerSupply/configure/Makefile deleted file mode 100644 index 9254309..0000000 --- a/examplePowerSupply/configure/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -TOP=.. - -include $(TOP)/configure/CONFIG - -TARGETS = $(CONFIG_TARGETS) -CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS))) - -include $(TOP)/configure/RULES diff --git a/examplePowerSupply/configure/RELEASE b/examplePowerSupply/configure/RELEASE deleted file mode 100644 index 3b2f69f..0000000 --- a/examplePowerSupply/configure/RELEASE +++ /dev/null @@ -1,42 +0,0 @@ -# pvDatabaseCPP/example RELEASE - Location of external support modules -# -# IF YOU CHANGE this file or any file it includes you must -# subsequently do a "gnumake rebuild" in the application's -# top level directory. -# -# The build process does not check dependencies against files -# that are outside this application, thus you should also do a -# "gnumake rebuild" in the top level directory after EPICS_BASE -# or any other external module pointed to below is rebuilt. -# -# Host- or target-specific settings can be given in files named -# RELEASE.$(EPICS_HOST_ARCH).Common -# RELEASE.Common.$(T_A) -# RELEASE.$(EPICS_HOST_ARCH).$(T_A) - -# EPICS V4 Developers: Do not edit the locations in this file! -# -# Create a file RELEASE.local pointing to your places -# for the dependencies, e.g. -# PVACCESS = /path/to/epics/pvAccessCPP -# PVDATA = /path/to/epics/pvDataCPP -# PVCOMMON = /path/to/epics/pvCommonCPP -# EPICS_BASE = /path/to/epics/base - -# If this example is built in a directory under pvDatabaseCPP, -# use the following definitions: - -PVDATABASE = $(TOP)/.. -PVDATABASETEST = $(TOP)/../test --include $(TOP)/../../RELEASE.local --include $(TOP)/../configure/RELEASE.local - -# If you copied this example from pvDatabaseCPP to be built as a -# standalone TOP, define -# PVDATABASE = /path/to/epics/pvDatabaseCPP -# PVDATABASETEST = /path/to/epics/pvDatabaseCPP/test -# in the appropriate RELEASE[.local], -# and use the following definitions instead: - -#-include $(TOP)/../RELEASE.local -#-include $(TOP)/configure/RELEASE.local diff --git a/examplePowerSupply/configure/RULES b/examplePowerSupply/configure/RULES deleted file mode 100644 index 6d56e14..0000000 --- a/examplePowerSupply/configure/RULES +++ /dev/null @@ -1,6 +0,0 @@ -# RULES - -include $(CONFIG)/RULES - -# Library should be rebuilt because LIBOBJS may have changed. -$(LIBNAME): ../Makefile diff --git a/examplePowerSupply/configure/RULES.ioc b/examplePowerSupply/configure/RULES.ioc deleted file mode 100644 index 901987c..0000000 --- a/examplePowerSupply/configure/RULES.ioc +++ /dev/null @@ -1,2 +0,0 @@ -#RULES.ioc -include $(CONFIG)/RULES.ioc diff --git a/examplePowerSupply/configure/RULES_DIRS b/examplePowerSupply/configure/RULES_DIRS deleted file mode 100644 index 3ba269d..0000000 --- a/examplePowerSupply/configure/RULES_DIRS +++ /dev/null @@ -1,2 +0,0 @@ -#RULES_DIRS -include $(CONFIG)/RULES_DIRS diff --git a/examplePowerSupply/configure/RULES_TOP b/examplePowerSupply/configure/RULES_TOP deleted file mode 100644 index d09d668..0000000 --- a/examplePowerSupply/configure/RULES_TOP +++ /dev/null @@ -1,3 +0,0 @@ -#RULES_TOP -include $(CONFIG)/RULES_TOP - diff --git a/examplePowerSupply/ioc/Db/Makefile b/examplePowerSupply/ioc/Db/Makefile deleted file mode 100644 index 1e9fd60..0000000 --- a/examplePowerSupply/ioc/Db/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -TOP=../.. -include $(TOP)/configure/CONFIG -#---------------------------------------- -# ADD MACRO DEFINITIONS AFTER THIS LINE - -#---------------------------------------------------- -# Optimization of db files using dbst (DEFAULT: NO) -#DB_OPT = YES - -#---------------------------------------------------- -# Create and install (or just install) -# databases, templates, substitutions like this -DB += dbScalar.db -DB += dbInteger.db -DB += dbArray.db -DB += dbString.db -DB += dbStringArray.db -DB += dbEnum.db -DB += dbCounter.db - -#---------------------------------------------------- -# If .db template is not named *.template add -# _TEMPLATE = - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/examplePowerSupply/ioc/Db/dbArray.db b/examplePowerSupply/ioc/Db/dbArray.db deleted file mode 100644 index 76506a7..0000000 --- a/examplePowerSupply/ioc/Db/dbArray.db +++ /dev/null @@ -1,9 +0,0 @@ -record(waveform, "$(name)") -{ - field(NELM,"5") - field(NORD,"5") - field(FTVL,"$(type)") - field(EGU, "Counts") - field(HOPR, "10") - field(LOPR, "0") -} diff --git a/examplePowerSupply/ioc/Db/dbCounter.db b/examplePowerSupply/ioc/Db/dbCounter.db deleted file mode 100644 index 06aec31..0000000 --- a/examplePowerSupply/ioc/Db/dbCounter.db +++ /dev/null @@ -1,20 +0,0 @@ -record(calc, "${name}") -{ - field(DESC, "Counter") - field(SCAN,"1 second") - field(CALC, "(A_registerRecordDeviceDriver.cpp will be created from .dbd -powerSupply_SRCS += powerSupply_registerRecordDeviceDriver.cpp -powerSupply_SRCS_DEFAULT += powerSupplyMain.cpp -powerSupply_SRCS_vxWorks += -nil- - - -# The following adds support from base/src/vxWorks -powerSupply_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary - -powerSupply_LIBS += powerSupply -powerSupply_LIBS += pvDatabase -powerSupply_LIBS += pvaSrv -powerSupply_LIBS += pvAccess -powerSupply_LIBS += pvData -powerSupply_LIBS += $(EPICS_BASE_IOC_LIBS) - -#=========================== - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/examplePowerSupply/ioc/src/powerSupplyInclude.dbd b/examplePowerSupply/ioc/src/powerSupplyInclude.dbd deleted file mode 100644 index 02758f6..0000000 --- a/examplePowerSupply/ioc/src/powerSupplyInclude.dbd +++ /dev/null @@ -1,5 +0,0 @@ -include "base.dbd" -include "PVAServerRegister.dbd" -include "registerChannelProviderLocal.dbd" -include "dbPv.dbd" -include "powerSupplyRegister.dbd" diff --git a/examplePowerSupply/ioc/src/powerSupplyMain.cpp b/examplePowerSupply/ioc/src/powerSupplyMain.cpp deleted file mode 100644 index f7c1208..0000000 --- a/examplePowerSupply/ioc/src/powerSupplyMain.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* powerSupplyMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.07.24 - */ - -#include -#include -#include -#include -#include - -#include "epicsExit.h" -#include "epicsThread.h" -#include "iocsh.h" - -int main(int argc,char *argv[]) -{ - if(argc>=2) { - iocsh(argv[1]); - epicsThreadSleep(.2); - } - iocsh(NULL); - epicsExit(0); - return(0); -} diff --git a/examplePowerSupply/iocBoot/Makefile b/examplePowerSupply/iocBoot/Makefile deleted file mode 100644 index 315ee35..0000000 --- a/examplePowerSupply/iocBoot/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -TOP = .. -include $(TOP)/configure/CONFIG -DIRS += $(wildcard *ioc*) -DIRS += $(wildcard as*) -DIRS += $(wildcard power*) -include $(CONFIG)/RULES_DIRS - diff --git a/examplePowerSupply/iocBoot/powerSupply/Makefile b/examplePowerSupply/iocBoot/powerSupply/Makefile deleted file mode 100644 index e1b9aa4..0000000 --- a/examplePowerSupply/iocBoot/powerSupply/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -TOP = ../.. -include $(TOP)/configure/CONFIG -ARCH = $(EPICS_HOST_ARCH) -TARGETS = envPaths -include $(TOP)/configure/RULES.ioc diff --git a/examplePowerSupply/iocBoot/powerSupply/st.cmd b/examplePowerSupply/iocBoot/powerSupply/st.cmd deleted file mode 100644 index b8388f4..0000000 --- a/examplePowerSupply/iocBoot/powerSupply/st.cmd +++ /dev/null @@ -1,23 +0,0 @@ -< envPaths - -cd ${TOP} - -## Register all support components -dbLoadDatabase("dbd/powerSupply.dbd") -powerSupply_registerRecordDeviceDriver(pdbbase) - -## Load record instances -dbLoadRecords("db/dbScalar.db","name=pvdouble,type=ao") -dbLoadRecords("db/dbArray.db","name=pvdoubleArray,type=DOUBLE") -dbLoadRecords("db/dbStringArray.db","name=pvstringArray") -dbLoadRecords("db/dbEnum.db","name=pvenum") -dbLoadRecords("db/dbCounter.db","name=pvcounter"); - -cd ${TOP}/iocBoot/${IOC} -iocInit() -dbl -epicsThreadSleep(1.0) -powerSupplyCreateRecord powerSupply -startPVAServer -pvdbl - diff --git a/examplePowerSupply/src/Makefile b/examplePowerSupply/src/Makefile deleted file mode 100644 index ded3df1..0000000 --- a/examplePowerSupply/src/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -TOP=.. - -include $(TOP)/configure/CONFIG -#---------------------------------------- -# ADD MACRO DEFINITIONS AFTER THIS LINE -#============================= - -#================================================== -# Build an IOC support library -# - - -PROD_HOST += powerSupplyMain -powerSupplyMain_SRCS += powerSupplyMain.cpp - -powerSupplyMain_LIBS += pvDatabase -powerSupplyMain_LIBS += pvAccess -powerSupplyMain_LIBS += pvData -powerSupplyMain_LIBS += Com -powerSupplyMain_LIBS += powerSupply - -#=========================== - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/examplePowerSupply/src/powerSupplyMain.cpp b/examplePowerSupply/src/powerSupplyMain.cpp deleted file mode 100644 index f344d39..0000000 --- a/examplePowerSupply/src/powerSupplyMain.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/*powerSupplyMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - */ - -/* Author: Marty Kraimer */ - -#include -#include - -#include -#include -#include - -using namespace std; -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; - - -int main(int argc,char *argv[]) -{ - PVDatabasePtr master = PVDatabase::getMaster(); - - PVRecordPtr pvRecord; - bool result = false; - string recordName; - - recordName = "powerSupply"; - PVStructurePtr pv = createPowerSupply(); - pvRecord = PowerSupply::create(recordName,pv); - result = master->addRecord(pvRecord); - cout << "result of addRecord " << recordName << " " << result << endl; - - recordName = "traceRecordPGRPC"; - pvRecord = TraceRecord::create(recordName); - result = master->addRecord(pvRecord); - if (!result) cout<< "record " << recordName << " not added" << endl; - - ContextLocal::shared_pointer contextLocal = ContextLocal::create(); - contextLocal->start(true); - - return 0; -} - diff --git a/examplePowerSupply/src/powerSupplyRegisterInclude.dbd b/examplePowerSupply/src/powerSupplyRegisterInclude.dbd deleted file mode 100644 index 1444fc1..0000000 --- a/examplePowerSupply/src/powerSupplyRegisterInclude.dbd +++ /dev/null @@ -1 +0,0 @@ -registrar("powerSupplyRegister") diff --git a/exampleRPC/Makefile b/exampleRPC/Makefile deleted file mode 100644 index 14826ca..0000000 --- a/exampleRPC/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -#Makefile at top of application tree -TOP = . -include $(TOP)/configure/CONFIG -DIRS += configure - -DIRS += src -src_DEPEND_DIRS = configure - -DIRS += ioc -ioc_DEPEND_DIRS = src - -DIRS += iocBoot - -include $(TOP)/configure/RULES_TOP - - diff --git a/exampleRPC/configure/CONFIG b/exampleRPC/configure/CONFIG deleted file mode 100644 index c1a4703..0000000 --- a/exampleRPC/configure/CONFIG +++ /dev/null @@ -1,29 +0,0 @@ -# CONFIG - Load build configuration data -# -# Do not make changes to this file! - -# Allow user to override where the build rules come from -RULES = $(EPICS_BASE) - -# RELEASE files point to other application tops -include $(TOP)/configure/RELEASE --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common -ifdef T_A --include $(TOP)/configure/RELEASE.Common.$(T_A) --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) -endif - -CONFIG = $(RULES)/configure -include $(CONFIG)/CONFIG - -# Override the Base definition: -INSTALL_LOCATION = $(TOP) - -# CONFIG_SITE files contain other build configuration settings -include $(TOP)/configure/CONFIG_SITE --include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common -ifdef T_A - -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A) - -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) -endif - diff --git a/exampleRPC/configure/CONFIG_SITE b/exampleRPC/configure/CONFIG_SITE deleted file mode 100644 index 0165b88..0000000 --- a/exampleRPC/configure/CONFIG_SITE +++ /dev/null @@ -1,39 +0,0 @@ -# CONFIG_SITE - -# Make any application-specific changes to the EPICS build -# configuration variables in this file. -# -# Host/target specific settings can be specified in files named -# CONFIG_SITE.$(EPICS_HOST_ARCH).Common -# CONFIG_SITE.Common.$(T_A) -# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) - -# CHECK_RELEASE controls the consistency checking of the support -# applications pointed to by the RELEASE* files. -# Normally CHECK_RELEASE should be set to YES. -# Set CHECK_RELEASE to NO to disable checking completely. -# Set CHECK_RELEASE to WARN to perform consistency checking but -# continue building anyway if conflicts are found. -#CHECK_RELEASE = YES - -# Set this when you only want to compile this application -# for a subset of the cross-compiled target architectures -# that Base is built for. -#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 - -# To install files into a location other than $(TOP) define -# INSTALL_LOCATION here. -#INSTALL_LOCATION= - -# Set this when your IOC and the host use different paths -# to access the application. This will be needed to boot -# from a Microsoft FTP server or with some NFS mounts. -# You must rebuild in the iocBoot directory for this to -# take effect. -#IOCS_APPL_TOP = - -INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv -USR_INCLUDES += -I $(INSTALL_LOCATION)/include - --include $(TOP)/../../CONFIG_SITE.local --include $(TOP)/../configure/CONFIG_SITE.local diff --git a/exampleRPC/configure/ExampleRELEASE.local b/exampleRPC/configure/ExampleRELEASE.local deleted file mode 100644 index 95e3783..0000000 --- a/exampleRPC/configure/ExampleRELEASE.local +++ /dev/null @@ -1,8 +0,0 @@ -EPICS_BASE=/home/install/epics/base -TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top -EPICSV4HOME=/home/hg -PVCOMMON=${EPICSV4HOME}/pvCommonCPP -PVDATA=${EPICSV4HOME}/pvDataCPP -PVACCESS=${EPICSV4HOME}/pvAccessCPP -PVDATABASE=${EPICSV4HOME}/pvDatabaseCPP -PVASRV=${EPICSV4HOME}/pvaSrv diff --git a/exampleRPC/configure/Makefile b/exampleRPC/configure/Makefile deleted file mode 100644 index 9254309..0000000 --- a/exampleRPC/configure/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -TOP=.. - -include $(TOP)/configure/CONFIG - -TARGETS = $(CONFIG_TARGETS) -CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS))) - -include $(TOP)/configure/RULES diff --git a/exampleRPC/configure/RELEASE b/exampleRPC/configure/RELEASE deleted file mode 100644 index 18dd963..0000000 --- a/exampleRPC/configure/RELEASE +++ /dev/null @@ -1,40 +0,0 @@ -# pvDatabaseCPP/example RELEASE - Location of external support modules -# -# IF YOU CHANGE this file or any file it includes you must -# subsequently do a "gnumake rebuild" in the application's -# top level directory. -# -# The build process does not check dependencies against files -# that are outside this application, thus you should also do a -# "gnumake rebuild" in the top level directory after EPICS_BASE -# or any other external module pointed to below is rebuilt. -# -# Host- or target-specific settings can be given in files named -# RELEASE.$(EPICS_HOST_ARCH).Common -# RELEASE.Common.$(T_A) -# RELEASE.$(EPICS_HOST_ARCH).$(T_A) - -# EPICS V4 Developers: Do not edit the locations in this file! -# -# Create a file RELEASE.local pointing to your places -# for the dependencies, e.g. -# PVACCESS = /path/to/epics/pvAccessCPP -# PVDATA = /path/to/epics/pvDataCPP -# PVCOMMON = /path/to/epics/pvCommonCPP -# EPICS_BASE = /path/to/epics/base - -# If this example is built in a directory under pvDatabaseCPP, -# use the following definitions: - -PVDATABASE = $(TOP)/.. --include $(TOP)/../../RELEASE.local --include $(TOP)/../configure/RELEASE.local - -# If you copied this example from pvDatabaseCPP to be built as a -# standalone TOP, define -# PVDATABASE = /path/to/epics/pvDatabaseCPP -# in the appropriate RELEASE[.local], -# and use the following definitions instead: - -#-include $(TOP)/../RELEASE.local -#-include $(TOP)/configure/RELEASE.local diff --git a/exampleRPC/configure/RULES b/exampleRPC/configure/RULES deleted file mode 100644 index 6d56e14..0000000 --- a/exampleRPC/configure/RULES +++ /dev/null @@ -1,6 +0,0 @@ -# RULES - -include $(CONFIG)/RULES - -# Library should be rebuilt because LIBOBJS may have changed. -$(LIBNAME): ../Makefile diff --git a/exampleRPC/configure/RULES.ioc b/exampleRPC/configure/RULES.ioc deleted file mode 100644 index 901987c..0000000 --- a/exampleRPC/configure/RULES.ioc +++ /dev/null @@ -1,2 +0,0 @@ -#RULES.ioc -include $(CONFIG)/RULES.ioc diff --git a/exampleRPC/configure/RULES_DIRS b/exampleRPC/configure/RULES_DIRS deleted file mode 100644 index 3ba269d..0000000 --- a/exampleRPC/configure/RULES_DIRS +++ /dev/null @@ -1,2 +0,0 @@ -#RULES_DIRS -include $(CONFIG)/RULES_DIRS diff --git a/exampleRPC/configure/RULES_TOP b/exampleRPC/configure/RULES_TOP deleted file mode 100644 index d09d668..0000000 --- a/exampleRPC/configure/RULES_TOP +++ /dev/null @@ -1,3 +0,0 @@ -#RULES_TOP -include $(CONFIG)/RULES_TOP - diff --git a/exampleRPC/ioc/Db/Makefile b/exampleRPC/ioc/Db/Makefile deleted file mode 100644 index 1e9fd60..0000000 --- a/exampleRPC/ioc/Db/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -TOP=../.. -include $(TOP)/configure/CONFIG -#---------------------------------------- -# ADD MACRO DEFINITIONS AFTER THIS LINE - -#---------------------------------------------------- -# Optimization of db files using dbst (DEFAULT: NO) -#DB_OPT = YES - -#---------------------------------------------------- -# Create and install (or just install) -# databases, templates, substitutions like this -DB += dbScalar.db -DB += dbInteger.db -DB += dbArray.db -DB += dbString.db -DB += dbStringArray.db -DB += dbEnum.db -DB += dbCounter.db - -#---------------------------------------------------- -# If .db template is not named *.template add -# _TEMPLATE = - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/exampleRPC/ioc/Db/dbArray.db b/exampleRPC/ioc/Db/dbArray.db deleted file mode 100644 index 76506a7..0000000 --- a/exampleRPC/ioc/Db/dbArray.db +++ /dev/null @@ -1,9 +0,0 @@ -record(waveform, "$(name)") -{ - field(NELM,"5") - field(NORD,"5") - field(FTVL,"$(type)") - field(EGU, "Counts") - field(HOPR, "10") - field(LOPR, "0") -} diff --git a/exampleRPC/ioc/Db/dbCounter.db b/exampleRPC/ioc/Db/dbCounter.db deleted file mode 100644 index 06aec31..0000000 --- a/exampleRPC/ioc/Db/dbCounter.db +++ /dev/null @@ -1,20 +0,0 @@ -record(calc, "${name}") -{ - field(DESC, "Counter") - field(SCAN,"1 second") - field(CALC, "(A_registerRecordDeviceDriver.cpp will be created from .dbd -exampleRPC_SRCS += exampleRPC_registerRecordDeviceDriver.cpp -exampleRPC_SRCS_DEFAULT += exampleRPCMain.cpp -exampleRPC_SRCS_vxWorks += -nil- - - -# The following adds support from base/src/vxWorks -exampleRPC_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary - -exampleRPC_LIBS += exampleRPC -exampleRPC_LIBS += pvDatabase -exampleRPC_LIBS += pvaSrv -exampleRPC_LIBS += pvAccess -exampleRPC_LIBS += pvData -exampleRPC_LIBS += $(EPICS_BASE_IOC_LIBS) - -#=========================== - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/exampleRPC/ioc/src/exampleRPCInclude.dbd b/exampleRPC/ioc/src/exampleRPCInclude.dbd deleted file mode 100644 index d326696..0000000 --- a/exampleRPC/ioc/src/exampleRPCInclude.dbd +++ /dev/null @@ -1,6 +0,0 @@ -include "base.dbd" -include "PVAClientRegister.dbd" -include "PVAServerRegister.dbd" -include "registerChannelProviderLocal.dbd" -include "dbPv.dbd" -include "exampleRPC.dbd" diff --git a/exampleRPC/ioc/src/exampleRPCMain.cpp b/exampleRPC/ioc/src/exampleRPCMain.cpp deleted file mode 100644 index 78a9ecc..0000000 --- a/exampleRPC/ioc/src/exampleRPCMain.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* exampleRPCMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.07.24 - */ - -#include -#include -#include -#include -#include - -#include "epicsExit.h" -#include "epicsThread.h" -#include "iocsh.h" - -int main(int argc,char *argv[]) -{ - if(argc>=2) { - iocsh(argv[1]); - epicsThreadSleep(.2); - } - iocsh(NULL); - epicsExit(0); - return(0); -} diff --git a/exampleRPC/iocBoot/Makefile b/exampleRPC/iocBoot/Makefile deleted file mode 100644 index 3e914fd..0000000 --- a/exampleRPC/iocBoot/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -TOP = .. -include $(TOP)/configure/CONFIG -DIRS += $(wildcard *ioc*) -DIRS += $(wildcard as*) -DIRS += $(wildcard example*) -include $(CONFIG)/RULES_DIRS - diff --git a/exampleRPC/iocBoot/exampleRPC/Makefile b/exampleRPC/iocBoot/exampleRPC/Makefile deleted file mode 100644 index e1b9aa4..0000000 --- a/exampleRPC/iocBoot/exampleRPC/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -TOP = ../.. -include $(TOP)/configure/CONFIG -ARCH = $(EPICS_HOST_ARCH) -TARGETS = envPaths -include $(TOP)/configure/RULES.ioc diff --git a/exampleRPC/iocBoot/exampleRPC/st.cmd b/exampleRPC/iocBoot/exampleRPC/st.cmd deleted file mode 100644 index 6088b39..0000000 --- a/exampleRPC/iocBoot/exampleRPC/st.cmd +++ /dev/null @@ -1,23 +0,0 @@ -< envPaths - -cd ${TOP} - -## Register all support components -dbLoadDatabase("dbd/exampleRPC.dbd") -exampleRPC_registerRecordDeviceDriver(pdbbase) - -## Load record instances -dbLoadRecords("db/dbScalar.db","name=pvdouble,type=ao") -dbLoadRecords("db/dbArray.db","name=pvdoubleArray,type=DOUBLE") -dbLoadRecords("db/dbStringArray.db","name=pvstringArray") -dbLoadRecords("db/dbEnum.db","name=pvenum") -dbLoadRecords("db/dbCounter.db","name=pvcounter"); - -cd ${TOP}/iocBoot/${IOC} -iocInit() -dbl -epicsThreadSleep(1.0) -exampleRPCCreateRecord mydevice -startPVAServer -pvdbl - diff --git a/exampleRPC/src/Makefile b/exampleRPC/src/Makefile deleted file mode 100644 index 4e2ef3b..0000000 --- a/exampleRPC/src/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -TOP=.. - -include $(TOP)/configure/CONFIG -#---------------------------------------- -# ADD MACRO DEFINITIONS AFTER THIS LINE -#============================= - -#================================================== -# Build an IOC support library -# - - -DBD += exampleRPC.dbd - -INC += exampleRPC.h - -LIBRARY_IOC += exampleRPC -exampleRPC_SRCS += exampleRPC.cpp -exampleRPC_SRCS += exampleRPCRegister.cpp -exampleRPC_LIBS += pvDatabase -exampleRPC_LIBS += pvAccess -exampleRPC_LIBS += pvData -exampleRPC_LIBS += Com -exampleRPC_LIBS += $(EPICS_BASE_IOC_LIBS) - -PROD_HOST += exampleRPCMain -exampleRPCMain_SRCS += exampleRPCMain.cpp -exampleRPCMain_LIBS += exampleRPC -exampleRPCMain_LIBS += pvDatabase -exampleRPCMain_LIBS += pvAccess -exampleRPCMain_LIBS += pvData -exampleRPCMain_LIBS += Com - -PROD_HOST += move -move_SRCS += positionClient.cpp -move_LIBS += pvAccess pvData Com - -#=========================== - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/exampleRPC/src/exampleRPC.cpp b/exampleRPC/src/exampleRPC.cpp deleted file mode 100644 index 1565f04..0000000 --- a/exampleRPC/src/exampleRPC.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/* exampleRPC.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author dgh - * @date 2015.12.08 - */ - -#include - -#define epicsExportSharedSymbols -#include - -#include -#include - -#include - -using namespace epics::pvData; -using namespace epics::pvDatabase; -using std::tr1::static_pointer_cast; -using std::string; -using namespace std; - -namespace epics { namespace exampleRPC { - - -PVStructurePtr ExampleRPCService::request( - PVStructure::shared_pointer const & args -) throw (epics::pvAccess::RPCRequestException) -{ - - bool haveControl = pvRecord->takeControl(); - if (!haveControl) - throw pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, - "I'm busy"); - - FieldCreatePtr fieldCreate = getFieldCreate(); - PVDataCreatePtr pvDataCreate = getPVDataCreate(); - - PVStructureArrayPtr valueField = args->getSubField("value"); - if (valueField.get() == 0) { - pvRecord->releaseControl(); - throw pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, - "No structure array value field"); - } - - StructureConstPtr valueFieldStructure = valueField-> - getStructureArray()->getStructure(); - - ScalarConstPtr xField = valueFieldStructure->getField("x"); - if (xField.get() == 0 || xField->getScalarType() != pvDouble) { - pvRecord->releaseControl(); - throw pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, - "value field's structure has no double field x"); - } - - ScalarConstPtr yField = valueFieldStructure->getField("y"); - if (xField.get() == 0 || xField->getScalarType() != pvDouble) { - pvRecord->releaseControl(); - throw pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, - "value field's structure has no double field y"); - } - - PVStructureArray::const_svector vals = valueField->view(); - - for (PVStructureArray::const_svector::const_iterator it = vals.begin(); - it != vals.end(); ++it) - { - double x = (*it)->getSubFieldT("x")->get(); - double y = (*it)->getSubFieldT("y")->get(); - pvRecord->put(x,y); - epicsThreadSleep(1.0); - } - - StructureConstPtr topStructure = fieldCreate->createFieldBuilder()-> - createStructure(); - - PVStructurePtr returned = pvDataCreate->createPVStructure(topStructure); - pvRecord->releaseControl(); - return returned; -} - -void ExampleRPCServiceAsync::request( - epics::pvData::PVStructurePtr const & args, - epics::pvAccess::RPCResponseCallback::shared_pointer const & callback) -{ - bool haveControl = pvRecord->takeControl(); - if (!haveControl) - throw pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, - "I'm busy"); - - FieldCreatePtr fieldCreate = getFieldCreate(); - PVDataCreatePtr pvDataCreate = getPVDataCreate(); - - PVStructureArrayPtr valueField = args->getSubField("value"); - if (valueField.get() == 0) { - pvRecord->releaseControl(); - throw pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, - "No structure array value field"); - } - - StructureConstPtr valueFieldStructure = valueField-> - getStructureArray()->getStructure(); - - ScalarConstPtr xField = valueFieldStructure->getField("x"); - if (xField.get() == 0 || xField->getScalarType() != pvDouble) { - pvRecord->releaseControl(); - throw pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, - "value field's structure has no double field x"); - } - - ScalarConstPtr yField = valueFieldStructure->getField("y"); - if (yField.get() == 0 || yField->getScalarType() != pvDouble) { - pvRecord->releaseControl(); - throw pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, - "value field's structure has no double field y"); - } - - PVStructureArray::const_svector vals = valueField->view(); - - for (PVStructureArray::const_svector::const_iterator it = vals.begin(); - it != vals.end(); ++it) - { - double x = (*it)->getSubFieldT("x")->get(); - double y = (*it)->getSubFieldT("y")->get(); - pvRecord->put(x,y); - epicsThreadSleep(1.0); - } - - StructureConstPtr topStructure = fieldCreate->createFieldBuilder()-> - createStructure(); - - PVStructurePtr returned = pvDataCreate->createPVStructure(topStructure); - pvRecord->releaseControl(); - callback->requestDone(Status::Ok, returned); -} - - - -ExampleRPCPtr ExampleRPC::create( - string const & recordName) -{ - StandardFieldPtr standardField = getStandardField(); - FieldCreatePtr fieldCreate = getFieldCreate(); - PVDataCreatePtr pvDataCreate = getPVDataCreate(); - StructureConstPtr topStructure = fieldCreate->createFieldBuilder()-> - add("x",pvDouble)-> - add("y",pvDouble)-> - add("timeStamp",standardField->timeStamp()) -> - createStructure(); - PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure); - - ExampleRPCPtr pvRecord( - new ExampleRPC(recordName,pvStructure)); - if(!pvRecord->init()) pvRecord.reset(); - return pvRecord; -} - -ExampleRPC::ExampleRPC( - string const & recordName, - PVStructurePtr const & pvStructure) -: PVRecord(recordName,pvStructure) -{ -} - -ExampleRPC::~ExampleRPC() -{ -} - -void ExampleRPC::destroy() -{ - PVRecord::destroy(); -} - -bool ExampleRPC::init() -{ - - initPVRecord(); - - service = ExampleRPCService::create( - std::tr1::dynamic_pointer_cast( - shared_from_this())); - - PVFieldPtr pvField; - pvTimeStamp.attach(getPVStructure()->getSubField("timeStamp")); - return true; -} - -epics::pvAccess::Service::shared_pointer ExampleRPC::getService( - PVStructurePtr const & /*pvRequest*/) -{ - return service; -} - -bool ExampleRPC::takeControl() -{ - return taskMutex.tryLock(); -} - -void ExampleRPC::releaseControl() -{ - taskMutex.unlock(); -} - -void ExampleRPC::put(double x, double y) -{ - lock(); - beginGroupPut(); - getPVStructure()->getSubField("x")->put(x); - getPVStructure()->getSubField("y")->put(y); - endGroupPut(); - process(); - unlock(); -} - -void ExampleRPC::process() -{ - timeStamp.getCurrent(); - pvTimeStamp.set(timeStamp); -} - - -}} diff --git a/exampleRPC/src/exampleRPC.h b/exampleRPC/src/exampleRPC.h deleted file mode 100644 index 505874d..0000000 --- a/exampleRPC/src/exampleRPC.h +++ /dev/null @@ -1,126 +0,0 @@ -/* exampleRPC.h */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author dgh - * @date 2015.12.08 - */ -#ifndef EXAMPLERPC_H -#define EXAMPLERPC_H - -#ifdef epicsExportSharedSymbols -# define exampleRPCEpicsExportSharedSymbols -# undef epicsExportSharedSymbols -#endif - -#include -#include -#include - -#ifdef exampleRPCEpicsExportSharedSymbols -# define epicsExportSharedSymbols -# undef exampleRPCEpicsExportSharedSymbols -#endif - -#include - - -namespace epics { namespace exampleRPC { - -class ExampleRPCService; -typedef std::tr1::shared_ptr ExampleRPCServicePtr; - -class ExampleRPCServiceAsync; -typedef std::tr1::shared_ptr ExampleRPCServiceAsyncPtr; - - -class ExampleRPC; -typedef std::tr1::shared_ptr ExampleRPCPtr; - - - -class epicsShareClass ExampleRPCService : - public virtual epics::pvAccess::RPCService -{ -public: - POINTER_DEFINITIONS(ExampleRPCService); - - static ExampleRPCService::shared_pointer create(ExampleRPCPtr const & pvRecord) - { - return ExampleRPCServicePtr(new ExampleRPCService(pvRecord)); - } - ~ExampleRPCService() {}; - - epics::pvData::PVStructurePtr request( - epics::pvData::PVStructure::shared_pointer const & args - ) throw (epics::pvAccess::RPCRequestException); -private: - ExampleRPCService(ExampleRPCPtr const & pvRecord) - : pvRecord(pvRecord) - { - } - - ExampleRPCPtr pvRecord; -}; - - -class ExampleRPCServiceAsync : - public epics::pvAccess::RPCServiceAsync -{ -public: - POINTER_DEFINITIONS(ExampleRPCServiceAsync); - - static ExampleRPCServiceAsync::shared_pointer create(ExampleRPCPtr const & pvRecord) - { - return ExampleRPCServiceAsyncPtr(new ExampleRPCServiceAsync(pvRecord)); - } - - void request(epics::pvData::PVStructurePtr const & args, - epics::pvAccess::RPCResponseCallback::shared_pointer const & callback); -private: - ExampleRPCServiceAsync(ExampleRPCPtr const & pvRecord) - : pvRecord(pvRecord) - { - } - - ExampleRPCPtr pvRecord; -}; - -class ExampleRPC; -typedef std::tr1::shared_ptr ExampleRPCPtr; - -class epicsShareClass ExampleRPC : - public epics::pvDatabase::PVRecord -{ -public: - POINTER_DEFINITIONS(ExampleRPC); - static ExampleRPCPtr create( - std::string const & recordName); - virtual ~ExampleRPC(); - virtual void destroy(); - virtual bool init(); - virtual void process(); - virtual epics::pvAccess::Service::shared_pointer getService( - epics::pvData::PVStructurePtr const & pvRequest); - void put(double x, double y); - - bool takeControl(); - void releaseControl(); -private: - - ExampleRPC(std::string const & recordName, - epics::pvData::PVStructurePtr const & pvStructure); - - epics::pvData::PVTimeStamp pvTimeStamp; - epics::pvData::TimeStamp timeStamp; - epics::pvData::Mutex taskMutex; - epics::pvAccess::Service::shared_pointer service; -}; - - -}} - -#endif /* EXAMPLERPC_H */ diff --git a/exampleRPC/src/exampleRPCInclude.dbd b/exampleRPC/src/exampleRPCInclude.dbd deleted file mode 100644 index 64d5783..0000000 --- a/exampleRPC/src/exampleRPCInclude.dbd +++ /dev/null @@ -1 +0,0 @@ -registrar("exampleRPCRegister") diff --git a/exampleRPC/src/exampleRPCMain.cpp b/exampleRPC/src/exampleRPCMain.cpp deleted file mode 100644 index a7209cb..0000000 --- a/exampleRPC/src/exampleRPCMain.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/*ExampleRPCMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - */ - -/* Author: Marty Kraimer */ - -#include -#include - -#include -#include - -using namespace std; -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; -using namespace epics::exampleRPC; - -int main(int argc,char *argv[]) -{ - PVDatabasePtr master = PVDatabase::getMaster(); - PVRecordPtr pvRecord; - bool result = false; - string recordName; - - recordName = "mydevice"; - pvRecord = ExampleRPC::create(recordName); - result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; - - ContextLocal::shared_pointer contextLocal = ContextLocal::create(); - contextLocal->start(); - - PVStringArrayPtr pvNames = master->getRecordNames(); - shared_vector names = pvNames->view(); - for(size_t i=0; iwaitForExit(); - - return 0; -} - diff --git a/exampleRPC/src/exampleRPCRegister.cpp b/exampleRPC/src/exampleRPCRegister.cpp deleted file mode 100644 index 001ae65..0000000 --- a/exampleRPC/src/exampleRPCRegister.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/*exampleRPCRegister.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.07.24 - */ - - -/* Author: Marty Kraimer */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; -using namespace epics::exampleRPC; -using std::cout; -using std::endl; - -static const iocshArg testArg0 = { "recordName", iocshArgString }; -static const iocshArg *testArgs[] = { - &testArg0}; - -static const iocshFuncDef exampleRPCFuncDef = { - "exampleRPCCreateRecord", 1, testArgs}; -static void exampleRPCCallFunc(const iocshArgBuf *args) -{ - PVDatabasePtr master = PVDatabase::getMaster(); - char *recordName = args[0].sval; - ExampleRPCPtr record = ExampleRPC::create(recordName); - bool result = master->addRecord(record); - if(!result) cout << "recordname" << " not added" << endl; -} - -static void exampleRPCRegister(void) -{ - static int firstTime = 1; - if (firstTime) { - firstTime = 0; - iocshRegister(&exampleRPCFuncDef, exampleRPCCallFunc); - } -} - -extern "C" { - epicsExportRegistrar(exampleRPCRegister); -} diff --git a/exampleRPC/src/exampleRPCRegister.dbd b/exampleRPC/src/exampleRPCRegister.dbd deleted file mode 100644 index 64d5783..0000000 --- a/exampleRPC/src/exampleRPCRegister.dbd +++ /dev/null @@ -1 +0,0 @@ -registrar("exampleRPCRegister") diff --git a/exampleRPC/src/positionClient.cpp b/exampleRPC/src/positionClient.cpp deleted file mode 100644 index 09d8596..0000000 --- a/exampleRPC/src/positionClient.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS exampleCPP is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ - -#include -#include -#include -#include - -#include -#include - -using namespace epics::pvData; -using namespace std; - - -static StructureConstPtr makeDeviceStructure() -{ - static StructureConstPtr deviceStructure; - if (deviceStructure.get() == 0) - { - FieldCreatePtr fieldCreate = getFieldCreate(); - - deviceStructure = fieldCreate->createFieldBuilder()-> - add("x",pvDouble)-> - add("y",pvDouble)-> - createStructure(); - } - return deviceStructure; -} - - -static StructureConstPtr makeArgumentStructure() -{ - static StructureConstPtr requestStructure; - if (requestStructure.get() == 0) - { - FieldCreatePtr fieldCreate = getFieldCreate(); - - requestStructure = fieldCreate->createFieldBuilder()-> - addArray("value", makeDeviceStructure())-> - createStructure(); - } - return requestStructure; -} - -// Set a pvAccess connection timeout, after which the client gives up trying -// to connect to server. -const static double REQUEST_TIMEOUT = 3.0; -const static std::string DEVICE_NAME = "mydevice"; -const static std::string APP_NAME = "move"; - -void usage() -{ - std::cout << "Usage: " << APP_NAME << " [x_1 y_1] ... [x_n y_n]\n" - << "Sequentially sets the values of the x and y fields of " - << DEVICE_NAME << " to (x_i,y_i).\n" - << "Returns on completion." - << std::endl; -} - -/** - */ -int main (int argc, char *argv[]) -{ - for (int i = 1; i < argc; ++i) - { - std::string arg(argv[i]); - if (arg == "-h" || arg == "--help") - { - usage(); - return 0; - } - } - - if ((argc % 2) != 1) - { - std::cerr << APP_NAME << " requires an even number of arguments." - << std::endl; - usage(); - return 1; - } - - if(argc<2) - { - std::cerr << APP_NAME << " requires at least 2 arguments." - << std::endl; - usage(); - return 1; - } - // Start the pvAccess client side. - epics::pvAccess::ClientFactory::start(); - - try - { - PVStructurePtr arguments(getPVDataCreate()->createPVStructure(makeArgumentStructure())); - - PVStructureArray::svector values; - - for (int i = 1; i < argc; ) - { - PVStructurePtr point(getPVDataCreate()->createPVStructure(makeDeviceStructure())); - point->getSubField("x")->put(atof(argv[i++])); - point->getSubField("y")->put(atof(argv[i++])); - values.push_back(point); - } - - arguments->getSubField("value")->replace(freeze(values)); - - epics::pvAccess::RPCClient::shared_pointer client - = epics::pvAccess::RPCClient::create(DEVICE_NAME); - - PVStructurePtr response = client->request(arguments, - REQUEST_TIMEOUT + 1.0 * (argc/2)); - - std::cout << "Done" << std::endl; - } - catch (epics::pvAccess::RPCRequestException & ex) - { - std::cerr << "Operation failed. RPCException:" << std::endl; - std::cerr << ex.what() << std::endl; - } - catch (...) - { - // Catch any other exceptions so we always call ClientFactory::stop(). - std::cerr << "Unexpected exception." << std::endl; - } - - // Stop pvAccess client, so that this application exits cleanly. - epics::pvAccess::ClientFactory::stop(); - - return 0; -} diff --git a/exampleServer/Makefile b/exampleServer/Makefile deleted file mode 100644 index 14826ca..0000000 --- a/exampleServer/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -#Makefile at top of application tree -TOP = . -include $(TOP)/configure/CONFIG -DIRS += configure - -DIRS += src -src_DEPEND_DIRS = configure - -DIRS += ioc -ioc_DEPEND_DIRS = src - -DIRS += iocBoot - -include $(TOP)/configure/RULES_TOP - - diff --git a/exampleServer/configure/CONFIG b/exampleServer/configure/CONFIG deleted file mode 100644 index c1a4703..0000000 --- a/exampleServer/configure/CONFIG +++ /dev/null @@ -1,29 +0,0 @@ -# CONFIG - Load build configuration data -# -# Do not make changes to this file! - -# Allow user to override where the build rules come from -RULES = $(EPICS_BASE) - -# RELEASE files point to other application tops -include $(TOP)/configure/RELEASE --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common -ifdef T_A --include $(TOP)/configure/RELEASE.Common.$(T_A) --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) -endif - -CONFIG = $(RULES)/configure -include $(CONFIG)/CONFIG - -# Override the Base definition: -INSTALL_LOCATION = $(TOP) - -# CONFIG_SITE files contain other build configuration settings -include $(TOP)/configure/CONFIG_SITE --include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common -ifdef T_A - -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A) - -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) -endif - diff --git a/exampleServer/configure/CONFIG_SITE b/exampleServer/configure/CONFIG_SITE deleted file mode 100644 index 0165b88..0000000 --- a/exampleServer/configure/CONFIG_SITE +++ /dev/null @@ -1,39 +0,0 @@ -# CONFIG_SITE - -# Make any application-specific changes to the EPICS build -# configuration variables in this file. -# -# Host/target specific settings can be specified in files named -# CONFIG_SITE.$(EPICS_HOST_ARCH).Common -# CONFIG_SITE.Common.$(T_A) -# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) - -# CHECK_RELEASE controls the consistency checking of the support -# applications pointed to by the RELEASE* files. -# Normally CHECK_RELEASE should be set to YES. -# Set CHECK_RELEASE to NO to disable checking completely. -# Set CHECK_RELEASE to WARN to perform consistency checking but -# continue building anyway if conflicts are found. -#CHECK_RELEASE = YES - -# Set this when you only want to compile this application -# for a subset of the cross-compiled target architectures -# that Base is built for. -#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 - -# To install files into a location other than $(TOP) define -# INSTALL_LOCATION here. -#INSTALL_LOCATION= - -# Set this when your IOC and the host use different paths -# to access the application. This will be needed to boot -# from a Microsoft FTP server or with some NFS mounts. -# You must rebuild in the iocBoot directory for this to -# take effect. -#IOCS_APPL_TOP = - -INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv -USR_INCLUDES += -I $(INSTALL_LOCATION)/include - --include $(TOP)/../../CONFIG_SITE.local --include $(TOP)/../configure/CONFIG_SITE.local diff --git a/exampleServer/configure/ExampleRELEASE.local b/exampleServer/configure/ExampleRELEASE.local deleted file mode 100644 index 95e3783..0000000 --- a/exampleServer/configure/ExampleRELEASE.local +++ /dev/null @@ -1,8 +0,0 @@ -EPICS_BASE=/home/install/epics/base -TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top -EPICSV4HOME=/home/hg -PVCOMMON=${EPICSV4HOME}/pvCommonCPP -PVDATA=${EPICSV4HOME}/pvDataCPP -PVACCESS=${EPICSV4HOME}/pvAccessCPP -PVDATABASE=${EPICSV4HOME}/pvDatabaseCPP -PVASRV=${EPICSV4HOME}/pvaSrv diff --git a/exampleServer/configure/Makefile b/exampleServer/configure/Makefile deleted file mode 100644 index 9254309..0000000 --- a/exampleServer/configure/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -TOP=.. - -include $(TOP)/configure/CONFIG - -TARGETS = $(CONFIG_TARGETS) -CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS))) - -include $(TOP)/configure/RULES diff --git a/exampleServer/configure/RELEASE b/exampleServer/configure/RELEASE deleted file mode 100644 index 18dd963..0000000 --- a/exampleServer/configure/RELEASE +++ /dev/null @@ -1,40 +0,0 @@ -# pvDatabaseCPP/example RELEASE - Location of external support modules -# -# IF YOU CHANGE this file or any file it includes you must -# subsequently do a "gnumake rebuild" in the application's -# top level directory. -# -# The build process does not check dependencies against files -# that are outside this application, thus you should also do a -# "gnumake rebuild" in the top level directory after EPICS_BASE -# or any other external module pointed to below is rebuilt. -# -# Host- or target-specific settings can be given in files named -# RELEASE.$(EPICS_HOST_ARCH).Common -# RELEASE.Common.$(T_A) -# RELEASE.$(EPICS_HOST_ARCH).$(T_A) - -# EPICS V4 Developers: Do not edit the locations in this file! -# -# Create a file RELEASE.local pointing to your places -# for the dependencies, e.g. -# PVACCESS = /path/to/epics/pvAccessCPP -# PVDATA = /path/to/epics/pvDataCPP -# PVCOMMON = /path/to/epics/pvCommonCPP -# EPICS_BASE = /path/to/epics/base - -# If this example is built in a directory under pvDatabaseCPP, -# use the following definitions: - -PVDATABASE = $(TOP)/.. --include $(TOP)/../../RELEASE.local --include $(TOP)/../configure/RELEASE.local - -# If you copied this example from pvDatabaseCPP to be built as a -# standalone TOP, define -# PVDATABASE = /path/to/epics/pvDatabaseCPP -# in the appropriate RELEASE[.local], -# and use the following definitions instead: - -#-include $(TOP)/../RELEASE.local -#-include $(TOP)/configure/RELEASE.local diff --git a/exampleServer/configure/RULES b/exampleServer/configure/RULES deleted file mode 100644 index 6d56e14..0000000 --- a/exampleServer/configure/RULES +++ /dev/null @@ -1,6 +0,0 @@ -# RULES - -include $(CONFIG)/RULES - -# Library should be rebuilt because LIBOBJS may have changed. -$(LIBNAME): ../Makefile diff --git a/exampleServer/configure/RULES.ioc b/exampleServer/configure/RULES.ioc deleted file mode 100644 index 901987c..0000000 --- a/exampleServer/configure/RULES.ioc +++ /dev/null @@ -1,2 +0,0 @@ -#RULES.ioc -include $(CONFIG)/RULES.ioc diff --git a/exampleServer/configure/RULES_DIRS b/exampleServer/configure/RULES_DIRS deleted file mode 100644 index 3ba269d..0000000 --- a/exampleServer/configure/RULES_DIRS +++ /dev/null @@ -1,2 +0,0 @@ -#RULES_DIRS -include $(CONFIG)/RULES_DIRS diff --git a/exampleServer/configure/RULES_TOP b/exampleServer/configure/RULES_TOP deleted file mode 100644 index d09d668..0000000 --- a/exampleServer/configure/RULES_TOP +++ /dev/null @@ -1,3 +0,0 @@ -#RULES_TOP -include $(CONFIG)/RULES_TOP - diff --git a/exampleServer/ioc/Db/Makefile b/exampleServer/ioc/Db/Makefile deleted file mode 100644 index 1e9fd60..0000000 --- a/exampleServer/ioc/Db/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -TOP=../.. -include $(TOP)/configure/CONFIG -#---------------------------------------- -# ADD MACRO DEFINITIONS AFTER THIS LINE - -#---------------------------------------------------- -# Optimization of db files using dbst (DEFAULT: NO) -#DB_OPT = YES - -#---------------------------------------------------- -# Create and install (or just install) -# databases, templates, substitutions like this -DB += dbScalar.db -DB += dbInteger.db -DB += dbArray.db -DB += dbString.db -DB += dbStringArray.db -DB += dbEnum.db -DB += dbCounter.db - -#---------------------------------------------------- -# If .db template is not named *.template add -# _TEMPLATE = - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/exampleServer/ioc/Db/dbArray.db b/exampleServer/ioc/Db/dbArray.db deleted file mode 100644 index 76506a7..0000000 --- a/exampleServer/ioc/Db/dbArray.db +++ /dev/null @@ -1,9 +0,0 @@ -record(waveform, "$(name)") -{ - field(NELM,"5") - field(NORD,"5") - field(FTVL,"$(type)") - field(EGU, "Counts") - field(HOPR, "10") - field(LOPR, "0") -} diff --git a/exampleServer/ioc/Db/dbCounter.db b/exampleServer/ioc/Db/dbCounter.db deleted file mode 100644 index 06aec31..0000000 --- a/exampleServer/ioc/Db/dbCounter.db +++ /dev/null @@ -1,20 +0,0 @@ -record(calc, "${name}") -{ - field(DESC, "Counter") - field(SCAN,"1 second") - field(CALC, "(A_registerRecordDeviceDriver.cpp will be created from .dbd -exampleServer_SRCS += exampleServer_registerRecordDeviceDriver.cpp -exampleServer_SRCS_DEFAULT += exampleServerMain.cpp -exampleServer_SRCS_vxWorks += -nil- - - -# The following adds support from base/src/vxWorks -exampleServer_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary - -exampleServer_LIBS += exampleServer -exampleServer_LIBS += pvDatabase -exampleServer_LIBS += pvaSrv -exampleServer_LIBS += pvAccess -exampleServer_LIBS += pvData -exampleServer_LIBS += $(EPICS_BASE_IOC_LIBS) - -#=========================== - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/exampleServer/ioc/src/exampleServerInclude.dbd b/exampleServer/ioc/src/exampleServerInclude.dbd deleted file mode 100644 index afeea76..0000000 --- a/exampleServer/ioc/src/exampleServerInclude.dbd +++ /dev/null @@ -1,6 +0,0 @@ -include "base.dbd" -include "PVAClientRegister.dbd" -include "PVAServerRegister.dbd" -include "registerChannelProviderLocal.dbd" -include "dbPv.dbd" -include "exampleServer.dbd" diff --git a/exampleServer/ioc/src/exampleServerMain.cpp b/exampleServer/ioc/src/exampleServerMain.cpp deleted file mode 100644 index fb2f17c..0000000 --- a/exampleServer/ioc/src/exampleServerMain.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* exampleServerMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.07.24 - */ - -#include -#include -#include -#include -#include - -#include "epicsExit.h" -#include "epicsThread.h" -#include "iocsh.h" - -int main(int argc,char *argv[]) -{ - if(argc>=2) { - iocsh(argv[1]); - epicsThreadSleep(.2); - } - iocsh(NULL); - epicsExit(0); - return(0); -} diff --git a/exampleServer/iocBoot/Makefile b/exampleServer/iocBoot/Makefile deleted file mode 100644 index 3e914fd..0000000 --- a/exampleServer/iocBoot/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -TOP = .. -include $(TOP)/configure/CONFIG -DIRS += $(wildcard *ioc*) -DIRS += $(wildcard as*) -DIRS += $(wildcard example*) -include $(CONFIG)/RULES_DIRS - diff --git a/exampleServer/iocBoot/exampleServer/Makefile b/exampleServer/iocBoot/exampleServer/Makefile deleted file mode 100644 index e1b9aa4..0000000 --- a/exampleServer/iocBoot/exampleServer/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -TOP = ../.. -include $(TOP)/configure/CONFIG -ARCH = $(EPICS_HOST_ARCH) -TARGETS = envPaths -include $(TOP)/configure/RULES.ioc diff --git a/exampleServer/iocBoot/exampleServer/st.cmd b/exampleServer/iocBoot/exampleServer/st.cmd deleted file mode 100644 index 46b8c68..0000000 --- a/exampleServer/iocBoot/exampleServer/st.cmd +++ /dev/null @@ -1,23 +0,0 @@ -< envPaths - -cd ${TOP} - -## Register all support components -dbLoadDatabase("dbd/exampleServer.dbd") -exampleServer_registerRecordDeviceDriver(pdbbase) - -## Load record instances -dbLoadRecords("db/dbScalar.db","name=pvdouble,type=ao") -dbLoadRecords("db/dbArray.db","name=pvdoubleArray,type=DOUBLE") -dbLoadRecords("db/dbStringArray.db","name=pvstringArray") -dbLoadRecords("db/dbEnum.db","name=pvenum") -dbLoadRecords("db/dbCounter.db","name=pvcounter"); - -cd ${TOP}/iocBoot/${IOC} -iocInit() -dbl -epicsThreadSleep(1.0) -exampleServerCreateRecord exampleServer -startPVAServer -pvdbl - diff --git a/exampleServer/src/Makefile b/exampleServer/src/Makefile deleted file mode 100644 index a0e18f1..0000000 --- a/exampleServer/src/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -TOP=.. - -include $(TOP)/configure/CONFIG -#---------------------------------------- -# ADD MACRO DEFINITIONS AFTER THIS LINE -#============================= - -#================================================== -# Build an IOC support library -# - - -DBD += exampleServer.dbd - -INC += exampleServer.h - -LIBRARY_IOC += exampleServer -exampleServer_SRCS += exampleServer.cpp -exampleServer_SRCS += exampleServerRegister.cpp -exampleServer_LIBS += pvDatabase -exampleServer_LIBS += pvAccess -exampleServer_LIBS += pvData -exampleServer_LIBS += Com -exampleServer_LIBS += $(EPICS_BASE_IOC_LIBS) - -PROD_HOST += exampleServerMain -exampleServerMain_SRCS += exampleServerMain.cpp -exampleServerMain_LIBS += exampleServer -exampleServerMain_LIBS += pvDatabase -exampleServerMain_LIBS += pvAccess -exampleServerMain_LIBS += pvData -exampleServerMain_LIBS += Com - -#=========================== - -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE - diff --git a/exampleServer/src/exampleServer.cpp b/exampleServer/src/exampleServer.cpp deleted file mode 100644 index 0208f48..0000000 --- a/exampleServer/src/exampleServer.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* exampleServer.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.04.02 - */ - -#include - -#define epicsExportSharedSymbols -#include - -using namespace epics::pvData; -using namespace epics::pvDatabase; -using std::tr1::static_pointer_cast; -using std::string; - -namespace epics { namespace exampleServer { - - -ExampleServerPtr ExampleServer::create( - string const & recordName) -{ - StandardFieldPtr standardField = getStandardField(); - FieldCreatePtr fieldCreate = getFieldCreate(); - PVDataCreatePtr pvDataCreate = getPVDataCreate(); - StructureConstPtr topStructure = fieldCreate->createFieldBuilder()-> - addNestedStructure("argument")-> - add("value",pvString)-> - endNested()-> - addNestedStructure("result") -> - add("value",pvString) -> - add("timeStamp",standardField->timeStamp()) -> - endNested()-> - createStructure(); - PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure); - - ExampleServerPtr pvRecord( - new ExampleServer(recordName,pvStructure)); - if(!pvRecord->init()) pvRecord.reset(); - return pvRecord; -} - -ExampleServer::ExampleServer( - string const & recordName, - PVStructurePtr const & pvStructure) -: PVRecord(recordName,pvStructure) -{ -} - -ExampleServer::~ExampleServer() -{ -} - -void ExampleServer::destroy() -{ - PVRecord::destroy(); -} - -bool ExampleServer::init() -{ - - initPVRecord(); - PVFieldPtr pvField; - pvArgumentValue = getPVStructure()->getSubField("argument.value"); - if(pvArgumentValue.get()==NULL) return false; - pvResultValue = getPVStructure()->getSubField("result.value"); - if(pvResultValue.get()==NULL) return false; - pvTimeStamp.attach(getPVStructure()->getSubField("result.timeStamp")); - return true; -} - -void ExampleServer::process() -{ - pvResultValue->put(string("Hello ") + pvArgumentValue->get()); - timeStamp.getCurrent(); - pvTimeStamp.set(timeStamp); -} - -}} diff --git a/exampleServer/src/exampleServer.h b/exampleServer/src/exampleServer.h deleted file mode 100644 index 4220752..0000000 --- a/exampleServer/src/exampleServer.h +++ /dev/null @@ -1,61 +0,0 @@ -/* exampleServer.h */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.04.02 - */ -#ifndef EXAMPLESERVER_H -#define EXAMPLESERVER_H - -#ifdef epicsExportSharedSymbols -# define exampleServerEpicsExportSharedSymbols -# undef epicsExportSharedSymbols -#endif - -#include -#include -#include - -#ifdef exampleServerEpicsExportSharedSymbols -# define epicsExportSharedSymbols -# undef exampleServerEpicsExportSharedSymbols -#endif - -#include - - -namespace epics { namespace exampleServer { - - -class ExampleServer; -typedef std::tr1::shared_ptr ExampleServerPtr; - -class epicsShareClass ExampleServer : - public epics::pvDatabase::PVRecord -{ -public: - POINTER_DEFINITIONS(ExampleServer); - static ExampleServerPtr create( - std::string const & recordName); - virtual ~ExampleServer(); - virtual void destroy(); - virtual bool init(); - virtual void process(); -private: - ExampleServer(std::string const & recordName, - epics::pvData::PVStructurePtr const & pvStructure); - - epics::pvData::PVStringPtr pvArgumentValue; - epics::pvData::PVStringPtr pvResultValue; - epics::pvData::PVTimeStamp pvTimeStamp; - epics::pvData::TimeStamp timeStamp; -}; - - -}} - -#endif /* EXAMPLESERVER_H */ diff --git a/exampleServer/src/exampleServerInclude.dbd b/exampleServer/src/exampleServerInclude.dbd deleted file mode 100644 index cb97782..0000000 --- a/exampleServer/src/exampleServerInclude.dbd +++ /dev/null @@ -1 +0,0 @@ -registrar("exampleServerRegister") diff --git a/exampleServer/src/exampleServerMain.cpp b/exampleServer/src/exampleServerMain.cpp deleted file mode 100644 index 118150f..0000000 --- a/exampleServer/src/exampleServerMain.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/*ExampleServerMain.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - */ - -/* Author: Marty Kraimer */ - -#include -#include - -#include -#include -#include - -using namespace std; -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; -using namespace epics::exampleServer; - -int main(int argc,char *argv[]) -{ - PVDatabasePtr master = PVDatabase::getMaster(); - PVRecordPtr pvRecord; - bool result = false; - string recordName; - - recordName = "exampleServer"; - pvRecord = ExampleServer::create(recordName); - result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; - - recordName = "traceRecordPGRPC"; - pvRecord = TraceRecord::create(recordName); - result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; - - - ContextLocal::shared_pointer contextLocal = ContextLocal::create(); - contextLocal->start(); - - PVStringArrayPtr pvNames = master->getRecordNames(); - shared_vector names = pvNames->view(); - for(size_t i=0; iwaitForExit(); - - return 0; -} - diff --git a/exampleServer/src/exampleServerRegister.cpp b/exampleServer/src/exampleServerRegister.cpp deleted file mode 100644 index 2a5f4cb..0000000 --- a/exampleServer/src/exampleServerRegister.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/*exampleServerRegister.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.07.24 - */ - - -/* Author: Marty Kraimer */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; -using namespace epics::exampleServer; -using std::cout; -using std::endl; - -static const iocshArg testArg0 = { "recordName", iocshArgString }; -static const iocshArg *testArgs[] = { - &testArg0}; - -static const iocshFuncDef exampleServerFuncDef = { - "exampleServerCreateRecord", 1, testArgs}; -static void exampleServerCallFunc(const iocshArgBuf *args) -{ - PVDatabasePtr master = PVDatabase::getMaster(); - char *recordName = args[0].sval; - ExampleServerPtr record = ExampleServer::create(recordName); - bool result = master->addRecord(record); - if(!result) cout << "recordname" << " not added" << endl; -} - -static void exampleServerRegister(void) -{ - static int firstTime = 1; - if (firstTime) { - firstTime = 0; - iocshRegister(&exampleServerFuncDef, exampleServerCallFunc); - } -} - -extern "C" { - epicsExportRegistrar(exampleServerRegister); -} diff --git a/exampleServer/src/exampleServerRegister.dbd b/exampleServer/src/exampleServerRegister.dbd deleted file mode 100644 index cb97782..0000000 --- a/exampleServer/src/exampleServerRegister.dbd +++ /dev/null @@ -1 +0,0 @@ -registrar("exampleServerRegister") diff --git a/jenkins/cloudbees_build b/jenkins/cloudbees_build index c84c2bf..ed99da9 100644 --- a/jenkins/cloudbees_build +++ b/jenkins/cloudbees_build @@ -21,7 +21,6 @@ USE_MB=${2:-"MB_NO"} PVCOMMON_BRANCH="" PVDATA_BRANCH="" PVACCESS_BRANCH="" -PVASRV_BRANCH="" ########################################### # Fetch and unpack dependencies @@ -39,7 +38,6 @@ wget -nv https://openepics.ci.cloudbees.com/job/pvaSrvCPP_${PVASRV_BRANCH}Build/ tar -xzf base-${BASE}.CB-dist.tar.gz tar -xzf pvData.CB-dist.tar.gz tar -xzf pvAccess.CB-dist.tar.gz -tar -xzf pvaSrv.CB-dist.tar.gz if [ "${USE_MB}" = "MB_YES" ]; then wget -nv https://openepics.ci.cloudbees.com/job/pvCommonCPP_${PVCOMMON_BRANCH}Build/BASE=${BASE},USE_MB=MB_YES/lastSuccessfulBuild/artifact/pvCommon.CB-dist.tar.gz @@ -65,7 +63,7 @@ make distclean all ########################################### # Test -# Currently no tests. See the file called README. +make runtests ########################################### # Create distribution diff --git a/src/Makefile b/src/Makefile index e86dc0b..a638f70 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,14 +3,18 @@ TOP = .. include $(TOP)/configure/CONFIG +INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv +USR_INCLUDES += -I$(INSTALL_LOCATION)/include + PVDATABASE_SRC = $(TOP)/src -LIBRARY += pvDatabase - +include $(PVDATABASE_SRC)/pv/Makefile include $(PVDATABASE_SRC)/database/Makefile include $(PVDATABASE_SRC)/pvAccess/Makefile include $(PVDATABASE_SRC)/special/Makefile +LIBRARY += pvDatabase + pvDatabase_LIBS += pvAccess pvData Com pvDatabase_LIBS += $(EPICS_BASE_IOC_LIBS) diff --git a/src/database/Makefile b/src/database/Makefile index 8329d2e..54fb3f4 100644 --- a/src/database/Makefile +++ b/src/database/Makefile @@ -2,7 +2,5 @@ SRC_DIRS += $(PVDATABASE_SRC)/database -INC += pvDatabase.h - LIBSRCS += pvRecord.cpp LIBSRCS += pvDatabase.cpp diff --git a/src/pv/Makefile b/src/pv/Makefile new file mode 100644 index 0000000..5480c95 --- /dev/null +++ b/src/pv/Makefile @@ -0,0 +1,5 @@ +SRC_DIRS += $(PVDATABASE_SRC)/pv +INC += pvDatabase.h +INC += channelProviderLocal.h +INC += traceRecord.h + diff --git a/src/pvAccess/channelProviderLocal.h b/src/pv/channelProviderLocal.h similarity index 100% rename from src/pvAccess/channelProviderLocal.h rename to src/pv/channelProviderLocal.h diff --git a/src/database/pvDatabase.h b/src/pv/pvDatabase.h similarity index 99% rename from src/database/pvDatabase.h rename to src/pv/pvDatabase.h index ce88c4c..56f4f2d 100644 --- a/src/database/pvDatabase.h +++ b/src/pv/pvDatabase.h @@ -531,7 +531,7 @@ private: /** @page Overview Documentation * - * pvDatabase.html + * pvDatabase.html * */ diff --git a/src/special/traceRecord.h b/src/pv/traceRecord.h similarity index 100% rename from src/special/traceRecord.h rename to src/pv/traceRecord.h diff --git a/src/pvAccess/Makefile b/src/pvAccess/Makefile index 3fadc76..3aa259b 100644 --- a/src/pvAccess/Makefile +++ b/src/pvAccess/Makefile @@ -2,8 +2,6 @@ SRC_DIRS += $(PVDATABASE_SRC)/pvAccess -INC += channelProviderLocal.h - DBD += registerChannelProviderLocal.dbd LIBSRCS += channelProviderLocal.cpp diff --git a/src/special/Makefile b/src/special/Makefile index 30f6d2a..e370817 100644 --- a/src/special/Makefile +++ b/src/special/Makefile @@ -2,6 +2,4 @@ SRC_DIRS += $(PVDATABASE_SRC)/special -INC += traceRecord.h - LIBSRCS += traceRecord.cpp diff --git a/test/Makefile b/test/Makefile index 999631d..13e0db1 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,11 +1,28 @@ -#Makefile at top of application tree -TOP = . +# Makefile for the pvData tests + +TOP = .. include $(TOP)/configure/CONFIG -DIRS += configure -DIRS += src -src_DEPEND_DIRS = configure +PVDATABASE_TEST = $(TOP)/test -include $(TOP)/configure/RULES_TOP +PROD_LIBS += pvDatabase pvAccess pvData Com + +include $(PVDATABASE_TEST)/src/Makefile + +# The testHarness runs all the test programs in a known working order. +testHarness_SRCS += pvDatabaseAllTests.c + +PROD_vxWorks = vxTestHarness +vxTestHarness_SRCS += $(testHarness_SRCS) +TESTSPEC_vxWorks = vxTestHarness.$(MUNCH_SUFFIX); pvDatabaseAllTests + +PROD_RTEMS += rtemsTestHarness +rtemsTestHarness_SRCS += rtemsTestHarness.c rtemsConfig.c +rtemsTestHarness_SRCS += $(testHarness_SRCS) +TESTSPEC_RTEMS = rtemsTestHarness.$(MUNCH_SUFFIX); pvDatabaseAllTests + +TESTSCRIPTS_HOST += $(TESTS:%=%.t) + +include $(TOP)/configure/RULES diff --git a/test/configure/CONFIG b/test/configure/CONFIG deleted file mode 100644 index c1a4703..0000000 --- a/test/configure/CONFIG +++ /dev/null @@ -1,29 +0,0 @@ -# CONFIG - Load build configuration data -# -# Do not make changes to this file! - -# Allow user to override where the build rules come from -RULES = $(EPICS_BASE) - -# RELEASE files point to other application tops -include $(TOP)/configure/RELEASE --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common -ifdef T_A --include $(TOP)/configure/RELEASE.Common.$(T_A) --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) -endif - -CONFIG = $(RULES)/configure -include $(CONFIG)/CONFIG - -# Override the Base definition: -INSTALL_LOCATION = $(TOP) - -# CONFIG_SITE files contain other build configuration settings -include $(TOP)/configure/CONFIG_SITE --include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).Common -ifdef T_A - -include $(TOP)/configure/CONFIG_SITE.Common.$(T_A) - -include $(TOP)/configure/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) -endif - diff --git a/test/configure/CONFIG_SITE b/test/configure/CONFIG_SITE deleted file mode 100644 index 0165b88..0000000 --- a/test/configure/CONFIG_SITE +++ /dev/null @@ -1,39 +0,0 @@ -# CONFIG_SITE - -# Make any application-specific changes to the EPICS build -# configuration variables in this file. -# -# Host/target specific settings can be specified in files named -# CONFIG_SITE.$(EPICS_HOST_ARCH).Common -# CONFIG_SITE.Common.$(T_A) -# CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) - -# CHECK_RELEASE controls the consistency checking of the support -# applications pointed to by the RELEASE* files. -# Normally CHECK_RELEASE should be set to YES. -# Set CHECK_RELEASE to NO to disable checking completely. -# Set CHECK_RELEASE to WARN to perform consistency checking but -# continue building anyway if conflicts are found. -#CHECK_RELEASE = YES - -# Set this when you only want to compile this application -# for a subset of the cross-compiled target architectures -# that Base is built for. -#CROSS_COMPILER_TARGET_ARCHS = vxWorks-68040 - -# To install files into a location other than $(TOP) define -# INSTALL_LOCATION here. -#INSTALL_LOCATION= - -# Set this when your IOC and the host use different paths -# to access the application. This will be needed to boot -# from a Microsoft FTP server or with some NFS mounts. -# You must rebuild in the iocBoot directory for this to -# take effect. -#IOCS_APPL_TOP = - -INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv -USR_INCLUDES += -I $(INSTALL_LOCATION)/include - --include $(TOP)/../../CONFIG_SITE.local --include $(TOP)/../configure/CONFIG_SITE.local diff --git a/test/configure/Makefile b/test/configure/Makefile deleted file mode 100644 index 9254309..0000000 --- a/test/configure/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -TOP=.. - -include $(TOP)/configure/CONFIG - -TARGETS = $(CONFIG_TARGETS) -CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS))) - -include $(TOP)/configure/RULES diff --git a/test/configure/RELEASE b/test/configure/RELEASE deleted file mode 100644 index 392a719..0000000 --- a/test/configure/RELEASE +++ /dev/null @@ -1,40 +0,0 @@ -# pvDatabaseCPP/test RELEASE - Location of external support modules -# -# IF YOU CHANGE this file or any file it includes you must -# subsequently do a "gnumake rebuild" in the application's -# top level directory. -# -# The build process does not check dependencies against files -# that are outside this application, thus you should also do a -# "gnumake rebuild" in the top level directory after EPICS_BASE -# or any other external module pointed to below is rebuilt. -# -# Host- or target-specific settings can be given in files named -# RELEASE.$(EPICS_HOST_ARCH).Common -# RELEASE.Common.$(T_A) -# RELEASE.$(EPICS_HOST_ARCH).$(T_A) - -# EPICS V4 Developers: Do not edit the locations in this file! -# -# Create a file RELEASE.local pointing to your places -# for the dependencies, e.g. -# PVACCESS = /path/to/epics/pvAccessCPP -# PVDATA = /path/to/epics/pvDataCPP -# PVCOMMON = /path/to/epics/pvCommonCPP -# EPICS_BASE = /path/to/epics/base - -# If these tests are built in a directory under pvDatabaseCPP, -# use the following definitions: - -PVDATABASE = $(TOP)/.. --include $(TOP)/../../RELEASE.local --include $(TOP)/../configure/RELEASE.local - -# If you copied these tests from pvDatabaseCPP to be built as a -# standalone TOP, define -# PVDATABASE = /path/to/epics/pvDatabaseCPP -# in the appropriate RELEASE[.local], -# and use the following definitions instead: - -#-include $(TOP)/../RELEASE.local -#-include $(TOP)/configure/RELEASE.local diff --git a/test/configure/RULES b/test/configure/RULES deleted file mode 100644 index 6d56e14..0000000 --- a/test/configure/RULES +++ /dev/null @@ -1,6 +0,0 @@ -# RULES - -include $(CONFIG)/RULES - -# Library should be rebuilt because LIBOBJS may have changed. -$(LIBNAME): ../Makefile diff --git a/test/configure/RULES.ioc b/test/configure/RULES.ioc deleted file mode 100644 index 901987c..0000000 --- a/test/configure/RULES.ioc +++ /dev/null @@ -1,2 +0,0 @@ -#RULES.ioc -include $(CONFIG)/RULES.ioc diff --git a/test/configure/RULES_DIRS b/test/configure/RULES_DIRS deleted file mode 100644 index 3ba269d..0000000 --- a/test/configure/RULES_DIRS +++ /dev/null @@ -1,2 +0,0 @@ -#RULES_DIRS -include $(CONFIG)/RULES_DIRS diff --git a/test/configure/RULES_TOP b/test/configure/RULES_TOP deleted file mode 100644 index d09d668..0000000 --- a/test/configure/RULES_TOP +++ /dev/null @@ -1,3 +0,0 @@ -#RULES_TOP -include $(CONFIG)/RULES_TOP - diff --git a/test/pvDatabaseAllTests.c b/test/pvDatabaseAllTests.c new file mode 100644 index 0000000..90690e0 --- /dev/null +++ b/test/pvDatabaseAllTests.c @@ -0,0 +1,26 @@ +/* + * Run pvData tests as a batch. + * + * Do *not* include performance measurements here, they don't help to + * prove functionality (which is the point of this convenience routine). + */ + +#include +#include +#include + +/* src */ +int testExampleRecord(void); +int testPVCopy(void); +int testPVRecord(void); + +void pvDatabaseAllTests(void) +{ + testHarness(); + + /* src */ + runTest(testExampleRecord); + runTest(testPVCopy); + runTest(testPVRecord); +} + diff --git a/test/qemuRunTest.sh b/test/qemuRunTest.sh new file mode 100755 index 0000000..b9cd499 --- /dev/null +++ b/test/qemuRunTest.sh @@ -0,0 +1 @@ +qemu-system-i386 --kernel O.RTEMS-pc386/rtemsTestHarness -netdev user,id=mynet0 -device ne2k_isa,netdev=mynet0 -redir tcp:5075::5075 -redir udp:5076::5076 -m 1024 --no-reboot -curses diff --git a/test/rtemsConfig.c b/test/rtemsConfig.c new file mode 100644 index 0000000..4ad8433 --- /dev/null +++ b/test/rtemsConfig.c @@ -0,0 +1,72 @@ +/*************************************************************************\ +* Copyright (c) 2002 The University of Saskatchewan +* 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. +\*************************************************************************/ +/* + * RTEMS configuration for EPICS + * Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd + * Author: W. Eric Norum + * norume@aps.anl.gov + * (630) 252-4793 + */ + +#include + +/* + *********************************************************************** + * RTEMS CONFIGURATION * + *********************************************************************** + */ +#define CONFIGURE_RTEMS_INIT_TASKS_TABLE + +#if __RTEMS_MAJOR__>4 || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>9) || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==9 && __RTEMS_REVISION__==99) +# define CONFIGURE_UNIFIED_WORK_AREAS +#else +# define CONFIGURE_EXECUTIVE_RAM_SIZE (2000*1024) +#endif + +#define CONFIGURE_MAXIMUM_TASKS rtems_resource_unlimited(30) +#define CONFIGURE_MAXIMUM_SEMAPHORES rtems_resource_unlimited(500) +#define CONFIGURE_MAXIMUM_TIMERS rtems_resource_unlimited(20) +#define CONFIGURE_MAXIMUM_MESSAGE_QUEUES rtems_resource_unlimited(5) +#define CONFIGURE_MAXIMUM_USER_EXTENSIONS 1 + +#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 150 +#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM +#define CONFIGURE_MAXIMUM_DRIVERS 8 + +#define CONFIGURE_MICROSECONDS_PER_TICK 20000 + +#define CONFIGURE_INIT_TASK_PRIORITY 80 + +#define CONFIGURE_MALLOC_STATISTICS 1 + +#define CONFIGURE_INIT +#define CONFIGURE_INIT_TASK_INITIAL_MODES (RTEMS_PREEMPT | \ + RTEMS_NO_TIMESLICE | \ + RTEMS_NO_ASR | \ + RTEMS_INTERRUPT_LEVEL(0)) +#define CONFIGURE_INIT_TASK_ATTRIBUTES (RTEMS_FLOATING_POINT | RTEMS_LOCAL) +#define CONFIGURE_INIT_TASK_STACK_SIZE (16*1024) +rtems_task Init (rtems_task_argument argument); + +#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER +#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER + +#define CONFIGURE_FILESYSTEM_NFS +#define CONFIGURE_FILESYSTEM_IMFS + +/* + * This should be made BSP dependent, not CPU dependent but I know of no + * appropriate conditionals to use. + * The new general time support makes including the RTC driver less important. + */ +#if !defined(mpc604) && !defined(__mc68040__) && !defined(__mcf5200__) && !defined(mpc7455) && !defined(__arm__) /* don't have RTC code */ +#define CONFIGURE_APPLICATION_NEEDS_RTC_DRIVER +#endif + + +#include +#include diff --git a/test/rtemsNetworking.h b/test/rtemsNetworking.h new file mode 100644 index 0000000..f646edb --- /dev/null +++ b/test/rtemsNetworking.h @@ -0,0 +1,294 @@ +/* + * Network configuration -- QEMU NOT using DHCP + * + ************************************************************ + * EDIT THIS FILE TO REFLECT YOUR NETWORK CONFIGURATION * + * BEFORE RUNNING ANY RTEMS PROGRAMS WHICH USE THE NETWORK! * + ************************************************************ + * + * The dynamic probing is based upon the EPICS network + * configuration file written by: + * W. Eric Norum + * eric.norum@usask.ca + * (306) 966-5394 + */ + +#ifndef _RTEMS_NETWORKCONFIG_H_ +#define _RTEMS_NETWORKCONFIG_H_ + +/* #define USE_LIBBSDPORT */ + +#if defined(USE_LIBBSDPORT) + #include + #define CONFIGURE_MAXIMUM_TIMERS 10 +#endif +/* + * For TFTP test application + */ +#if (defined (RTEMS_USE_BOOTP)) +#define RTEMS_TFTP_TEST_HOST_NAME "BOOTP_HOST" +#define RTEMS_TFTP_TEST_FILE_NAME "BOOTP_FILE" +#else +#define RTEMS_TFTP_TEST_HOST_NAME "XXX.YYY.ZZZ.XYZ" +#define RTEMS_TFTP_TEST_FILE_NAME "tftptest" +#endif + +/* + * For NFS test application + * + * NFS mount and a directory to ls once mounted + */ +#define RTEMS_NFS_SERVER "192.168.1.210" +#define RTEMS_NFS_SERVER_PATH "/home" +#define RTEMS_NFS_LS_PATH "/mnt/nfstest" + + + +/* + * This file can be copied to an application source directory + * and modified to override the values shown below. + * + * The following CPP symbols may be passed from the Makefile: + * + * symbol default description + * + * NETWORK_TASK_PRIORITY 150 can be read by app from public + * var 'gesysNetworkTaskPriority' + * FIXED_IP_ADDR hardcoded IP address (e.g., + * "192.168.0.10"); disables BOOTP; + * must also define FIXED_NETMASK + * FIXED_NETMASK IP netmask string + * (e.g. "255.255.255.0") + * MULTI_NETDRIVER ugly hack; if defined try to probe + * a variety of PCI and ISA drivers + * (i386 ONLY) use is discouraged! + * NIC_NAME Ethernet driver name (e.g. "pcn1"); + * must also define NIC_ATTACH + * NIC_ATTACH Ethernet driver attach function + * (e.g., rtems_fxp_attach). + * If these are undefined then + * a) MULTI_NETDRIVER is used + * (if defined) + * b) RTEMS_BSP_NETWORK_DRIVER_NAME/ + * RTEMS_BSP_NETWORK_DRIVER_ATTACH + * are tried + * MEMORY_CUSTOM Allocate the defined amount of + * memory for mbufs and mbuf clusters, + * respectively. Define to a comma ',' + * separated pair of two numerical + * values, e.g: 100*1024,200*1024 + * MEMORY_SCARCE Allocate few memory for mbufs + * (hint for how much memory the + * board has) + * MEMORY_HUGE Allocate a lot of memory for mbufs + * (hint for how much memory the + * board has) + * If none of MEMORY_CUSTOM/ + * MEMORY_SCARCE/MEMORY_HUGE are + * defined then a medium amount of + * memory is allocated for mbufs. + */ + +#include +#include +#include + +#if 0 +#ifdef HAVE_CONFIG_H +#include +#else +#include "verscheck.h" +#endif +#endif + +//#define MULTI_NETDRIVER +//#define RTEMS_BSP_NETWORK_DRIVER_NAME 1 + +#define FIXED_IP_ADDR "192.168.1.249" +#define FIXED_NETMASK "255.255.255.0" + +#ifndef NETWORK_TASK_PRIORITY +#define NETWORK_TASK_PRIORITY 150 /* within EPICS' range */ +#endif + +/* make publicly available for startup scripts... */ +const int gesysNetworkTaskPriority = NETWORK_TASK_PRIORITY; + +#ifdef FIXED_IP_ADDR +#define RTEMS_DO_BOOTP 0 +#else +#define RTEMS_DO_BOOTP rtems_bsdnet_do_bootp +#define FIXED_IP_ADDR 0 +#undef FIXED_NETMASK +#define FIXED_NETMASK 0 +#endif + +#if !defined(NIC_NAME) + +#ifdef MULTI_NETDRIVER + +#if 0 + #if RTEMS_VERSION_ATLEAST(4,6,99) + #define pcib_init pci_initialize + #endif +#endif + +extern int rtems_3c509_driver_attach (struct rtems_bsdnet_ifconfig *, int); +extern int rtems_fxp_attach (struct rtems_bsdnet_ifconfig *, int); +extern int rtems_elnk_driver_attach (struct rtems_bsdnet_ifconfig *, int); +extern int rtems_dec21140_driver_attach (struct rtems_bsdnet_ifconfig *, int); + +/* these don't probe and will be used even if there's no device :-( */ +extern int rtems_ne_driver_attach (struct rtems_bsdnet_ifconfig *, int); +extern int rtems_wd_driver_attach (struct rtems_bsdnet_ifconfig *, int); + +static struct rtems_bsdnet_ifconfig isa_netdriver_config[] = { + { + "ep0", rtems_3c509_driver_attach, isa_netdriver_config + 1, + }, + { + "ne1", rtems_ne_driver_attach, 0, irno: 9 /* qemu cannot configure irq-no :-(; has it hardwired to 9 */ + }, +}; + +static struct rtems_bsdnet_ifconfig pci_netdriver_config[]={ + { + "dc1", rtems_dec21140_driver_attach, pci_netdriver_config+1, + }, + #if !defined(USE_LIBBSDPORT) + { + "fxp1", rtems_fxp_attach, pci_netdriver_config+2, + }, + #else + { + "", libbsdport_netdriver_attach, pci_netdriver_config+2, + }, + #endif + { + "elnk1", rtems_elnk_driver_attach, isa_netdriver_config, + }, +}; + +static int pci_check(struct rtems_bsdnet_ifconfig *ocfg, int attaching) +{ +struct rtems_bsdnet_ifconfig *cfg; +int if_index_pre; +extern int if_index; + if ( attaching ) { + cfg = pci_initialize() ? + isa_netdriver_config : pci_netdriver_config; + } + while ( cfg ) { + printk("Probing '%s'", cfg->name); + /* unfortunately, the return value is unreliable - some drivers report + * success even if they fail. + * Check if they chained an interface (ifnet) structure instead + */ + if_index_pre = if_index; + cfg->attach(cfg, attaching); + if ( if_index > if_index_pre ) { + /* assume success */ + printk(" .. seemed to work\n"); + ocfg->name = cfg->name; + ocfg->attach = cfg->attach; + return 0; + } + printk(" .. failed\n"); + cfg = cfg->next; + } + return -1; +} + + +#define NIC_NAME "dummy" +#define NIC_ATTACH pci_check + +#else + +#if defined(RTEMS_BSP_NETWORK_DRIVER_NAME) /* Use NIC provided by BSP */ + +/* force ne2k_isa on i386 for qemu */ +#if defined(__i386__) +# define NIC_NAME BSP_NE2000_NETWORK_DRIVER_NAME +# define NIC_ATTACH BSP_NE2000_NETWORK_DRIVER_ATTACH + +#else + +# define NIC_NAME RTEMS_BSP_NETWORK_DRIVER_NAME +# define NIC_ATTACH RTEMS_BSP_NETWORK_DRIVER_ATTACH +#endif + +#endif + +#endif /* ifdef MULTI_NETDRIVER */ +#endif + + +#ifdef NIC_NAME + +extern int NIC_ATTACH(); + +#if RTEMS_BSP_NETWORK_DRIVER_ATTACH == BSP_NE2000_NETWORK_DRIVER_ATTACH +static char ethernet_address[6] = { 0x00, 0xab, 0xcd, 0xef, 0x12, 0x34 }; +#endif + +static struct rtems_bsdnet_ifconfig netdriver_config[1] = {{ + NIC_NAME, /* name */ + (int (*)(struct rtems_bsdnet_ifconfig*,int))NIC_ATTACH, /* attach function */ + 0, /* link to next interface */ + FIXED_IP_ADDR, + FIXED_NETMASK +#if RTEMS_BSP_NETWORK_DRIVER_ATTACH == BSP_NE2000_NETWORK_DRIVER_ATTACH + , + ethernet_address, + irno:9, + port:0xc100 +#endif +}}; +#else +#warning "NO KNOWN NETWORK DRIVER FOR THIS BSP -- YOU MAY HAVE TO EDIT networkconfig.h" +#endif + +struct rtems_bsdnet_config rtems_bsdnet_config = { +#ifdef NIC_NAME + netdriver_config, /* link to next interface */ + RTEMS_DO_BOOTP, /* Use BOOTP to get network configuration */ +#else + 0, + 0, +#endif + NETWORK_TASK_PRIORITY, /* Network task priority */ +#if defined(MEMORY_CUSTOM) + MEMORY_CUSTOM, +#elif defined(MEMORY_SCARCE) + 100*1024, /* MBUF space */ + 200*1024, /* MBUF cluster space */ +#elif defined(MEMORY_HUGE) + 2*1024*1024, /* MBUF space */ + 5*1024*1024, /* MBUF cluster space */ +#else + 180*1024, /* MBUF space */ + 350*1024, /* MBUF cluster space */ +#endif +#if (!defined (RTEMS_USE_BOOTP)) && defined(ON_RTEMS_LAB_WINSYSTEMS) + "rtems", /* Host name */ + "nodomain.com", /* Domain name */ + "192.168.1.14", /* Gateway */ + "192.168.1.1", /* Log host */ + {"89.212.75.6" }, /* Name server(s) */ + {"192.168.1.1" }, /* NTP server(s) */ +#else + NULL, /* Host name */ + NULL, /* Domain name */ + NULL, /* Gateway */ + NULL, /* Log host */ + { NULL }, /* Name server(s) */ + { NULL }, /* NTP server(s) */ +#endif /* !RTEMS_USE_BOOTP */ + 0, /* efficiency */ + 0, /* udp TX buffer */ + 0, /* udp RX buffer */ + 0, /* tcp TX buffer */ + 0, /* tcp RX buffer */ +}; +#endif /* _RTEMS_NETWORKCONFIG_H_ */ diff --git a/test/rtemsTestHarness.c b/test/rtemsTestHarness.c new file mode 100644 index 0000000..b6ef3ba --- /dev/null +++ b/test/rtemsTestHarness.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +#include "rtemsNetworking.h" + +#include +#include + +rtems_task +Init (rtems_task_argument ignored) +{ + rtems_bsdnet_initialize_network (); + //rtems_bsdnet_show_if_stats (); + + rtems_time_of_day timeOfDay; + if (rtems_clock_get(RTEMS_CLOCK_GET_TOD,&timeOfDay) != RTEMS_SUCCESSFUL) { + timeOfDay.year = 2014; + timeOfDay.month = 1; + timeOfDay.day = 1; + timeOfDay.hour = 0; + timeOfDay.minute = 0; + timeOfDay.second = 0; + timeOfDay.ticks = 0; + + rtems_status_code ret = rtems_clock_set(&timeOfDay); + if (ret != RTEMS_SUCCESSFUL) { + printf("**** Can't set time %s\n", rtems_status_text(ret)); + } + } + osdTimeRegister(); + + extern void pvDataAllTests(void); + pvDataAllTests(); + epicsExit(0); +} diff --git a/test/src/Makefile b/test/src/Makefile index ed4eda8..71d09d2 100644 --- a/test/src/Makefile +++ b/test/src/Makefile @@ -1,37 +1,21 @@ -TOP=.. -include $(TOP)/configure/CONFIG +# This is a Makefile fragment, see ../Makefile -DBD += powerSupplyRegister.dbd +SRC_DIRS += $(PVDATABASE_TEST)/src -INC += powerSupply.h - -LIBRARY_IOC += powerSupply -powerSupply_SRCS += powerSupply.cpp -powerSupply_SRCS += powerSupplyRegister.cpp -powerSupply_LIBS += pvDatabase -powerSupply_LIBS += pvAccess -powerSupply_LIBS += pvData -powerSupply_LIBS += Com -powerSupply_LIBS += $(EPICS_BASE_IOC_LIBS) - - -PROD_HOST += testPVCopy -testPVCopy_SRCS += powerSupply.cpp +TESTPROD_HOST += testPVCopy testPVCopy_SRCS += testPVCopy.cpp -testPVCopy_LIBS += pvDatabase pvAccess pvData Com +testHarness_SRCS += testPVCopy.cpp +TESTS += testPVCopy -PROD_HOST += testPVRecord -testPVRecord_SRCS += powerSupply.cpp +TESTPROD_HOST += testPVRecord testPVRecord_SRCS += testPVRecord.cpp -testPVRecord_LIBS += pvDatabase pvAccess pvData Com +testHarness_SRCS += testPVRecord.cpp +TESTS += testPVRecord -PROD_HOST += testExampleRecord -testExampleRecord_SRCS += powerSupply.cpp +TESTPROD_HOST += testExampleRecord testExampleRecord_SRCS += testExampleRecord.cpp -testExampleRecord_LIBS += pvDatabase pvAccess pvData Com +testHarness_SRCS += testExampleRecord.cpp +TESTS += testExampleRecord -include $(TOP)/configure/RULES -#---------------------------------------- -# ADD RULES AFTER THIS LINE diff --git a/test/src/powerSupply.cpp b/test/src/powerSupply.cpp deleted file mode 100644 index de880c7..0000000 --- a/test/src/powerSupply.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* powerSupply.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.04.02 - */ - -#include -#include - -#define epicsExportSharedSymbols -#include "powerSupply.h" - -using namespace epics::pvData; -using std::string; -using std::cout; -using std::cerr; -using std::endl; - -namespace epics { namespace pvDatabase { - -PVStructurePtr createPowerSupply() -{ - FieldCreatePtr fieldCreate = getFieldCreate(); - StandardFieldPtr standardField = getStandardField(); - PVDataCreatePtr pvDataCreate = getPVDataCreate(); - - return pvDataCreate->createPVStructure( - fieldCreate->createFieldBuilder()-> - add("alarm",standardField->alarm()) -> - add("timeStamp",standardField->timeStamp()) -> - addNestedStructure("power") -> - add("value",pvDouble) -> - add("alarm",standardField->alarm()) -> - endNested()-> - addNestedStructure("voltage") -> - add("value",pvDouble) -> - add("alarm",standardField->alarm()) -> - endNested()-> - addNestedStructure("current") -> - add("value",pvDouble) -> - add("alarm",standardField->alarm()) -> - endNested()-> - createStructure()); -} - -PowerSupplyPtr PowerSupply::create( - string const & recordName, - PVStructurePtr const & pvStructure) -{ - PowerSupplyPtr pvRecord( - new PowerSupply(recordName,pvStructure)); - if(!pvRecord->init()) pvRecord.reset(); - return pvRecord; -} - -PowerSupply::PowerSupply( - string const & recordName, - PVStructurePtr const & pvStructure) -: PVRecord(recordName,pvStructure) -{ -} - -PowerSupply::~PowerSupply() -{ -} - -void PowerSupply::destroy() -{ - PVRecord::destroy(); -} - -bool PowerSupply::init() -{ - initPVRecord(); - PVStructurePtr pvStructure = getPVStructure(); - PVFieldPtr pvField; - bool result; - pvField = pvStructure->getSubField("timeStamp"); - if(!pvField) { - cerr << "no timeStamp" << endl; - return false; - } - result = pvTimeStamp.attach(pvField); - if(!result) { - cerr << "no timeStamp" << endl; - return false; - } - pvField = pvStructure->getSubField("alarm"); - if(!pvField) { - cerr << "no alarm" << endl; - return false; - } - result = pvAlarm.attach(pvField); - if(!result) { - cerr << "no alarm" << endl; - return false; - } - pvCurrent = pvStructure->getSubField("current.value"); - if(!pvCurrent) { - cerr << "no current\n"; - return false; - } - pvVoltage = pvStructure->getSubField("voltage.value"); - if(!pvVoltage) { - cerr << "no current\n"; - return false; - } - pvPower = pvStructure->getSubField("power.value"); - if(!pvPower) { - cerr << "no powert\n"; - return false; - } - return true; -} - -void PowerSupply::process() -{ - timeStamp.getCurrent(); - pvTimeStamp.set(timeStamp); - double voltage = pvVoltage->get(); - double power = pvPower->get(); - if(voltage<1e-3 && voltage>-1e-3) { - alarm.setMessage("bad voltage"); - alarm.setSeverity(majorAlarm); - pvAlarm.set(alarm); - return; - } - double current = power/voltage; - pvCurrent->put(current); - alarm.setMessage(""); - alarm.setSeverity(noAlarm); - pvAlarm.set(alarm); -} - -void PowerSupply::put(double power,double voltage) -{ - pvPower->put(power); - pvVoltage->put(voltage); -} - -double PowerSupply::getPower() -{ - return pvPower->get(); -} - -double PowerSupply::getVoltage() -{ - return pvVoltage->get(); -} - -double PowerSupply::getCurrent() -{ - return pvCurrent->get(); -} - - -}} diff --git a/test/src/powerSupply.h b/test/src/powerSupply.h index 4babcc1..c3303d6 100644 --- a/test/src/powerSupply.h +++ b/test/src/powerSupply.h @@ -30,11 +30,10 @@ #include +//epicsShareExtern epics::pvData::PVStructurePtr createPowerSupply(); namespace epics { namespace pvDatabase { -epicsShareExtern epics::pvData::PVStructurePtr createPowerSupply(); - class PowerSupply; typedef std::tr1::shared_ptr PowerSupplyPtr; @@ -66,6 +65,141 @@ private: epics::pvData::TimeStamp timeStamp; }; +epics::pvData::PVStructurePtr createPowerSupply() +{ + epics::pvData::FieldCreatePtr fieldCreate = epics::pvData::getFieldCreate(); + epics::pvData::StandardFieldPtr standardField = epics::pvData::getStandardField(); + epics::pvData::PVDataCreatePtr pvDataCreate = epics::pvData::getPVDataCreate(); + + return pvDataCreate->createPVStructure( + fieldCreate->createFieldBuilder()-> + add("alarm",standardField->alarm()) -> + add("timeStamp",standardField->timeStamp()) -> + addNestedStructure("power") -> + add("value",epics::pvData::pvDouble) -> + add("alarm",standardField->alarm()) -> + endNested()-> + addNestedStructure("voltage") -> + add("value",epics::pvData::pvDouble) -> + add("alarm",standardField->alarm()) -> + endNested()-> + addNestedStructure("current") -> + add("value",epics::pvData::pvDouble) -> + add("alarm",standardField->alarm()) -> + endNested()-> + createStructure()); +} + +PowerSupplyPtr PowerSupply::create( + std::string const & recordName, + epics::pvData::PVStructurePtr const & pvStructure) +{ + PowerSupplyPtr pvRecord( + new PowerSupply(recordName,pvStructure)); + if(!pvRecord->init()) pvRecord.reset(); + return pvRecord; +} + +PowerSupply::PowerSupply( + std::string const & recordName, + epics::pvData::PVStructurePtr const & pvStructure) +: PVRecord(recordName,pvStructure) +{ +} + +PowerSupply::~PowerSupply() +{ +} + +void PowerSupply::destroy() +{ + PVRecord::destroy(); +} + +bool PowerSupply::init() +{ + initPVRecord(); + epics::pvData::PVStructurePtr pvStructure = getPVStructure(); + epics::pvData::PVFieldPtr pvField; + bool result; + pvField = pvStructure->getSubField("timeStamp"); + if(!pvField) { + std::cerr << "no timeStamp" << std::endl; + return false; + } + result = pvTimeStamp.attach(pvField); + if(!result) { + std::cerr << "no timeStamp" << std::endl; + return false; + } + pvField = pvStructure->getSubField("alarm"); + if(!pvField) { + std::cerr << "no alarm" << std::endl; + return false; + } + result = pvAlarm.attach(pvField); + if(!result) { + std::cerr << "no alarm" << std::endl; + return false; + } + pvCurrent = pvStructure->getSubField("current.value"); + if(!pvCurrent) { + std::cerr << "no current\n"; + return false; + } + pvVoltage = pvStructure->getSubField("voltage.value"); + if(!pvVoltage) { + std::cerr << "no current\n"; + return false; + } + pvPower = pvStructure->getSubField("power.value"); + if(!pvPower) { + std::cerr << "no powert\n"; + return false; + } + return true; +} + +void PowerSupply::process() +{ + timeStamp.getCurrent(); + pvTimeStamp.set(timeStamp); + double voltage = pvVoltage->get(); + double power = pvPower->get(); + if(voltage<1e-3 && voltage>-1e-3) { + alarm.setMessage("bad voltage"); + alarm.setSeverity(epics::pvData::majorAlarm); + pvAlarm.set(alarm); + return; + } + double current = power/voltage; + pvCurrent->put(current); + alarm.setMessage(""); + alarm.setSeverity(epics::pvData::noAlarm); + pvAlarm.set(alarm); +} + +void PowerSupply::put(double power,double voltage) +{ + pvPower->put(power); + pvVoltage->put(voltage); +} + +double PowerSupply::getPower() +{ + return pvPower->get(); +} + +double PowerSupply::getVoltage() +{ + return pvVoltage->get(); +} + +double PowerSupply::getCurrent() +{ + return pvCurrent->get(); +} + }} diff --git a/test/src/powerSupplyRegister.cpp b/test/src/powerSupplyRegister.cpp deleted file mode 100644 index 1e038c9..0000000 --- a/test/src/powerSupplyRegister.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/*powerSupplyRegister.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.07.24 - */ - - -/* Author: Marty Kraimer */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; -using std::cout; -using std::endl; - - -static const iocshArg testArg0 = { "recordName", iocshArgString }; -static const iocshArg *testArgs[] = { - &testArg0}; - -static const iocshFuncDef powerSupplyFuncDef = { - "powerSupplyCreateRecord", 1, testArgs}; -static void powerSupplyCallFunc(const iocshArgBuf *args) -{ - char *recordName = args[0].sval; - PVStructurePtr pv = createPowerSupply(); - - PowerSupplyPtr record = PowerSupply::create(recordName,pv); - bool result = PVDatabase::getMaster()->addRecord(record); - if(!result) cout << "recordname" << " not added" << endl; -} - -static void powerSupplyRegister(void) -{ - static int firstTime = 1; - if (firstTime) { - firstTime = 0; - iocshRegister(&powerSupplyFuncDef, powerSupplyCallFunc); - } -} - -extern "C" { - epicsExportRegistrar(powerSupplyRegister); -} diff --git a/test/src/powerSupplyRegister.dbd b/test/src/powerSupplyRegister.dbd deleted file mode 100644 index 1444fc1..0000000 --- a/test/src/powerSupplyRegister.dbd +++ /dev/null @@ -1 +0,0 @@ -registrar("powerSupplyRegister") diff --git a/test/src/testExampleRecord.cpp b/test/src/testExampleRecord.cpp index 02b79c4..821fc83 100644 --- a/test/src/testExampleRecord.cpp +++ b/test/src/testExampleRecord.cpp @@ -10,6 +10,9 @@ /* Author: Marty Kraimer */ +#include +#include + #include #include #include @@ -37,7 +40,7 @@ using namespace epics::pvData; using namespace epics::pvAccess; using namespace epics::pvDatabase; - +static bool debug = false; void test() @@ -57,7 +60,7 @@ void test() pvRecord->process(); pvRecord->unlock(); } - cout << "processed exampleDouble " << endl; + if(debug) {cout << "processed exampleDouble " << endl; } pvRecord->destroy(); pvRecord.reset(); recordName = "powerSupplyExample"; @@ -65,8 +68,9 @@ void test() PowerSupplyPtr psr; pvStructure = createPowerSupply(); psr = PowerSupply::create("powerSupply",pvStructure); - if(psr.get()==NULL) { - cout << "PowerSupplyRecordTest::create failed" << endl; + testOk1(psr.get()!=0); + if(!psr) { + if(debug) {cout << "PowerSupplyRecordTest::create failed" << endl;} return; } pvStructure.reset(); @@ -78,17 +82,24 @@ void test() current = psr->getCurrent(); psr->unlock(); } - cout << "initial "; - cout << " voltage " << voltage ; - cout << " power " << power; - cout << " current " << current; - cout << endl; + if(debug ) { + cout << "initial "; + cout << " voltage " << voltage ; + cout << " power " << power; + cout << " current " << current; + cout << endl; + } + testOk1(psr->getVoltage()==0.0); + testOk1(psr->getPower()==0.0); + testOk1(psr->getCurrent()==0.0); voltage = 1.0; power = 1.0; - cout << "before put "; - cout << " voltage " << voltage ; - cout << " power " << power; - cout << endl; + if(debug) { + cout << "before put "; + cout << " voltage " << voltage ; + cout << " power " << power; + cout << endl; + } { psr->lock(); psr->put(power,voltage); @@ -97,21 +108,27 @@ void test() } { psr->lock(); - cout << "after put "; - cout << " voltage " << psr->getVoltage() ; - cout << " power " << psr->getPower(); - cout << " current " << psr->getCurrent(); - cout << endl; + if(debug) { + cout << "after put "; + cout << " voltage " << psr->getVoltage() ; + cout << " power " << psr->getPower(); + cout << " current " << psr->getCurrent(); + cout << endl; + } psr->unlock(); } + testOk1(psr->getVoltage()==1.0); + testOk1(psr->getPower()==1.0); + testOk1(psr->getCurrent()==1.0); PVDatabasePtr pvDatabase = PVDatabase::getMaster(); pvDatabase->addRecord(psr); psr.reset(); pvDatabase->destroy(); } -int main(int argc,char *argv[]) +MAIN(testExampleRecord) { + testPlan(7); test(); return 0; } diff --git a/test/src/testPVCopy.cpp b/test/src/testPVCopy.cpp index 221aa39..db70e70 100644 --- a/test/src/testPVCopy.cpp +++ b/test/src/testPVCopy.cpp @@ -9,6 +9,8 @@ */ /* Author: Marty Kraimer */ +#include +#include #include #include @@ -36,6 +38,7 @@ using namespace epics::pvData; using namespace epics::pvAccess; using namespace epics::pvDatabase; +static bool debug = false; class MyRequester; typedef std::tr1::shared_ptr MyRequesterPtr; @@ -89,58 +92,79 @@ static void testPVScalar( size_t offset; ConvertPtr convert = getConvert(); - cout << endl; + if(debug) cout << endl; pvStructureRecord = pvRecord->getPVRecordStructure()->getPVStructure(); pvField = pvStructureRecord->getSubField(valueNameRecord); pvValueRecord = static_pointer_cast(pvField); convert->fromDouble(pvValueRecord,.04); StructureConstPtr structure = pvCopy->getStructure(); - cout << "structure from copy" << endl << *structure << endl; + if(debug) cout << "structure from copy" << endl << *structure << endl; pvStructureCopy = pvCopy->createPVStructure(); pvField = pvStructureCopy->getSubField(valueNameCopy); pvValueCopy = static_pointer_cast(pvField); bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields())); pvCopy->initCopy(pvStructureCopy, bitSet); - cout << "after initCopy pvValueCopy " << convert->toDouble(pvValueCopy); - cout << endl; + if(debug) { + cout << "after initCopy pvValueCopy " << convert->toDouble(pvValueCopy); + cout << endl; + } convert->fromDouble(pvValueRecord,.06); + testOk1(convert->toDouble(pvValueCopy)==.04); pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet); - cout << "after put(.06) pvValueCopy " << convert->toDouble(pvValueCopy); - cout << " bitSet " << *bitSet; - cout << endl; + testOk1(convert->toDouble(pvValueCopy)==.06); + testOk1(bitSet->get(pvValueCopy->getFieldOffset())); + if(debug) { + cout << "after put(.06) pvValueCopy " << convert->toDouble(pvValueCopy); + cout << " bitSet " << *bitSet; + cout << endl; + } offset = pvCopy->getCopyOffset(pvValueRecord); - cout << "getCopyOffset() " << offset; - cout << " pvValueCopy->getOffset() " << pvValueCopy->getFieldOffset(); - cout << " pvValueRecord->getOffset() " << pvValueRecord->getFieldOffset(); - cout << " bitSet " << *bitSet; - cout << endl; + if(debug) { + cout << "getCopyOffset() " << offset; + cout << " pvValueCopy->getOffset() " << pvValueCopy->getFieldOffset(); + cout << " pvValueRecord->getOffset() " << pvValueRecord->getFieldOffset(); + cout << " bitSet " << *bitSet; + cout << endl; + } bitSet->clear(); convert->fromDouble(pvValueRecord,1.0); - cout << "before updateCopyFromBitSet"; - cout << " recordValue " << convert->toDouble(pvValueRecord); - cout << " copyValue " << convert->toDouble(pvValueCopy); - cout << " bitSet " << *bitSet; - cout << endl; + if(debug) { + cout << "before updateCopyFromBitSet"; + cout << " recordValue " << convert->toDouble(pvValueRecord); + cout << " copyValue " << convert->toDouble(pvValueCopy); + cout << " bitSet " << *bitSet; + cout << endl; + } bitSet->set(0); + testOk1(convert->toDouble(pvValueCopy)==0.06); pvCopy->updateCopyFromBitSet(pvStructureCopy,bitSet); - cout << "after updateCopyFromBitSet"; - cout << " recordValue " << convert->toDouble(pvValueRecord); - cout << " copyValue " << convert->toDouble(pvValueCopy); - cout << " bitSet " << *bitSet; - cout << endl; + testOk1(convert->toDouble(pvValueCopy)==1.0); + if(debug) { + cout << "after updateCopyFromBitSet"; + cout << " recordValue " << convert->toDouble(pvValueRecord); + cout << " copyValue " << convert->toDouble(pvValueCopy); + cout << " bitSet " << *bitSet; + cout << endl; + } convert->fromDouble(pvValueCopy,2.0); bitSet->set(0); - cout << "before updateMaster"; - cout << " recordValue " << convert->toDouble(pvValueRecord); - cout << " copyValue " << convert->toDouble(pvValueCopy); - cout << " bitSet " << *bitSet; - cout << endl; + if(debug) { + cout << "before updateMaster"; + cout << " recordValue " << convert->toDouble(pvValueRecord); + cout << " copyValue " << convert->toDouble(pvValueCopy); + cout << " bitSet " << *bitSet; + cout << endl; + } + testOk1(convert->toDouble(pvValueRecord)==1.0); pvCopy->updateMaster(pvStructureCopy,bitSet); - cout << "after updateMaster"; - cout << " recordValue " << convert->toDouble(pvValueRecord); - cout << " copyValue " << convert->toDouble(pvValueCopy); - cout << " bitSet " << *bitSet; - cout << endl; + testOk1(convert->toDouble(pvValueRecord)==2.0); + if(debug) { + cout << "after updateMaster"; + cout << " recordValue " << convert->toDouble(pvValueRecord); + cout << " copyValue " << convert->toDouble(pvValueCopy); + cout << " bitSet " << *bitSet; + cout << endl; + } } static void testPVScalarArray( @@ -157,72 +181,100 @@ static void testPVScalarArray( size_t offset; size_t n = 5; shared_vector values(n); - cout << endl; + shared_vector cvalues; + + if(debug) {cout << endl;} pvStructureRecord = pvRecord->getPVRecordStructure()->getPVStructure(); pvValueRecord = pvStructureRecord->getSubField(valueNameRecord); for(size_t i=0; i xxx(freeze(values)); pvValueRecord->putFrom(xxx); StructureConstPtr structure = pvCopy->getStructure(); - cout << "structure from copy" << endl << *structure << endl; + if(debug) { cout << "structure from copy" << endl << *structure << endl;} pvStructureCopy = pvCopy->createPVStructure(); pvValueCopy = pvStructureCopy->getSubField(valueNameCopy); bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields())); pvCopy->initCopy(pvStructureCopy, bitSet); - cout << "after initCopy pvValueCopy " << *pvValueCopy << endl; - cout << endl; + if(debug) { + cout << "after initCopy pvValueCopy " << *pvValueCopy << endl; + cout << endl; + } values.resize(n); for(size_t i=0; i yyy(freeze(values)); pvValueRecord->putFrom(yyy); + pvValueCopy->getAs(cvalues); + testOk1(cvalues[0]==0.0); pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet); - cout << "after put(i+ .06) pvValueCopy " << *pvValueCopy << endl; - cout << " bitSet " << *bitSet; - cout << endl; + pvValueCopy->getAs(cvalues); + testOk1(cvalues[0]==0.06); + if(debug) { + cout << "after put(i+ .06) pvValueCopy " << *pvValueCopy << endl; + cout << " bitSet " << *bitSet; + cout << endl; + } offset = pvCopy->getCopyOffset(pvValueRecord); - cout << "getCopyOffset() " << offset; - cout << " pvValueCopy->getOffset() " << pvValueCopy->getFieldOffset(); - cout << " pvValueRecord->getOffset() " << pvValueRecord->getFieldOffset(); - cout << " bitSet " << *bitSet; - cout << endl; + if(debug) { + cout << "getCopyOffset() " << offset; + cout << " pvValueCopy->getOffset() " << pvValueCopy->getFieldOffset(); + cout << " pvValueRecord->getOffset() " << pvValueRecord->getFieldOffset(); + cout << " bitSet " << *bitSet; + cout << endl; + } bitSet->clear(); values.resize(n); for(size_t i=0; i zzz(freeze(values)); pvValueRecord->putFrom(zzz); - cout << "before updateCopyFromBitSet"; - cout << " recordValue " << *pvValueRecord << endl; - cout << " copyValue " << *pvValueCopy << endl; - cout << " bitSet " << *bitSet; - cout << endl; + if(debug) { + cout << "before updateCopyFromBitSet"; + cout << " recordValue " << *pvValueRecord << endl; + cout << " copyValue " << *pvValueCopy << endl; + cout << " bitSet " << *bitSet; + cout << endl; + } bitSet->set(0); + pvValueCopy->getAs(cvalues); + testOk1(cvalues[0]==0.06); pvCopy->updateCopyFromBitSet(pvStructureCopy,bitSet); - cout << "after updateCopyFromBitSet"; - cout << " recordValue " << *pvValueRecord << endl; - cout << " copyValue " << *pvValueCopy << endl; - cout << " bitSet " << *bitSet; - cout << endl; + pvValueCopy->getAs(cvalues); + testOk1(cvalues[0]==1.0); + if(debug) { + cout << "after updateCopyFromBitSet"; + cout << " recordValue " << *pvValueRecord << endl; + cout << " copyValue " << *pvValueCopy << endl; + cout << " bitSet " << *bitSet; + cout << endl; + } values.resize(n); for(size_t i=0; i ttt(freeze(values)); pvValueRecord->putFrom(ttt); bitSet->set(0); - cout << "before updateMaster"; - cout << " recordValue " << *pvValueRecord << endl; - cout << " copyValue " << *pvValueCopy << endl; - cout << " bitSet " << *bitSet; - cout << endl; + if(debug) { + cout << "before updateMaster"; + cout << " recordValue " << *pvValueRecord << endl; + cout << " copyValue " << *pvValueCopy << endl; + cout << " bitSet " << *bitSet; + cout << endl; + } + pvValueRecord->getAs(cvalues); + testOk1(cvalues[0]==2.0); pvCopy->updateMaster(pvStructureCopy,bitSet); - cout << "after updateMaster"; - cout << " recordValue " << *pvValueRecord << endl; - cout << " copyValue " << *pvValueRecord << endl; - cout << " bitSet " << *bitSet; - cout << endl; + pvValueRecord->getAs(cvalues); + testOk1(cvalues[0]==1.0); + if(debug) { + cout << "after updateMaster"; + cout << " recordValue " << *pvValueRecord << endl; + cout << " copyValue " << *pvValueRecord << endl; + cout << " bitSet " << *bitSet; + cout << endl; + } } static void scalarTest() { - cout << endl << endl << "****scalarTest****" << endl; + if(debug) {cout << endl << endl << "****scalarTest****" << endl;} RequesterPtr requester(new MyRequester("exampleTest")); PVRecordPtr pvRecord; string request; @@ -236,21 +288,27 @@ static void scalarTest() valueNameRecord = request = "value"; CreateRequest::shared_pointer createRequest = CreateRequest::create(); pvRequest = createRequest->createRequest(request); - cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + if(debug) { + cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + } pvCopy = PVCopy::create(pvRecord->getPVRecordStructure()->getPVStructure(),pvRequest,""); valueNameCopy = "value"; testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy); request = ""; valueNameRecord = "value"; pvRequest = createRequest->createRequest(request); - cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + if(debug) { + cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + } pvCopy = PVCopy::create(pvRecord->getPVRecordStructure()->getPVStructure(),pvRequest,""); valueNameCopy = "value"; testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy); request = "alarm,timeStamp,value"; valueNameRecord = "value"; pvRequest = createRequest->createRequest(request); - cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + if(debug) { + cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + } pvCopy = PVCopy::create(pvRecord->getPVRecordStructure()->getPVStructure(),pvRequest,""); valueNameCopy = "value"; testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy); @@ -259,7 +317,7 @@ static void scalarTest() static void arrayTest() { - cout << endl << endl << "****arrayTest****" << endl; + if(debug) {cout << endl << endl << "****arrayTest****" << endl;} RequesterPtr requester(new MyRequester("exampleTest")); PVRecordPtr pvRecord; string request; @@ -273,21 +331,21 @@ static void arrayTest() pvRecord = createScalarArray("doubleArrayRecord",pvDouble,"alarm,timeStamp"); valueNameRecord = request = "value"; pvRequest = createRequest->createRequest(request); - cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + if(debug) {cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ;} pvCopy = PVCopy::create(pvRecord->getPVRecordStructure()->getPVStructure(),pvRequest,""); valueNameCopy = "value"; testPVScalarArray(valueNameRecord,valueNameCopy,pvRecord,pvCopy); request = ""; valueNameRecord = "value"; pvRequest = createRequest->createRequest(request); - cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + if(debug) {cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ;} pvCopy = PVCopy::create(pvRecord->getPVRecordStructure()->getPVStructure(),pvRequest,""); valueNameCopy = "value"; testPVScalarArray(valueNameRecord,valueNameCopy,pvRecord,pvCopy); request = "alarm,timeStamp,value"; valueNameRecord = "value"; pvRequest = createRequest->createRequest(request); - cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + if(debug) {cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ;} pvCopy = PVCopy::create(pvRecord->getPVRecordStructure()->getPVStructure(),pvRequest,""); valueNameCopy = "value"; testPVScalarArray(valueNameRecord,valueNameCopy,pvRecord,pvCopy); @@ -296,7 +354,7 @@ static void arrayTest() static void powerSupplyTest() { - cout << endl << endl << "****powerSupplyTest****" << endl; + if(debug) {cout << endl << endl << "****powerSupplyTest****" << endl;} RequesterPtr requester(new MyRequester("exampleTest")); PowerSupplyPtr pvRecord; string request; @@ -311,36 +369,37 @@ static void powerSupplyTest() pvRecord = PowerSupply::create("powerSupply",pv); valueNameRecord = request = "power.value"; pvRequest = createRequest->createRequest(request); - cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + if(debug) {cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ;} pvCopy = PVCopy::create(pvRecord->getPVRecordStructure()->getPVStructure(),pvRequest,""); valueNameCopy = "power.value"; testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy); request = ""; valueNameRecord = "power.value"; pvRequest = createRequest->createRequest(request); - cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + if(debug) {cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ;} pvCopy = PVCopy::create(pvRecord->getPVRecordStructure()->getPVStructure(),pvRequest,""); valueNameCopy = "power.value"; testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy); request = "alarm,timeStamp,voltage.value,power.value,current.value"; valueNameRecord = "power.value"; pvRequest = createRequest->createRequest(request); - cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + if(debug) {cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ;} pvCopy = PVCopy::create(pvRecord->getPVRecordStructure()->getPVStructure(),pvRequest,""); valueNameCopy = "power.value"; testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy); request = "alarm,timeStamp,voltage{value,alarm},power{value,alarm,display},current.value"; valueNameRecord = "power.value"; pvRequest = createRequest->createRequest(request); - cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ; + if(debug) {cout << "request " << request << endl << "pvRequest" << *pvRequest << endl ;} pvCopy = PVCopy::create(pvRecord->getPVRecordStructure()->getPVStructure(),pvRequest,""); valueNameCopy = "power.value"; testPVScalar(valueNameRecord,valueNameCopy,pvRecord,pvCopy); pvRecord->destroy(); } -int main(int argc,char *argv[]) +MAIN(testPVCopy) { + testPlan(67); scalarTest(); arrayTest(); powerSupplyTest(); diff --git a/test/src/testPVRecord.cpp b/test/src/testPVRecord.cpp index a7b1e29..fa0b50d 100644 --- a/test/src/testPVRecord.cpp +++ b/test/src/testPVRecord.cpp @@ -9,6 +9,8 @@ */ /* Author: Marty Kraimer */ +#include +#include #include #include @@ -36,6 +38,8 @@ using namespace epics::pvData; using namespace epics::pvDatabase; using std::string; +static bool debug = false; + static PVRecordPtr createScalar( string const & recordName, ScalarType scalarType, @@ -58,31 +62,44 @@ static PVRecordPtr createScalarArray( static void scalarTest() { - cout << endl << endl << "****scalarTest****" << endl; + if(debug) {cout << endl << endl << "****scalarTest****" << endl; } PVRecordPtr pvRecord; pvRecord = createScalar("doubleRecord",pvDouble,"alarm,timeStamp.display"); + testOk1(pvRecord.get()!=0); + if(pvRecord && debug) { + cout << pvRecord << endl; + } pvRecord->destroy(); } static void arrayTest() { - cout << endl << endl << "****arrayTest****" << endl; + if(debug) {cout << endl << endl << "****arrayTest****" << endl; } PVRecordPtr pvRecord; pvRecord = createScalarArray("doubleArrayRecord",pvDouble,"alarm,timeStamp"); + testOk1(pvRecord.get()!=0); + if(pvRecord && debug) { + cout << pvRecord << endl; + } pvRecord->destroy(); } static void powerSupplyTest() { - cout << endl << endl << "****powerSupplyTest****" << endl; + if(debug) {cout << endl << endl << "****powerSupplyTest****" << endl; } PVRecordPtr pvRecord; PVStructurePtr pv = createPowerSupply(); pvRecord = PowerSupply::create("powerSupply",pv); + testOk1(pvRecord.get()!=0); + if(pvRecord && debug) { + cout << pvRecord << endl; + } pvRecord->destroy(); } -int main(int argc,char *argv[]) +MAIN(testPVRecord) { + testPlan(3); scalarTest(); arrayTest(); powerSupplyTest();