Merge pull request #5 from mrkraimer/master

remove examples, make test regression test, update documentation
This commit is contained in:
Marty Kraimer
2016-01-25 07:10:07 -05:00
231 changed files with 907 additions and 37872 deletions

View File

@ -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

View File

@ -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

View File

@ -17,18 +17,18 @@ then just type:
make
If RELEASE.local does not exist then look at <b>configure/RELEASE</b>
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
------

View File

@ -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
====================

View File

@ -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

View File

@ -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

View File

@ -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=</path/name/to/install/top>
# 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 = </IOC/path/to/application/top>
INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv
USR_INCLUDES += -I $(INSTALL_LOCATION)/include
-include $(TOP)/../../CONFIG_SITE.local
-include $(TOP)/../configure/CONFIG_SITE.local

View File

@ -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

View File

@ -1,8 +0,0 @@
TOP=..
include $(TOP)/configure/CONFIG
TARGETS = $(CONFIG_TARGETS)
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
include $(TOP)/configure/RULES

View File

@ -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

View File

@ -1,6 +0,0 @@
# RULES
include $(CONFIG)/RULES
# Library should be rebuilt because LIBOBJS may have changed.
$(LIBNAME): ../Makefile

View File

@ -1,2 +0,0 @@
#RULES.ioc
include $(CONFIG)/RULES.ioc

View File

@ -1,2 +0,0 @@
#RULES_DIRS
include $(CONFIG)/RULES_DIRS

View File

@ -1,2 +0,0 @@
#RULES_TOP
include $(CONFIG)/RULES_TOP

View File

@ -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

View File

@ -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 <pv/lock.h>
#define epicsExportSharedSymbols
#include <arrayPerformance.h>
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<PVLongArray>("value");
if(!pvLongArray) return false;
pvValue = pvLongArray;
ArrayPerformancePtr xxx = dynamic_pointer_cast<ArrayPerformance>(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<epicsThread>(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<int64> xxx(arrayPerformance->size,value++);
shared_vector<const int64> data(freeze(xxx));
arrayPerformance->beginGroupPut();
arrayPerformance->pvValue->replace(data);
arrayPerformance->process();
arrayPerformance->endGroupPut();
} catch(...) {
arrayPerformance->unlock();
throw;
}
arrayPerformance->unlock();
}
}
}}

View File

@ -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 <epicsThread.h>
#include <pv/standardPVField.h>
#include <pv/timeStamp.h>
#include <pv/pvTimeStamp.h>
#include <pv/pvDatabase.h>
#ifdef arrayperformanceEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef arrayperformanceEpicsExportSharedSymbols
# include <shareLib.h>
#endif
namespace epics { namespace pvDatabase {
class ArrayPerformance;
typedef std::tr1::shared_ptr<ArrayPerformance> ArrayPerformancePtr;
class ArrayPerformanceThread;
typedef std::tr1::shared_ptr<ArrayPerformanceThread> 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<epicsThread> thread;
};
}}
#endif /* ARRAYPERFORMANCE_H */

View File

@ -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 <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <vector>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/traceRecord.h>
#include <pv/channelProviderLocal.h>
#include <pv/serverContext.h>
#include <pv/clientFactory.h>
#include <arrayPerformance.h>
#include <longArrayMonitor.h>
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<LongArrayMonitorPtr> longArrayMonitor(nMonitor);
for(size_t i=0; i<nMonitor; ++i) {
longArrayMonitor[i]
= LongArrayMonitor::create(providerName,recordName,queueSize,waitTime);
}
epicsThreadSleep(1.0);
for(size_t i=0; i<nMonitor; ++i) longArrayMonitor[i]->start();
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; i<nMonitor; ++i) longArrayMonitor[i]->stop();
for(size_t i=0; i<nMonitor; ++i) longArrayMonitor[i]->destroy();
pvaServer->shutdown();
epicsThreadSleep(1.0);
pvaServer->destroy();
ClientFactory::stop();
epicsThreadSleep(1.0);
channelProvider->destroy();
return 0;
}

View File

@ -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 <epicsThread.h>
#include <pv/caProvider.h>
#define epicsExportSharedSymbols
#include <longArrayGet.h>
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<LongArrayChannelRequester> LongArrayChannelRequesterPtr;
class LongArrayChannelGetRequester;
typedef std::tr1::shared_ptr<LongArrayChannelGetRequester> LongArrayChannelGetRequesterPtr;
class LongArrayChannelRequester :
virtual public ChannelRequester,
public std::tr1::enable_shared_from_this<LongArrayChannelRequester>
{
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<LongArrayChannelGetRequester>
{
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<LongArrayChannelGet>,
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<epicsThread> 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<const ScalarArray>(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<epicsThread>(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<PVLongArray>(pvStructure->getSubField("value"));
if(!bitSet->get(pvValue->getFieldOffset())) {
return 0;
}
bitSet->clear();
shared_vector<const int64> 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();
}
}}

View File

@ -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 <pv/event.h>
#include <pv/lock.h>
#include <pv/standardPVField.h>
#include <pv/timeStamp.h>
#include <pv/pvTimeStamp.h>
#include <pv/pvAccess.h>
#ifdef longarraygetEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef longarraygetEpicsExportSharedSymbols
# include <shareLib.h>
#endif
namespace epics { namespace pvDatabase {
class LongArrayGet;
typedef std::tr1::shared_ptr<LongArrayGet> LongArrayGetPtr;
class LongArrayChannelGet;
typedef std::tr1::shared_ptr<LongArrayChannelGet> LongArrayChannelGetPtr;
class epicsShareClass LongArrayGet :
public std::tr1::enable_shared_from_this<LongArrayGet>
{
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 */

View File

@ -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 <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <vector>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/traceRecord.h>
#include <pv/channelProviderLocal.h>
#include <pv/serverContext.h>
#include <pv/clientFactory.h>
#include <arrayPerformance.h>
#include <longArrayGet.h>
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<delayTime) xxx = delayTime;
ClientFactory::stop();
epicsThreadSleep(xxx);
return 0;
}

View File

@ -1,319 +0,0 @@
/* longArrayMonitor.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 <epicsThread.h>
#include <pv/caProvider.h>
#define epicsExportSharedSymbols
#include <longArrayMonitor.h>
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<epicsThread> thread;
};
void LAMMonitorRequester::init()
{
thread = std::auto_ptr<epicsThread>(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<const ScalarArray>(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<PVLongArray>(pvStructure->getSubField("value"));
shared_vector<const int64> 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();
}
}}

View File

@ -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 <pv/event.h>
#include <pv/lock.h>
#include <pv/standardPVField.h>
#include <pv/timeStamp.h>
#include <pv/pvTimeStamp.h>
#include <pv/pvAccess.h>
#ifdef longarraymonitorEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef longarraymonitorEpicsExportSharedSymbols
# include <shareLib.h>
#endif
namespace epics { namespace pvDatabase {
class LongArrayMonitor;
typedef std::tr1::shared_ptr<LongArrayMonitor> LongArrayMonitorPtr;
class LAMChannelRequester;
typedef std::tr1::shared_ptr<LAMChannelRequester> LAMChannelRequesterPtr;
class LAMMonitorRequester;
typedef std::tr1::shared_ptr<LAMMonitorRequester> LAMMonitorRequesterPtr;
class epicsShareClass LongArrayMonitor :
public std::tr1::enable_shared_from_this<LongArrayMonitor>
{
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 */

View File

@ -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 <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <vector>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/traceRecord.h>
#include <pv/channelProviderLocal.h>
#include <pv/serverContext.h>
#include <pv/clientFactory.h>
#include <arrayPerformance.h>
#include <longArrayMonitor.h>
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;
}

View File

@ -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 <epicsThread.h>
#include <pv/caProvider.h>
#define epicsExportSharedSymbols
#include <longArrayPut.h>
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<LongArrayChannelPut>,
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<epicsThread> 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<epicsThread>(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<const ScalarArray>(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<PVLongArray>(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<int64> xxx(arraySize,numChannelPut);
shared_vector<const int64> 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();
}
}}

View File

@ -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 <pv/event.h>
#include <pv/lock.h>
#include <pv/standardPVField.h>
#include <pv/timeStamp.h>
#include <pv/pvTimeStamp.h>
#include <pv/pvAccess.h>
#ifdef longarrayputEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef longarrayputEpicsExportSharedSymbols
# include <shareLib.h>
#endif
namespace epics { namespace pvDatabase {
class LongArrayPut;
typedef std::tr1::shared_ptr<LongArrayPut> LongArrayPutPtr;
class LongArrayChannelPut;
typedef std::tr1::shared_ptr<LongArrayChannelPut> LongArrayChannelPutPtr;
class epicsShareClass LongArrayPut :
public std::tr1::enable_shared_from_this<LongArrayPut>
{
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 */

View File

@ -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 <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <vector>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/traceRecord.h>
#include <pv/channelProviderLocal.h>
#include <pv/serverContext.h>
#include <pv/clientFactory.h>
#include <arrayPerformance.h>
#include <longArrayPut.h>
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<delayTime) xxx = delayTime;
epicsThreadSleep(xxx);
ClientFactory::stop();
epicsThreadSleep(1.0);
return 0;
}

View File

@ -1,189 +0,0 @@
/*vectorPerformanceMain.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.09.02
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <vector>
#include <epicsThread.h>
#include <pv/timeStamp.h>
using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
class VectorPerformanceThread;
typedef std::tr1::shared_ptr<VectorPerformanceThread> 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<int64> vector;
std::auto_ptr<epicsThread> 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<epicsThread>(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; i<size; ++i) vector[i] = value;
}
}
int main(int argc,char *argv[])
{
size_t size = 50000000;
double delay = .01;
size_t nThread = 1;
if(argc==2 && string(argv[1])==string("-help")) {
cout << "vectorPerformanceMain size delay nThread" << endl;
cout << "default" << endl;
cout << "vectorPerformance ";
cout << size << " ";
cout << delay << " ";
cout << nThread << " ";
cout << endl;
return 0;
}
if(argc>1) 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<VectorPerformanceThreadPtr> threads;
for(size_t i=0; i<nThread; ++i) {
threads.push_back(
VectorPerformanceThreadPtr(
new VectorPerformanceThread(i,size,delay)));
}
epicsThreadSleep(.1);
for(size_t i=0; i<nThread; ++i) {
threads[i]->init();
}
for(size_t i=0; i<nThread; ++i) {
threads[i]->start();
}
string str;
while(true) {
cout << "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
for(size_t i=0; i<nThread; ++i) {
cout << "" << i << " calling destroy" << endl;
threads[i]->destroy();
threads[i].reset();
}
return 0;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -1,714 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title>pvDatabaseCPP</title>
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/base.css" />
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
<style type="text/css">
/*<![CDATA[*/
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
table { margin-left: auto; margin-right: auto }
.diagram { text-align: center; margin: 2.5em 0 }
span.opt { color: grey }
span.nterm { font-style:italic }
span.term { font-family:courier }
span.user { font-family:courier }
span.user:before { content:"<" }
span.user:after { content:">" }
.nonnorm { font-style:italic }
p.ed { color: #AA0000 }
span.ed { color: #AA0000 }
p.ed.priv { display: inline; }
span.ed.priv { display: inline; }
/*]]>*/</style>
<!-- Script that generates the Table of Contents -->
<script type="text/javascript"
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
</script>
</head>
<body>
<div class="head">
<h1>pvDatabaseCPP</h1>
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 27-Nov-2012</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>This version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>Previous version:</dt>
<dd>None</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
</dl>
<p class="copyright">This product is made available subject to acceptance of the <a
href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p>
<hr />
</div>
<h2 class="nocount">Abstract</h2>
<p>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.</p>
<p>EPICS version 4 is a set of related products in the EPICS
V4 control system programming environment:<br />
<a href="http://epics-pvdata.sourceforge.net/relatedDocumemntsV4.html">relatedDocumentsV4.html</a>
</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 27-Nov-2012 version of the definition of pvDatabaseCPP.
This is the original version.
</p>
<p>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.</p>
<div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
</div>
<div id="contents" class="contents">
<h2>Introduction</h2>
<h3>Overview</h3>
<p>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.
</p>
<p>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.</p>
<p>This document describes components that provides the following features:
<dl>
<dt>database<dt>
<dd>This encapsulates the concept of a database of memory resident smart records.
The two main components are:
<dl>
<dt>pvRecord</dt>
<dd>This encapsulates the concept of a smart record. It can be processed.
Changes to field values can be trapped. A record can be locked.</dd>
<dt>pvDatabase<dt>
<dd>This is a database of pvRecords.
Records can be added and removed from a database.</dd>
</dl>
<dt>localChannelProvider</dt>
<dd>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</dd>
</dl>
<p><b>database</b> 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:</p>
<dl>
<dt>process</dt>
<dd>This is what makes a record <b>smart</b>.
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 <b>not</b> complete.
Instead process invokes other threads that will complete the processing at a later time.</dd>
<dt>isSynchronous</dt>
<dd>Which execution model is being implemented.</dd>
</dl>
<h3>Example PVRecord Extension</h3>
<p>Directory <b>example/record</b> has an example PVRecord implementation.
It implements a counter.
The top level structure is:</p>
<pre>
structure
long value
</pre>
<p><b>NOTE:</b> The example compiles but does not build because nothing
is implemented.</p>
<h4>exampleRecord.h</h4>
<p>This is the class description.
The example extends PVRecord.</p>
<pre>
class ExampleRecord :
public virtual PVRecord
{
public:
POINTER_DEFINITIONS(ExampleRecord);
static PVRecordPtr create(epics::pvData::String const &amp; recordName);
virtual ~ExampleRecord();
virtual bool isSynchronous();
virtual void process(
epics::pvDatabase::RecordProcessRequesterPtr const &amp;processRequester);
private:
ExampleRecord(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure,
epics::pvData::PVLongPtr const &amp;pvValue);
epics::pvData::PVLongPtr pvValue;
};
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>This is example specific. See the implemention for details.</dd>
<dt>~ExampleRecord<dt>
<dd>The destructor must be declared virtual.</dd>
<dt>isSynchronous<dt>
<dd>The implementation must say if process is synchronous or asynchronous.</dd>
<dt>process<dt>
<dd><b>The</b> implementation.</dd>
<dt>ExampleRecord<dt>
<dd>For the example this is private.</dd>
<dl>
<h4>exampleRecord.cpp</h4>
<p>This is the class implementation.</p>
<pre>
ExampleRecord::~ExampleRecord(){}
PVRecordPtr ExampleRecord::create(String const &amp; recordName)
{
String properties;
PVStructurePtr pvStructure = getStandardPVField()-&gt;scalar(pvLong,properties);
PVLongPtr pvValue = pvStructure-&gt;getLongField("value");
PVRecordPtr pvRecord(new ExampleRecord(recordName,pvStructure,pvValue));
return pvRecord;
}
ExampleRecord::ExampleRecord(
String const &amp; recordName,
PVStructurePtr const &amp; pvStructure,
PVLongPtr const &amp;pvValue)
: PVRecord(recordName,pvStructure),
pvValue(pvValue)
{}
bool ExampleRecord::isSynchronous() {return true;}
void ExampleRecord::process(
RecordProcessRequesterPtr const &amp;processRequester)
{
pvValue-&gt;put(pvValue-&gt;get() + 1);
processRequester-&gt;recordProcessResult(Status::Ok);
processRequester-&gt;recordProcessComplete();
}
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>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.
</dd>
<dt>~ExampleRecord<dt>
<dd>Does not have to do anything because of shared pointers.</dd>
<dt>ExampleRecord<dt>
<dd>Calls the base class constructor and sets pvValue.</dd>
<dt>isSynchronous<dt>
<dd>The example is synchronous.</dd>
<dt>process<dt>
<dd>Gets the curent value, increments it, and puts the new value.
It than calls two processRequester callbacks.</dd>
<dl>
<h4>exampleRecordMain.cpp</h4>
<p>This is a main for creating and running the example.</p>
<pre>
int main(int argc,char *argv[])
{
String recordName("exampleRecord");
PVRecordPtr pvRecord = ExampleRecord::create(recordName);
PVDatabasePtr pvDatabase = PVDatabase::getMaster();
pvDatabase-&gt;addRecord(pvRecord);
cout &lt;&lt; recordName &lt;&lt; "\n";
string str;
while(true) {
cout &lt;&lt; "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
return 0;
}
</pre>
<p>The main program creates an example record and adds it to the database.
It then runs until the process is stopped by typing <b>exit</b>.
<p>Until the process is stopped,
pvAccess clients can put and get the value field.
For example</p>
<pre>
pvget exampleRecord
pvput exampleRecord 5
</pre>
<p>Will both work.</p>
<h3>Phased Development</h3>
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
<dl>
<dt>pvRecord</d>
<dd>Wrapper on PVStructure that implements methods required by Local Channel Provider.</dd>
<dt>pvDatabase</d>
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
<dt>Local Channel Provider</dt>
<dd>These two features will be the first phase.
But only synchronous record processing will be supported.</dd>
</dl>
<p>Future phases of pvDatabaseCPP should include:</p>
<dl>
<dt>Install</dt>
<dd>This provides on-line add and delete.</dd>
<dt>Field support</dt>
<dd>Add ability to optionally add support to fields.
In addition some of the basic support defined in pvIOCJava will also be implemented.</dd>
<dt>XML parser</dt>
<dd>This provides the ability to create record instances without writing any code.</dd>
</dl>
<p>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.</p>
<h3>Features Required for localChannelProvider</h3>
<dl>
<dt>pvCopy</dt>
<dd>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.<dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to fields of a record.</dd>
<dt>PVRecord and PVDatabase</dt>
<dd>Defined below.</dd>
<dt>local ChannelProvider</dt>
<dd>This is the pvAccess package in pvIOCJava.
The localChannelProvider will access data from PVRecords.
It will implement all channel methods except channelRPC.</dd>
</dl>
<h3>Minumum Features Required for pvRecord</h3>
<p>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</p>
<dl>
<dt>PVDatabase</dt>
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
<dt>PVRecord</dt>
<dd>This, and a set of related interfaces, provide the following:
<dl>
<dt>PVStructure</dt>
<dd>PVRecord is a wrapper on a top level pvStructure.</dd>
<dt>Record locking</dt>
<dd>A record can be locked and unlocked.
A record must be locked whenever data in the pvStructure is accessed.</dd>
<dt>Trapping data changes</dt>
<dd>A client can request to be notified when data in the pvStructure is modified.
It can do this on a field by field basis.</dd>
</dl>
</dd>
</dl>
<p>The following sections provide a first attempt to describe the classes required for the first
phase.</p>
<p>The last section gives a brief overview of the features provided by pvIOCJava.</p>
<h2>database</h2>
<p>The classes in <b>pvDatabase.h</b> 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:</p>
<dl>
<dt>PVRecord</dt>
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
<dt>PVRecordField</dt>
<dt>PVRecordStructure</dt>
<dd>These <b>wrap</b> PVField and PVStructure so that pvCopy and monitor can be implemented.</dd>
<dt>PVListener</dt>
<dd>This is implemented by anything that wants to trap calls to the PVRecord::message.</dd>
<dt>RecordProcessRequester</dt>
<dd>This is implemented by anything that calls PVRecord::queueProcessRequest.</dd>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
<dt>PVDatabase</dt>
<dd>This is a database of PVRecords.</dd>
</dl>
<p>Each class is described in a separate subsection.</p>
<h3>class PVRecord</h3>
<pre>
class PVRecord
{
public:
POINTER_DEFINITIONS(PVRecord);
PVRecord(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void process(
RecordProcessRequesterPtr const &amp;recordProcessRequester) = 0;
virtual bool isSynchronous() = 0;
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField);
void lock();
void unlock();
void registerClient(PVRecordClientPtr const &amp; pvRecordClient);
void unregisterClient(PVRecordClientPtr const &amp; pvRecordClient);
void detachClients();
void beginGroupPut();
void endGroupPut();
void registerListener(PVListenerPtr const &amp; pvListener);
void unregisterListener(PVListenerPtr const &amp; pvListener);
void removeEveryListener();
epics::pvData::Status processRequest();
void queueProcessRequest(
RecordProcessRequesterPtr const &amp;recordProcessRequester);
void addRequester(epics::pvData::RequesterPtr const &amp; requester);
void removeRequester(epics::pvData::RequesterPtr const &amp; requester);
void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
epics::pvData::String toString();
epics::pvData::String toString(int indentLevel);
};
</pre>
<p>The methods are:</h3>
<dl>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>~PVRecord</dt>
<dd>The desctructor which must be virtual. A derived class must also have
a virtual destructor.</dd>
<dt>process</dt>
<dd>Pure virtual method. Derived classes must implement this method.</dd>
<dt>isSynchronous</dt>
<dd>Pure virtual method. Derived classes must implement this method.</dd>
<dt>getRecordName</dt>
<dd>Return the recordName.</dd>
<dt>getPVRecordStructure</dt>
<dd>Get the top level PVStructure.</dd>
<dt>findPVRecordField</dt>
<dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd>
<dt>lock</dt>
<dt>unlock</dt>
<dd>Lock and Unlock the record.
Any code accessing the data in the record or calling other PVRecord methods
must have the record locked.</dd>
<dt>registerClient</dt>
<dd>Every client that accesses the record must call this so that the client can be notified when the record is deleted.</dd>
<dt>unregisterClient</dt>
<dd>Client is no longer accessing the record.</dd>
<dt>detachClients</dt>
<dd>Ask all clients to detach from the record</dd>
<dt>beginGroupPut</dt>
<dd>Begin a group of puts. This results in all registered PVListeners being called</dd>
<dt>endGroupPut</dt>
<dd>End a group of puts. This results in all registered PVListeners being called.</dd>
<dt>registerListener</dt>
<dd>Register a PVListener. This must be called before calling pvRecordField.addListener.</dd>
<dt>unregisterListener</dt>
<dd>Unregister a listener. The listener will also be removed from all fields to which it is attached.</dd>
<dt>removeEveryListener</dt>
<dd>This must be called by any code that is deleting or changing the structure of a record.</dd>
<dt>processRequest</dt>
<dd>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.</dd>
<dt>queueProcessRequest</dt>
<dd>Queue a process request.</dd>
<dt>addRequester</dt>
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
</dl>
<h3>class PVRecordField</h3>
<pre>
class PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordField);
PVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField,
PVRecordPtr const &amp; pvRecord);
virtual ~PVRecordField();
PVRecordStructurePtr getParent();
epics::pvData::PVFieldPtr getPVField();
epics::pvData::String getFullFieldName();
epics::pvData::String getFullName();
PVRecordPtr getPVRecord();
bool addListener(PVListenerPtr const &amp; pvListener);
void removeListener(PVListenerPtr const &amp; pvListener);
void postPut();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
};
</pre>
<p>When PVRecord is created it creates a PVRecordField for every field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordField</dt>
<dd>The constructor.</dd>
<dt>~PVRecordField</dt>
<dd>The destructor.</dd>
<dt>getParent</dt>
<dd>Get the parent PVRecordStructure for this field.</dd>
<dt>getPVField</dt>
<dd>Get the PVField associated with this PVRecordField.</dd>
<dt>getFullFieldName</dt>
<dd>This gets the full name of the field, i.e. field,field,..</dd>
<dt>getFullName</dt>
<dd>This gets recordName plus the full name of the field, i.e. recordName.field,field,..</dd>
<dt>getPVRecord</dt>
<dd>Returns the PVRecord to which this field belongs.</dd>
<dt>addListener</dt>
<dd>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.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl>
<h3>class PVRecordStructure</h3>
<pre>
class PVRecordStructure : public PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordStructure);
PVRecordStructure(
epics::pvData::PVStructurePtr const &amp; pvStructure,
PVRecordFieldPtrArrayPtr const &amp; pvRecordField);
virtual ~PVRecordStructure();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
};
</pre>
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordStructure</dt>
<dd>The constructor.</dd>
<dt>~PVRecordStructure</dt>
<dd>The destructor.</dd>
<dt>getPVRecordFields</dt>
<dd>Get the PVRecordField array for the subfields</dd>
<dt>getPVStructure</dt>
<dd>Get the PVStructure for this field.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl>
<h3>class PVListener</h3>
<pre>
class PVListener {
public:
POINTER_DEFINITIONS(PVListener);
virtual ~PVListener();
virtual void dataPut(PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void unlisten(PVRecordPtr const &amp; pvRecord) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~PVListener</dt>
<dd>The destructor.</dd>
<dt>dataPut(PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
This is called if the listener has called PVRecordField::addListener for pvRecordField.</dd>
<dt>dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
Requested is the field to which the requester issued a pvField-&amp;addListener.
This is called if the listener has called PVRecordField-&amp;addListener for requested.</dd>
<dt>beginGroupPut</dt>
<dd>A related set of changes is being started.</dd>
<dt>endGroupPut</dt>
<dd>A related set of changes is done.</dd>
<dt>unlisten</dt>
<dd>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.</dd>
</dl>
<h3>class RecordProcessRequester</h3>
<pre>
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;
};
</pre>
<p>where</p>
<dl>
<dt>~RecordProcessRequester</dt>
<dd>The destructor.</dd>
<dt>becomeProcessor</dt>
<dd>Called as a result of queueRequeProcessst. The requester can the call process.</dd>
<dt>recordProcessResult</dt>
<dd>The results of record processing.
This is called with the record locked so that the process requester
can access data from the record.</dd>
<dt>recordProcessComplete</dt>
<dd>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.</dd>
</dl>
<h3>class PVRecordClient</h3>
<pre>
class PVRecordClient {
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient();
virtual void detach(PVRecordPtr const &amp; pvRecord);
};
</pre>
<p>where</p>
<dl>
<dt>~PVRecordClient</dt>
<dd>The destructor.</dd>
<dt>detach</dt>
<dd>The record is being removed from the master database,</dd>
</dl>
<h3>class PVDatabase</h3>
<pre>
class PVDatabase : virtual public epics::pvData::Requester {
public:
POINTER_DEFINITIONS(PVDatabase);
static PVDatabasePtr getMaster();
virtual ~PVDatabase();
PVRecordPtr findRecord(epics::pvData::String const&amp; recordName);
bool addRecord(PVRecordPtr const &amp; record);
bool removeRecord(PVRecordPtr const &amp; record);
private:
PVDatabase();
};
</pre>
<p>where</p>
<dl>
<dt>getMaster</dt>
<dd>Get the master database. This is the database that localChannelProvider access.</dd>
<dt>~PVDatabase</dt>
<dd>The destructor.</dd>
<dt>findRecord</dt>
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
<dt>addRecord</dt>
<dd>Add a record to the database.
If the record already exists it is not modified and false is returned.</dd>
<dt>removeRecord</dt>
<dd>Remove a record from the database.
If the record was not in the database false is returned.</dd>
</dl>
<h2>Local Channel Provider</h2>
<p>Not yet described.</p>
<p>A brief description is that it must implement the following components of pvIOCJava:</p>
<dl>
<dt>pvCopy</dt>
<dt>monitor</dt>
<dt>pvAccess</dt>
<dd>See the next section for a description</dd>
</dl>
<h2>Summary of Packages in pvIOCJAVA</h2>
<p>The following are the direct sub packages of <b>pvIOCJava/src/org/epics/pvioc</b>:</p>
<dl>
<dt>pvCopy</dt>
<dd>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.</dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to a PVRecord. It is required for pvAccess monitors.</dd>
<dt>pvAccess</dt>
<dd>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.</dd>
<dt>database</dt>
<dd>This defines and implements PVRecord, PVDatabase , and PVListener.
It supports the basic feature required the implement a local Channel Provider.</dd>
<dt>support</dt>
<dd>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.</dd>
<dt>install</dt>
<dd>This provides the ability to dynamically initialize and add new PVRecords. It also provides
the ability to dynamicall delete PVRecords.</d>
<dt>xml</dt>
<dd>This provides the ability to configure record instances without writing code.</dd>
<dt>util</dt>
<dd>This is misnamed since it is code related to scanning.</dd>
<dt>pdrv</dt>
<dd>This is portDriver, which is a proposed sucessor to the asynManager component of asynDriver.</dd>
<dt>swtshell</dt>
<dd>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.
</dd>
<dt>caV3</dt>
<dd>This has two components:
<dl>
<dt>ClientFactory</dt>
<dd>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.</dd>
<dt>ServerFactory</dt>
<dd>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.</dd>
</dl>
</dd>
<dt>v3a</dt>
<dd>I do not know what this is.</dd>
</dl>
<p>In addition there is one class file <b>JavaIOC.java</b>.
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.</p>
</div>
</body>
</html>

View File

@ -1,874 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title>pvDatabaseCPP</title>
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/base.css" />
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
<style type="text/css">
/*<![CDATA[*/
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
table { margin-left: auto; margin-right: auto }
.diagram { text-align: center; margin: 2.5em 0 }
span.opt { color: grey }
span.nterm { font-style:italic }
span.term { font-family:courier }
span.user { font-family:courier }
span.user:before { content:"<" }
span.user:after { content:">" }
.nonnorm { font-style:italic }
p.ed { color: #AA0000 }
span.ed { color: #AA0000 }
p.ed.priv { display: inline; }
span.ed.priv { display: inline; }
/*]]>*/</style>
<!-- Script that generates the Table of Contents -->
<script type="text/javascript"
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
</script>
</head>
<body>
<div class="head">
<h1>pvDatabaseCPP</h1>
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 11-Dec-2012</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>This version:</dt>
<dd><a
href="pvDatabaseCPP_20121211.html">pvDatabaseCPP20121211.html</a>
</dd>
<dt>Previous version:</dt>
<dd><a
href="pvDatabaseCPP_20121127.html">pvDatabaseCPP_20121127.html</a>
</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
</dl>
<p class="copyright">This product is made available subject to acceptance of the <a
href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p>
<hr />
</div>
<h2 class="nocount">Abstract</h2>
<p>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.</p>
<p>EPICS version 4 is a set of related products in the EPICS
V4 control system programming environment:<br />
<a href="http://epics-pvdata.sourceforge.net/relatedDocumentsV4.html">relatedDocumentsV4.html</a>
</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 11-Dec-2012 version of the definition of pvDatabaseCPP.
</p>
<p>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.</p>
<div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
</div>
<div id="contents" class="contents">
<h2>Introduction</h2>
<h3>Overview</h3>
<p>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.
</p>
<p>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.</p>
<p>This document describes components that provides the following features:
<dl>
<dt>database<dt>
<dd>This encapsulates the concept of a database of memory resident smart records.
The two main components are:
<dl>
<dt>pvRecord</dt>
<dd>This encapsulates the concept of a smart record. It can be processed.
Changes to field values can be trapped. A record can be locked.</dd>
<dt>pvDatabase<dt>
<dd>This is a database of pvRecords.
Records can be added and removed from a database.</dd>
</dl>
<dt>localChannelProvider</dt>
<dd>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</dd>
</dl>
<p><b>database</b> 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:</p>
<dl>
<dt>process</dt>
<dd>This is what makes a record <b>smart</b>.
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 <b>not</b> complete.
Instead process invokes other threads that will complete the processing at a later time.</dd>
<dt>isSynchronous</dt>
<dd>Which execution model is being implemented.</dd>
</dl>
<h3>Example PVRecord Extension</h3>
<p>Directory <b>example/record</b> has an example PVRecord implementation.
It implements a counter.
The top level structure is:</p>
<pre>
structure
long value
</pre>
<p><b>NOTE:</b> The example compiles but does not build because nothing
is implemented.</p>
<h4>exampleRecord.h</h4>
<p>This is the class description.
The example extends PVRecord.</p>
<pre>
class ExampleRecord :
public virtual PVRecord
{
public:
POINTER_DEFINITIONS(ExampleRecord);
static PVRecordPtr create(epics::pvData::String const &amp; recordName);
virtual ~ExampleRecord();
virtual bool isSynchronous();
virtual void process(
epics::pvDatabase::RecordProcessRequesterPtr const &amp;processRequester);
private:
ExampleRecord(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure,
epics::pvData::PVLongPtr const &amp;pvValue);
epics::pvData::PVLongPtr pvValue;
};
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>This is example specific. See the implemention for details.</dd>
<dt>~ExampleRecord<dt>
<dd>The destructor must be declared virtual.</dd>
<dt>isSynchronous<dt>
<dd>The implementation must say if process is synchronous or asynchronous.</dd>
<dt>process<dt>
<dd><b>The</b> implementation.</dd>
<dt>ExampleRecord<dt>
<dd>For the example this is private.</dd>
<dl>
<h4>exampleRecord.cpp</h4>
<p>This is the class implementation.</p>
<pre>
ExampleRecord::~ExampleRecord(){}
PVRecordPtr ExampleRecord::create(String const &amp; recordName)
{
String properties;
PVStructurePtr pvStructure = getStandardPVField()-&gt;scalar(pvLong,properties);
PVLongPtr pvValue = pvStructure-&gt;getLongField("value");
PVRecordPtr pvRecord(new ExampleRecord(recordName,pvStructure,pvValue));
return pvRecord;
}
ExampleRecord::ExampleRecord(
String const &amp; recordName,
PVStructurePtr const &amp; pvStructure,
PVLongPtr const &amp;pvValue)
: PVRecord(recordName,pvStructure),
pvValue(pvValue)
{}
bool ExampleRecord::isSynchronous() {return true;}
void ExampleRecord::process(
RecordProcessRequesterPtr const &amp;processRequester,bool alreadyLocked)
{
if(!alreadyLocked) lock();
pvValue-&gt;put(pvValue-&gt;get() + 1);
processRequester-&gt;recordProcessResult(Status::Ok);
unlock();
processRequester-&gt;recordProcessComplete();
dequeueProcessRequest(processRequester);
}
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>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.
</dd>
<dt>~ExampleRecord<dt>
<dd>Does not have to do anything because of shared pointers.</dd>
<dt>ExampleRecord<dt>
<dd>Calls the base class constructor and sets pvValue.</dd>
<dt>isSynchronous<dt>
<dd>The example is synchronous.</dd>
<dt>process<dt>
<dd>Gets the curent value, increments it, and puts the new value.
It than calls two processRequester callbacks.</dd>
<dl>
<h4>exampleRecordMain.cpp</h4>
<p>This is a main for creating and running the example.</p>
<pre>
int main(int argc,char *argv[])
{
String recordName("exampleRecord");
PVRecordPtr pvRecord = ExampleRecord::create(recordName);
PVDatabasePtr pvDatabase = PVDatabase::getMaster();
pvDatabase-&gt;addRecord(pvRecord);
cout &lt;&lt; recordName &lt;&lt; "\n";
string str;
while(true) {
cout &lt;&lt; "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
return 0;
}
</pre>
<p>The main program creates an example record and adds it to the database.
It then runs until the process is stopped by typing <b>exit</b>.
<p>Until the process is stopped,
pvAccess clients can put and get the value field.
For example</p>
<pre>
pvget exampleRecord
pvput exampleRecord 5
</pre>
<p>Will both work.</p>
<h3>Phased Development</h3>
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
<dl>
<dt>pvRecord</d>
<dd>Wrapper on PVStructure that implements methods required by Local Channel Provider.</dd>
<dt>pvDatabase</d>
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
<dt>Local Channel Provider</dt>
<dd>These two features will be the first phase.
But only synchronous record processing will be supported.</dd>
</dl>
<p>Future phases of pvDatabaseCPP should include:</p>
<dl>
<dt>Install</dt>
<dd>This provides on-line add and delete.</dd>
<dt>Field support</dt>
<dd>Add ability to optionally add support to fields.
In addition some of the basic support defined in pvIOCJava will also be implemented.</dd>
<dt>XML parser</dt>
<dd>This provides the ability to create record instances without writing any code.</dd>
</dl>
<p>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.</p>
<h3>Features Required for localChannelProvider</h3>
<dl>
<dt>pvCopy</dt>
<dd>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.<dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to fields of a record.</dd>
<dt>PVRecord and PVDatabase</dt>
<dd>Defined below.</dd>
<dt>local ChannelProvider</dt>
<dd>This is the pvAccess package in pvIOCJava.
The localChannelProvider will access data from PVRecords.
It will implement all channel methods except channelRPC.</dd>
</dl>
<h3>Minumum Features Required for pvRecord</h3>
<p>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</p>
<dl>
<dt>PVDatabase</dt>
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
<dt>PVRecord</dt>
<dd>This, and a set of related interfaces, provide the following:
<dl>
<dt>PVStructure</dt>
<dd>PVRecord is a wrapper on a top level pvStructure.</dd>
<dt>Record locking</dt>
<dd>A record can be locked and unlocked.
A record must be locked whenever data in the pvStructure is accessed.</dd>
<dt>Trapping data changes</dt>
<dd>A client can request to be notified when data in the pvStructure is modified.
It can do this on a field by field basis.</dd>
</dl>
</dd>
</dl>
<p>The following sections provide a first attempt to describe the classes required for the first
phase.</p>
<p>The last section gives a brief overview of the features provided by pvIOCJava.</p>
<h2>database</h2>
<p>The classes in <b>pvDatabase.h</b> implement a database of memory resident
smart records.
It describes the following classes:</p>
<dl>
<dt>PVRecord</dt>
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
<dt>PVRecordField</dt>
<dt>PVRecordStructure</dt>
<dd>These <b>wrap</b> PVField and PVStructure so that pvCopy and monitor
can be implemented.</dd>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
<dt>PVListener</dt>
<dd>This is implemented by anything that wants to trap calls to the PVRecord::message.</dd>
<dt>RecordProcessRequester</dt>
<dd>This is implemented by anything that calls PVRecord::queueProcessRequest.</dd>
<dt>RecordPutRequester</dt>
<dd>This is implemented by anything that calls PVRecord::queuePutRequest.</dd>
<dt>PVDatabase</dt>
<dd>This is a database of PVRecords.</dd>
</dl>
<p>Each class is described in a separate subsection.</p>
<h3>C++ namespace and typedefs</h3>
<pre>
namespace epics { namespace pvDatabase {
class PVRecord;
typedef std::tr1::shared_ptr&lt;PVRecord&gt; PVRecordPtr;
class PVRecordField;
typedef std::tr1::shared_ptr&lt;PVRecordField&gt; PVRecordFieldPtr;
typedef std::vector&lt;PVRecordFieldPtr&gt; PVRecordFieldPtrArray;
typedef std::tr1::shared_ptr&lt;PVRecordFieldPtrArray&gt; PVRecordFieldPtrArrayPtr;
class PVRecordStructure;
typedef std::tr1::shared_ptr&lt;PVRecordStructure&gt; PVRecordStructurePtr;
class PVRecordClient;
typedef std::tr1::shared_ptr&lt;PVRecordClient&gt; PVRecordClientPtr;
class PVListener;
typedef std::tr1::shared_ptr&lt;PVListener&gt; PVListenerPtr;
class RecordProcessRequester;
typedef std::tr1::shared_ptr&lt;RecordProcessRequester&gt; RecordProcessRequesterPtr;
class RecordPutRequester;
typedef std::tr1::shared_ptr&lt;RecordPutRequester&gt; RecordPutRequesterPtr;
class PVDatabase;
typedef std::tr1::shared_ptr&lt;PVDatabase&gt; PVDatabasePtr;
</pre>
<h3>class PVRecord</h3>
<p><b>NOTES:</b>
<ul>
<li>This section uses the name record instead of "an instance of PVRecord".</li>
<li>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.</li>
</ul>
<hr>PVRecord Methods</h4>
<pre>
class PVRecord
public epics::pvData::Requester,
public std::tr1::enable_shared_from_this&lt;PVRecord&gt;
{
public:
POINTER_DEFINITIONS(PVRecord);
PVRecord(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void process(
RecordProcessRequesterPtr const &amp;recordProcessRequester,
bool alreadyLocked) = 0;
virtual bool isSynchronous() = 0;
virtual bool requestImmediatePut(epics::pvData::PVFieldPtr const &amp;pvField);
virtual void immediatePutDone();
virtual void destroy();
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField);
bool addRequester(epics::pvData::RequesterPtr const &amp; requester);
bool removeRequester(epics::pvData::RequesterPtr const &amp; requester);
void lock();
void unlock();
bool tryLock();
void lockOtherRecord(PVRecordPtr const &amp; otherRecord);
void addPVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void removePVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void detachClients();
void beginGroupPut();
void endGroupPut();
void queueProcessRequest(
RecordProcessRequesterPtr const &amp;recordProcessRequester);
void dequeueProcessRequest(
RecordProcessRequesterPtr const &amp;recordProcessRequester);
void queuePutRequest(
RecordPutRequesterPtr const &amp;recordPutRequester);
void putDone(
RecordPutRequesterPtr const &amp;recordPutRequester);
virtual epics::pvData::String getRequesterName();
void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void message(
PVRecordFieldPtr const &amp; pvRecordField,
epics::pvData::String const &amp; 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();
};
</pre>
<p>The methods are:</h3>
<dl>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>~PVRecord</dt>
<dd>The destructor which must be virtual. A derived class must also have
a virtual destructor.</dd>
<dt>process</dt>
<dd>Pure virtual method.
<p>Derived classes must implement this method.</p>
<p>A client <b>must</b> only call this method when
<b>RecordProcessRequester::becomeProcessor</b> is called as a result
of a <b>queueProcessRequest</b>.
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.</p>
<p>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.</p>
<p>When processing is done the record calls two client callbacks:</p>
<dl>
<dt>RecordProcessRequester::recordProcessResult</dt>
<dd>This is called with the record still locked.
The clients can get data from the record.</dd>
<dt>RecordProcessRequester::recordProcessComplete</dt>
<dd>This is called with the record unlocked.
The client can no longer access the record.</dd>
</dl>
</dd>
<dt>isSynchronous</dt>
<dd>Pure virtual method. Derived classes must implement this method.</dd>
<dt>requestImmediatePut</dt>
<dd>This is a virtual method.
<p> 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 <b>stop</b></p>
<p>The default always returns <b>false</b>.</p>
<p>A record implementation can override the default and return <b>true</b>.
In it does requestImmediatePut it returns with the record locked.</p>
<p>The client can change the value of the associated field and then call
<b>immediatePutDone</b></p>
</dd>
<dt>immediatePutDone</dt>
<dd>This is a virtual method.
<p>The default does nothing.</p>
<p>Must be called by client as a result of a call to <b>requestImmediatePut</b>
that returns <b>true</b>.</p>
</dd>
<dt>destroy</dt>
<dd>This is a virtual method.
<p>The default does nothing.</p>
</dd>
<dt>getRecordName</dt>
<dd>Return the recordName.</dd>
<dt>getPVRecordStructure</dt>
<dd>Get the top level PVStructure.</dd>
<dt>findPVRecordField</dt>
<dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd>
<dt>addRequester</dt>
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>lock</dt>
<dt>unlock</dt>
<dd>Lock and Unlock the record.
Any code accessing the data in the record or calling other PVRecord methods
must have the record locked.</dd>
<dt>tryLock</dt>
<dd>If <b>true</b> then just like <b>lock</b>.
If <b>false</b>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.
</dd>
<dt>lockOtherRecord</dt>
<dd>A client that holds the lock for one record can lock one other record.
A client <b>must</b> not call this if the client already has the lock for
more then one record.
</dd>
<dt>addPVRecordClient</dt>
<dd>Every client that accesses the record must call this so that the client can be notified when the record is deleted.</dd>
<dt>removePVRecordClient</dt>
<dd>Client is no longer accessing the record.</dd>
<dt>detachClients</dt>
<dd>Ask all clients to detach from the record</dd>
<dt>addListener</dt>
<dd>Add a PVListener. This must be called before calling pvRecordField.addListener.</dd>
<dt>removeListener</dt>
<dd>Removes a listener. The listener will also be removed from all fields to which it is attached.</dd>
<dt>beginGroupPut</dt>
<dd>Begin a group of puts.
This results in all registered PVListeners being called</dd>
<dt>endGroupPut</dt>
<dd>End a group of puts.
This results in all registered PVListeners being called.</dd>
<dt>queueProcessRequest</dt>
<dd>Queue a process request.</dd>
<dt>dequeueProcessRequest</dt>
<dd>This <b>must</b> be called by record implementation after it has
completed a process request.
</dd>
<dt>queuePutRequest</dt>
<dd>Queue a put request.
<p>This is for code that wants to change data in a record without processing.
If <b>RecordPutRequester::requestResult</b> is called with result <b>true</b>
then the record is locked and the client can make changes.
When done the client <b>must</b> call <b>putDone</b></p>
</dd>
<dt>putDone</dt>
<dd>Called by <b>RecordPutRequester</b> after changing values in record.
This method unlocks the record</dd>
<dt>getRequesterName</dt>
<dd>virtual method of <b>Requester</b>
</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
<dt>init</dt>
<dd>This method <b>must</b> be called by derived class
<b>after</b> class is completely constructed.</dd>
</dl>
<h3>class PVRecordField</h3>
<pre>
class PVRecordField {
public virtual epics::pvData::PostHandler,
public std::tr1::enable_shared_from_this&lt;PVRecordField&gt;
public:
POINTER_DEFINITIONS(PVRecordField);
PVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField,
PVRecordStructurePtr const &amp;parent,
PVRecordPtr const &amp; pvRecord);
virtual ~PVRecordField();
PVRecordStructurePtr getParent();
epics::pvData::PVFieldPtr getPVField();
epics::pvData::String getFullFieldName();
epics::pvData::String getFullName();
PVRecordPtr getPVRecord();
bool addListener(PVListenerPtr const &amp; pvListener);
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
};
</pre>
<p>When PVRecord is created it creates a PVRecordField for every field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordField</dt>
<dd>The constructor.</dd>
<dt>~PVRecordField</dt>
<dd>The destructor.</dd>
<dt>getParent</dt>
<dd>Get the parent PVRecordStructure for this field.</dd>
<dt>getPVField</dt>
<dd>Get the PVField associated with this PVRecordField.</dd>
<dt>getFullFieldName</dt>
<dd>This gets the full name of the field, i.e. field,field,..</dd>
<dt>getFullName</dt>
<dd>This gets recordName plus the full name of the field, i.e. recordName.field,field,..</dd>
<dt>getPVRecord</dt>
<dd>Returns the PVRecord to which this field belongs.</dd>
<dt>addListener</dt>
<dd>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.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl>
<h3>class PVRecordStructure</h3>
<pre>
class PVRecordStructure : public PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordStructure);
PVRecordStructure(
epics::pvData::PVStructurePtr const &amp; pvStructure,
PVRecordFieldPtrArrayPtr const &amp; pvRecordField);
virtual ~PVRecordStructure();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
};
</pre>
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordStructure</dt>
<dd>The constructor.</dd>
<dt>~PVRecordStructure</dt>
<dd>The destructor.</dd>
<dt>getPVRecordFields</dt>
<dd>Get the PVRecordField array for the subfields</dd>
<dt>getPVStructure</dt>
<dd>Get the PVStructure for this field.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<h3>class PVRecordClient</h3>
<pre>
class PVRecordClient {
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient();
virtual void detach(PVRecordPtr const &amp; pvRecord);
};
</pre>
<p>where</p>
<dl>
<dt>~PVRecordClient</dt>
<dd>The destructor.</dd>
<dt>detach</dt>
<dd>The record is being removed from the master database,</dd>
</dl>
</dl>
<h3>class PVListener</h3>
<pre>
class PVListener {
virtual public PVRecordClient
public:
POINTER_DEFINITIONS(PVListener);
virtual ~PVListener();
virtual void dataPut(PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~PVListener</dt>
<dd>The destructor.</dd>
<dt>dataPut(PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
This is called if the listener has called PVRecordField::addListener for pvRecordField.</dd>
<dt>dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
Requested is the field to which the requester issued a pvField-&amp;addListener.
This is called if the listener has called PVRecordField-&amp;addListener for requested.</dd>
<dt>beginGroupPut</dt>
<dd>A related set of changes is being started.</dd>
<dt>endGroupPut</dt>
<dd>A related set of changes is done.</dd>
</dl>
<h3>class RecordProcessRequester</h3>
<pre>
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;
};
</pre>
<p>where</p>
<dl>
<dt>~RecordProcessRequester</dt>
<dd>The destructor.</dd>
<dt>recordDestroyed</dt>
<dd>Record is being destroyed.</dd>
<dt>becomeProcessor</dt>
<dd>Called as a result of queueRequeProcessst. The requester can the call process.</dd>
<dt>recordProcessResult</dt>
<dd>The results of record processing.
This is called with the record locked so that the process requester
can access data from the record.</dd>
<dt>recordProcessComplete</dt>
<dd>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.</dd>
</dl>
<h3>class RecordPutRequester</h3>
<pre>
class RecordPutRequester :
virtual public PVRecordClient
{
public:
POINTER_DEFINITIONS(RecordPutRequester);
virtual ~RecordPutRequester();
virtual void requestResult(bool result) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~RecordPutRequester</dt>
<dd>The destructor.</dd>
<dt>requestResult</dt>
<dd>Result of a call to queuePutRequest. If <b>requestResult</b> is <b>false</b>
then the caller can not access the record.
If <b>requestResult</b> is <b>true</b>
then the record is locked and the caller can get and put data in the record.
When done the caller must call <b>PVRecord::putDone</b>, which will unlock the
record.
</dd>
</dl>
<h3>class PVDatabase</h3>
<pre>
class PVDatabase : virtual public epics::pvData::Requester {
public:
POINTER_DEFINITIONS(PVDatabase);
static PVDatabasePtr getMaster();
virtual ~PVDatabase();
PVRecordPtr findRecord(epics::pvData::String const&amp; recordName);
bool addRecord(PVRecordPtr const &amp; record);
bool removeRecord(PVRecordPtr const &amp; record);
private:
PVDatabase();
};
</pre>
<p>where</p>
<dl>
<dt>getMaster</dt>
<dd>Get the master database. This is the database that localChannelProvider access.</dd>
<dt>~PVDatabase</dt>
<dd>The destructor.</dd>
<dt>findRecord</dt>
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
<dt>addRecord</dt>
<dd>Add a record to the database.
If the record already exists it is not modified and false is returned.</dd>
<dt>removeRecord</dt>
<dd>Remove a record from the database.
If the record was not in the database false is returned.</dd>
</dl>
<h2>Local Channel Provider</h2>
<p>Not yet described.</p>
<p>A brief description is that it must implement the following components of pvIOCJava:</p>
<dl>
<dt>pvCopy</dt>
<dt>monitor</dt>
<dt>pvAccess</dt>
<dd>See the next section for a description</dd>
</dl>
<h2>Summary of Packages in pvIOCJAVA</h2>
<p>The following are the direct sub packages of <b>pvIOCJava/src/org/epics/pvioc</b>:</p>
<dl>
<dt>pvCopy</dt>
<dd>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.</dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to a PVRecord. It is required for pvAccess monitors.</dd>
<dt>pvAccess</dt>
<dd>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.</dd>
<dt>database</dt>
<dd>This defines and implements PVRecord, PVDatabase , and PVListener.
It supports the basic feature required the implement a local Channel Provider.</dd>
<dt>support</dt>
<dd>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.</dd>
<dt>install</dt>
<dd>This provides the ability to dynamically initialize and add new PVRecords. It also provides
the ability to dynamicall delete PVRecords.</d>
<dt>xml</dt>
<dd>This provides the ability to configure record instances without writing code.</dd>
<dt>util</dt>
<dd>This is misnamed since it is code related to scanning.</dd>
<dt>pdrv</dt>
<dd>This is portDriver, which is a proposed sucessor to the asynManager component of asynDriver.</dd>
<dt>swtshell</dt>
<dd>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.
</dd>
<dt>caV3</dt>
<dd>This has two components:
<dl>
<dt>ClientFactory</dt>
<dd>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.</dd>
<dt>ServerFactory</dt>
<dd>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.</dd>
</dl>
</dd>
<dt>v3a</dt>
<dd>I do not know what this is.</dd>
</dl>
<p>In addition there is one class file <b>JavaIOC.java</b>.
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.</p>
</div>
</body>
</html>

View File

@ -1,870 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title>pvDatabaseCPP</title>
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/base.css" />
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
<style type="text/css">
/*<![CDATA[*/
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
table { margin-left: auto; margin-right: auto }
.diagram { text-align: center; margin: 2.5em 0 }
span.opt { color: grey }
span.nterm { font-style:italic }
span.term { font-family:courier }
span.user { font-family:courier }
span.user:before { content:"<" }
span.user:after { content:">" }
.nonnorm { font-style:italic }
p.ed { color: #AA0000 }
span.ed { color: #AA0000 }
p.ed.priv { display: inline; }
span.ed.priv { display: inline; }
/*]]>*/</style>
<!-- Script that generates the Table of Contents -->
<script type="text/javascript"
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
</script>
</head>
<body>
<div class="head">
<h1>pvDatabaseCPP</h1>
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 17-Apr-2013</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>This version:</dt>
<dd><a
href="pvDatabaseCPP_20130417.html">pvDatabaseCPP20130417.html</a>
</dd>
<dt>Previous version:</dt>
<dd><a
href="pvDatabaseCPP_20121211.html">pvDatabaseCPP20121211.html</a>
</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
</dl>
<p class="copyright">This product is made available subject to acceptance of the <a
href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p>
<hr />
</div>
<h2 class="nocount">Abstract</h2>
<p>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.
</p>
<p>EPICS version 4 is a set of related products in the EPICS
V4 control system programming environment:<br />
<a href="http://epics-pvdata.sourceforge.net/relatedDocumentsV4.html">relatedDocumentsV4.html</a>
</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 17-Apr-2013 version of the definition of pvDatabaseCPP.
</p>
<p>The following Channel methods are implemented and working: getField,i
channelProcess, channelGet, channelPut, and channelPutGet.
But lots of work remains:</p>
<dl>
<dt>Other Channel Methods</dt>
<dd>Monitor is next.</dd>
<dt>Memory leaks at exit</dt>
<dd>May need help from Matej.</dd>
<dt>Scalar Arrays</dt>
<dd>Have not been tested. Share has not been implemented.</dd>
<dt>Structure Arrays</dt>
<dd>Has not been implemented</dd>
<dt>Testing</dt>
<dd>Needs lots more testing</dd>
</dl>
<div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
</div>
<div id="contents" class="contents">
<h2>Introduction</h2>
<h3>Overview</h3>
<p>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.</p>
<p>This document describes components that provides the following features:
<dl>
<dt>database<dt>
<dd>This encapsulates the concept of a database of memory resident smart records.
The two main components are:
<dl>
<dt>pvRecord</dt>
<dd>This encapsulates the concept of a smart record. It can be processed.
Changes to field values can be trapped. A record can be locked.</dd>
<dt>pvDatabase<dt>
<dd>This is a database of pvRecords.
Records can be added and removed from a database.</dd>
</dl>
<dt>pvAccess</dt>
<dd>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</dd>
</dl>
<p>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:</p>
<dl>
<dt>init</dt>
<dd>This is a method for initializing the support.
It returns true if successful and false otherwise.
</dd>
<dt>process</dt>
<dd>This is what makes a record smart.
</dd>
</dl>
<h3>Relationship with pvIOCJava.</h3>
<p>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.</p>
<p>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.</p>
</p>
<h3>Example PVRecord Extension</h3>
<p>The example implements a simple counter.
The example can be run on linux as follows:</p>
<pre>
mrk&gt; pwd
/home/hg/pvDatabaseCPP
mrk&gt; bin/linux-x86_64/exampleCounter
</pre>
<p>The example consists of two components:</p>
<dl>
<dt>ExampleCounter.h</dt>
<dd>The source code for the counter.</dd>
<dt>exampleCounterMain.cpp</dt>
<dd>A main program that runs the example so that it can be accessed
by a pvAccess client.</dd>
</dl>
<h4>ExampleCounter.h</h4>
<p>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</p>
<pre>
class ExampleCounter;
typedef std::tr1::shared_ptr&lt;ExampleCounter&gt; ExampleCounterPtr;
class ExampleCounter :
public PVRecord
{
public:
POINTER_DEFINITIONS(ExampleCounter);
static ExampleCounterPtr create(
epics::pvData::String const &amp; recordName);
virtual ~ExampleCounter();
virtual void destroy();
virtual bool init();
virtual void process();
private:
ExampleCounter(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
epics::pvData::PVLongPtr pvValue;
epics::pvData::PVTimeStamp pvTimeStamp;
epics::pvData::TimeStamp timeStamp;
};
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>This is example specific but each support could provide
a similar static method.
</dd>
<dt>~ExampleCounter<dt>
<dd>The destructor must be declared virtual.</dd>
<dt><destroy</dt>
<dd>Called when the record is being destroyed.
This must call the base class destroy method.
<dt>init<dt>
<dd>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.</dd>
<dt>process<dt>
<dd>
This again is a virtual method of PVRecord.
</dd>
<dt>ExampleCounter<dt>
<dd>For the example this is private.</dd>
<dt>pvValue</dt>
<dd>This is the field of the top level structure that process
accesses.
</dd>
<dl>
<p>The implementation of create method is:</p>
<pre>
ExampleCounterPtr ExampleCounter::create(
epics::pvData::String const &amp; recordName)
{
epics::pvData::PVStructurePtr pvStructure =
epics::pvData::getStandardPVField()-&gt;scalar(
epics::pvData::pvDouble,"timeStamp,alarm"");
ExampleCounterPtr pvRecord(
new ExampleCounter(recordName,pvStructure));
if(!pvRecord-&gt;init()) pvRecord.reset();
return pvRecord;
}
</pre>
This:
<ul>
<li>Creates the top level structure.</li>
<li>Creates a ExampleCounterPtr via the constructor.</li>
<li>Calls init and if it fails resets the shared pointer.</li>
<li>Returns the shared pointer to the newly created record.</li>
</ul>
<p>The private constructor method is:</p>
<pre>
ExampleCounter::ExampleCounter(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure)
: PVRecord(recordName,pvStructure)
{
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
}
</pre>
The example is very simple. It just calls the base class constructor.
<p>The destructor and destroy methods are:</p>
<pre>
ExampleCounter::~ExampleCounter()
{
destroy();
}
void ExampleCounter::destroy()
{
PVRecord::destroy();
}
</pre>
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.
<p>The implementation of init is:</p>
<pre>
bool ExampleCounter::init()
{
initPVRecord();
epics::pvData::PVFieldPtr pvField;
pvValue = getPVStructure()-&gt;getLongField("value");
if(pvValue.get()==NULL) return false;
return true;
}
</pre>
This:
<ul>
<li>Calls initRecord which is implemented by the base class.
It MUST be called.</li>
<li>Calls getLongField to get the interface to the value field,
which must be a scalar with type long.</li>
<li>If a long value field was not found it returns false.</li>
<li>Returns true</li>
</ul>
<p>The implementation of process is:</p>
<pre>
void ExampleCounter::process()
{
pvValue-&gt;put(pvValue-&gt;get() + 1.0);
timeStamp.getCurrent();
pvTimeStamp.set(timeStamp);
}
</pre>
It adds 1.0 to the current value.
It then sets the timeStamp to the current time.
<h4>exampleCounterMain.cpp</h4>
<p>This is in test/server.
The main program is:</p>
<pre>
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-&gt;addRecord(pvRecord);
cout &lt;&lt; "result of addRecord " &lt;&lt; recordName &lt;&lt; " " &lt;&lt; result &lt;&lt; endl;
pvRecord.reset();
cout &lt;&lt; "exampleServer\n";
string str;
while(true) {
cout &lt;&lt; "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
return 0;
}
</pre>
This:
<ul>
<li>Gets a pointer to the master database.</li>
<li>Creates the local Channel Provider. This starts the pvAccess server.</li>
<li>Creates a ExampleCounter record with the name exampleCounter
</li>
<li>Prints exampleCounter on standard out.</li>
<li>Runs forever until the user types exit on standard in.</li>
</ul>
<h3>Phased Development</h3>
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
<dl>
<dt>pvRecord</d>
<dd>Wrapper on PVStructure that implements methods required by Local Channel Provider.</dd>
<dt>pvDatabase</d>
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
<dt>Local Channel Provider</dt>
<dd>Complete implementation of ChannelProvider and Channel.
This means that pvCopy and monitor are also implemented.</dd>
</dl>
<p>Future phases of pvDatabaseCPP might include:</p>
<dl>
<dt>Install</dt>
<dd>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.
</dd>
<dt>Field support</dt>
<dd>Add the ability to optionally add support to fields.
In addition some of the basic support defined in pvIOCJava could also be implemented.</dd>
<dt>XML parser</dt>
<dd>This provides the ability to create record instances without writing any code.</dd>
</dl>
<p>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.</p>
<h3>Features Required for localChannelProvider</h3>
<dl>
<dt>pvCopy</dt>
<dd>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.<dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to fields of a record.</dd>
<dt>PVRecord and PVDatabase</dt>
<dd>Defined below.</dd>
<dt>The localChannelProvider itself</dt>
<dd>This is the pvAccess package in pvIOCJava.
The localChannelProvider will access data from PVRecords.
It will implement all channel methods.</dd>
</dl>
<h3>Minumum Features Required for pvRecord</h3>
<p>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</p>
<dl>
<dt>PVDatabase</dt>
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
<dt>PVRecord</dt>
<dd>This, and a set of related interfaces, provides the following:
<dl>
<dt>Access to top level PVStructure</dt>
<dd>PVRecord is a wrapper on a top level pvStructure.</dd>
<dt>Record locking</dt>
<dd>A record can be locked and unlocked.
A record must be locked whenever data in the pvStructure is accessed.</dd>
<dt>Trapping data changes</dt>
<dd>A client can request to be notified when data in the pvStructure is modified.
It can do this on a field by field basis.</dd>
</dl>
</dd>
</dl>
<p>The following sections describes the classes required for the first phase.</p>
<h2>database</h2>
<p>This directory has the following files:</p>
<dl>
<dt>pvDatabase.h</dt>
<dd>
This is what is described in this section.
</dd>
<dt>pvDatabase.cpp</dt>
<dd>
The implementation of PVDatabase.
</dd>
<dt>pvRecord.cpp</dt>
<dd>
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.
</dd>
<dt>exampleCounter.h</dt>
<dd>
This was described in the introduction.
</dd>
<dt>powerSupplyRecordTest.h</dt>
<dd>
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.
</dd>
</dl>
<p>The classes in pvDatabase.h describe a database of memory resident
smart records.
It describes the following classes:</p>
<dl>
<dt>PVRecord</dt>
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
<dt>PVRecordField</dt>
<dt>PVRecordStructure</dt>
<dd>These wrap PVField and PVStructure so that pvCopy and monitor
can be implemented.</dd>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
<dt>PVListener</dt>
<dd>This is implemented by anything that wants to trap calls to PVRecord::message.</dd>
<dt>PVDatabase</dt>
<dd>This is a database of PVRecords.</dd>
</dl>
<p>Each class is described in a separate subsection.</p>
<h3>C++ namespace and typedefs</h3>
<pre>
namespace epics { namespace pvDatabase {
class PVRecord;
typedef std::tr1::shared_ptr&lt;PVRecord&gt; PVRecordPtr;
typedef std::map&lt;epics::pvData::String,PVRecordPtr&gt; PVRecordMap;
class PVRecordField;
typedef std::tr1::shared_ptr&lt;PVRecordField&gt; PVRecordFieldPtr;
typedef std::vector&lt;PVRecordFieldPtr&gt; PVRecordFieldPtrArray;
typedef std::tr1::shared_ptr&lt;PVRecordFieldPtrArray&gt; PVRecordFieldPtrArrayPtr;
class PVRecordStructure;
typedef std::tr1::shared_ptr&lt;PVRecordStructure&gt; PVRecordStructurePtr;
class PVRecordClient;
typedef std::tr1::shared_ptr&lt;PVRecordClient&gt; PVRecordClientPtr;
class PVListener;
typedef std::tr1::shared_ptr&lt;PVListener&gt; PVListenerPtr;
class RecordProcessRequester;
typedef std::tr1::shared_ptr&lt;RecordProcessRequester&gt; RecordProcessRequesterPtr;
class RecordPutRequester;
typedef std::tr1::shared_ptr&lt;RecordPutRequester&gt; RecordPutRequesterPtr;
class PVDatabase;
typedef std::tr1::shared_ptr&lt;PVDatabase&gt; PVDatabasePtr;
</pre>
<h3>class PVRecord</h3>
<p>NOTES:
<ul>
<li>This section uses the name record instead of "an instance of PVRecord".</li>
<li>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.</li>
<li>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.
</ul>
<hr>PVRecord Methods</h4>
<pre>
class PVRecord
public epics::pvData::Requester,
public std::tr1::enable_shared_from_this&lt;PVRecord&gt;
{
public:
POINTER_DEFINITIONS(PVRecord);
virtual bool init() {initPVRecord(); return true;}
virtual void process() {}
static PVRecordPtr create(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void destroy();
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField);
bool addRequester(epics::pvData::RequesterPtr const &amp; requester);
bool removeRequester(epics::pvData::RequesterPtr const &amp; requester);
inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
void lock();
void unlock();
bool tryLock();
void lockOtherRecord(PVRecordPtr const &amp; otherRecord);
bool addPVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
bool removePVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void detachClients();
bool addListener(PVListenerPtr const &amp; pvListener);
bool removeListener(PVListenerPtr const &amp; pvListener);
void beginGroupPut();
void endGroupPut();
epics::pvData::String getRequesterName() {return getRecordName();}
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void message(
PVRecordFieldPtr const &amp; pvRecordField,
epics::pvData::String const &amp; 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 &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
void initPVRecord();
epics::pvData::PVStructurePtr getPVStructure();
PVRecordPtr getPtrSelf()
{
return shared_from_this();
}
private:
...
}
</pre>
<p>The methods are:</h3>
<dl>
<dt>init</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
This method Must call initPVRecord.</p>
</dd>
<dt>process</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
The base implementation does nothing.</p>
</dd>
<dt>create</dt>
<dd>Static method to create dumb records, i.e. records with a process method
that does nothing.</dd>
<dt>~PVRecord</dt>
<dd>The destructor which must be virtual. A derived class must also have
a virtual destructor.</dd>
<dt>destroy</dt>
<dd>This is a virtual method.
</dd>
<dt>getRecordName</dt>
<dd>Return the recordName.</dd>
<dt>getPVRecordStructure</dt>
<dd>Get the top level PVStructure.</dd>
<dt>findPVRecordField</dt>
<dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd>
<dt>addRequester</dt>
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>lock_guard</dt>
<dd>This is an inline method that locks the record. The record will automatically
be unlocked when control leaves the block that has the call.
<dt>lock</dt>
<dt>unlock</dt>
<dd>Lock and Unlock the record.
Any code accessing the data in the record or calling other PVRecord methods
must have the record locked.</dd>
<dt>tryLock</dt>
<dd>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.
</dd>
<dt>lockOtherRecord</dt>
<dd>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.
</dd>
<dt>addPVRecordClient</dt>
<dd>Every client that accesses the record must call this so that the client can be notified when the record is deleted.</dd>
<dt>removePVRecordClient</dt>
<dd>Client is no longer accessing the record.</dd>
<dt>detachClients</dt>
<dd>Ask all clients to detach from the record</dd>
<dt>addListener</dt>
<dd>Add a PVListener. This must be called before calling pvRecordField.addListener.</dd>
<dt>removeListener</dt>
<dd>Removes a listener. The listener will also be removed from all fields to which it is attached.</dd>
<dt>beginGroupPut</dt>
<dd>Begin a group of puts.
This results in all registered PVListeners being called</dd>
<dt>endGroupPut</dt>
<dd>End a group of puts.
This results in all registered PVListeners being called.</dd>
<dt>getRequesterName</dt>
<dd>virtual method of Requester
</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
<dt>toString</dt>
<dd>Just calls the top level PVStructure toString method.</dd>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>initPVRecord</dt>
<dd>This method must be called by derived class.</dd>
<dt>getPVStructure</dt>
<dd>Called by derived class.</dd>
</dl>
<h3>class PVRecordField</h3>
<pre>
class PVRecordField {
public virtual epics::pvData::PostHandler,
public std::tr1::enable_shared_from_this&lt;PVRecordField&gt;
public:
POINTER_DEFINITIONS(PVRecordField);
PVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField,
PVRecordStructurePtr const &amp;parent,
PVRecordPtr const &amp; 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 &amp; pvListener);
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
protected:
PVRecordFieldPtr getPtrSelf()
{
return shared_from_this();
}
virtual void init();
private:
...
};
</pre>
<p>When PVRecord is created it creates a PVRecordField for every field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordField</dt>
<dd>The constructor.</dd>
<dt>~PVRecordField</dt>
<dd>The destructor.</dd>
<dt>destroy</dt>
<dd>Called by PVRecordStructure when it's destroy method is called.</dd>
<dt>getParent</dt>
<dd>Get the parent PVRecordStructure for this field.</dd>
<dt>getPVField</dt>
<dd>Get the PVField associated with this PVRecordField.</dd>
<dt>getFullFieldName</dt>
<dd>This gets the full name of the field, i.e. field,field,..</dd>
<dt>getFullName</dt>
<dd>This gets recordName plus the full name of the field, i.e. recordName.field,field,..</dd>
<dt>getPVRecord</dt>
<dd>Returns the PVRecord to which this field belongs.</dd>
<dt>addListener</dt>
<dd>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.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl>
<h3>class PVRecordStructure</h3>
<pre>
class PVRecordStructure : public PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordStructure);
PVRecordStructure(
epics::pvData::PVStructurePtr const &amp; pvStructure,
PVRecordFieldPtrArrayPtr const &amp; pvRecordField);
virtual ~PVRecordStructure();
virtual void destroy();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
protected:
virtual void init();
private:
...
};
</pre>
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordStructure</dt>
<dd>The constructor.</dd>
<dt>~PVRecordStructure</dt>
<dd>The destructor.</dd>
<dt>getPVRecordFields</dt>
<dd>Get the PVRecordField array for the subfields</dd>
<dt>getPVStructure</dt>
<dd>Get the PVStructure for this field.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<h3>class PVRecordClient</h3>
<pre>
class PVRecordClient {
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient();
virtual void detach(PVRecordPtr const &amp; pvRecord);
};
</pre>
<p>where</p>
<dl>
<dt>~PVRecordClient</dt>
<dd>The destructor.</dd>
<dt>detach</dt>
<dd>The record is being removed from the master database,</dd>
</dl>
</dl>
<h3>class PVListener</h3>
<pre>
class PVListener {
virtual public PVRecordClient
public:
POINTER_DEFINITIONS(PVListener);
virtual ~PVListener();
virtual void dataPut(PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~PVListener</dt>
<dd>The destructor.</dd>
<dt>dataPut(PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
This is called if the listener has called PVRecordField::addListener for pvRecordField.</dd>
<dt>dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
Requested is the field to which the requester issued a pvField-&amp;addListener.
This is called if the listener has called PVRecordField-&amp;addListener for requested.</dd>
<dt>beginGroupPut</dt>
<dd>A related set of changes is being started.</dd>
<dt>endGroupPut</dt>
<dd>A related set of changes is done.</dd>
</dl>
<h3>class PVDatabase</h3>
<pre>
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&amp; recordName);
bool addRecord(PVRecordPtr const &amp; record);
bool removeRecord(PVRecordPtr const &amp; record);
virtual epics::pvData::String getRequesterName();
virtual void message(
epics::pvData::String const &amp;message,
epics::pvData::MessageType messageType);
private:
PVDatabase();
};
</pre>
<p>where</p>
<dl>
<dt>getMaster</dt>
<dd>Get the master database. This is the database that localChannelProvider access.</dd>
<dt>~PVDatabase</dt>
<dd>The destructor.</dd>
<dt>destroy</dt>
<dd>This is called by remote channelAccess when process exits.
This destroys and removes all records in the PVDatabase.</dd>
<dt>findRecord</dt>
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
<dt>addRecord</dt>
<dd>Add a record to the database.
If the record already exists it is not modified and false is returned.</dd>
<dt>removeRecord</dt>
<dd>Remove a record from the database.
If the record was not in the database false is returned.</dd>
<dt>getRequesterName</dt>
<dd>Virtual method of Requester</dd>
<dt>message</dt>
<dd>Virtual message of Requester.</dd>
</dl>
<h2>pvAccess</h2>
<p>Not yet described.
It is only of interest to someone who wants to understand how it works.
</p>
<p>A brief description is that it implements the following components of pvIOCJava:</p>
<dl>
<dt>pvCopy</dt>
<dt>monitor</dt>
<dt>local ChannelProvider and Channel</dt>
</dl>
</div>
</body>
</html>

View File

@ -1,876 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title>pvDatabaseCPP</title>
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/base.css" />
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
<style type="text/css">
/*<![CDATA[*/
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
table { margin-left: auto; margin-right: auto }
.diagram { text-align: center; margin: 2.5em 0 }
span.opt { color: grey }
span.nterm { font-style:italic }
span.term { font-family:courier }
span.user { font-family:courier }
span.user:before { content:"<" }
span.user:after { content:">" }
.nonnorm { font-style:italic }
p.ed { color: #AA0000 }
span.ed { color: #AA0000 }
p.ed.priv { display: inline; }
span.ed.priv { display: inline; }
/*]]>*/</style>
<!-- Script that generates the Table of Contents -->
<script type="text/javascript"
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
</script>
</head>
<body>
<div class="head">
<h1>pvDatabaseCPP</h1>
<!-- Maturity: Working Draft or Request for Comments, or Recommendation, and date. -->
<h2 class="nocount">EPICS v4 Working Group, Working Draft, 16-May-2013</h2>
<dl>
<dt>Latest version:</dt>
<dd><a
href="pvDatabaseCPP.html">pvDatabaseCPP.html</a>
</dd>
<dt>This version:</dt>
<dd><a
href="pvDatabaseCPP_20130516.html">pvDatabaseCPP20130516.html</a>
</dd>
<dt>Previous version:</dt>
<dd><a
href="pvDatabaseCPP_20130417.html">pvDatabaseCPP20130417.html</a>
</dd>
<dt>Editors:</dt>
<dd>Marty Kraimer, BNL</dd>
</dl>
<p class="copyright">This product is made available subject to acceptance of the <a
href="http://epics-pvdata.sourceforge.net/LICENSE.html">EPICS open source license.</a></p>
<hr />
</div>
<h2 class="nocount">Abstract</h2>
<p>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.
</p>
<p>EPICS version 4 is a set of related products in the EPICS
V4 control system programming environment:<br />
<a href="http://epics-pvdata.sourceforge.net/relatedDocumentsV4.html">relatedDocumentsV4.html</a>
</p>
<h2 class="nocount">Status of this Document</h2>
<p>This is the 16-May-2013 version of the definition of pvDatabaseCPP.
</p>
<p>The following Channel methods are implemented and working: getField,
channelProcess, channelGet, channelPut, channelPutGet, and Monitor.
But lots of work remains:</p>
<dl>
<dt>Other Channel Methods</dt>
<dd>ChannelArray is next.</dd>
<dt>Monitor Algorithms</dt>
<dd>Monitor algorithms have no been implemented.
Thus all monitors are onPut.</dd>
<dt>Lifecycle problems</dt>
<dd>Problems when channel clients disconnect.
May need help from Matej</dd>
<dt>Memory leaks at exit</dt>
<dd>May need help from Matej.</dd>
<dt>Scalar Arrays</dt>
<dd>Have not been tested. Share has not been implemented.</dd>
<dt>Structure Arrays</dt>
<dd>Has not been implemented</dd>
<dt>Testing</dt>
<dd>Needs lots more testing</dd>
</dl>
<div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
</div>
<div id="contents" class="contents">
<h2>Introduction</h2>
<h3>Overview</h3>
<p>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.</p>
<p>This document describes components that provides the following features:
<dl>
<dt>database<dt>
<dd>This encapsulates the concept of a database of memory resident smart records.
The two main components are:
<dl>
<dt>pvRecord</dt>
<dd>This encapsulates the concept of a smart record. It can be processed.
Changes to field values can be trapped. A record can be locked.</dd>
<dt>pvDatabase<dt>
<dd>This is a database of pvRecords.
Records can be added and removed from a database.</dd>
</dl>
<dt>pvAccess</dt>
<dd>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</dd>
</dl>
<p>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:</p>
<dl>
<dt>init</dt>
<dd>This is a method for initializing the support.
It returns true if successful and false otherwise.
</dd>
<dt>process</dt>
<dd>This is what makes a record smart.
</dd>
</dl>
<h3>Relationship with pvIOCJava.</h3>
<p>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.</p>
<p>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.</p>
</p>
<h3>Example PVRecord Extension</h3>
<p>The example implements a simple counter.
The example can be run on linux as follows:</p>
<pre>
mrk&gt; pwd
/home/hg/pvDatabaseCPP
mrk&gt; bin/linux-x86_64/exampleCounter
</pre>
<p>The example consists of two components:</p>
<dl>
<dt>ExampleCounter.h</dt>
<dd>The source code for the counter.</dd>
<dt>exampleCounterMain.cpp</dt>
<dd>A main program that runs the example so that it can be accessed
by a pvAccess client.</dd>
</dl>
<h4>ExampleCounter.h</h4>
<p>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</p>
<pre>
class ExampleCounter;
typedef std::tr1::shared_ptr&lt;ExampleCounter&gt; ExampleCounterPtr;
class ExampleCounter :
public PVRecord
{
public:
POINTER_DEFINITIONS(ExampleCounter);
static ExampleCounterPtr create(
epics::pvData::String const &amp; recordName);
virtual ~ExampleCounter();
virtual void destroy();
virtual bool init();
virtual void process();
private:
ExampleCounter(epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
epics::pvData::PVLongPtr pvValue;
epics::pvData::PVTimeStamp pvTimeStamp;
epics::pvData::TimeStamp timeStamp;
};
</pre>
<p>where</p>
<dl>
<dt>create<dt>
<dd>This is example specific but each support could provide
a similar static method.
</dd>
<dt>~ExampleCounter<dt>
<dd>The destructor must be declared virtual.</dd>
<dt><destroy</dt>
<dd>Called when the record is being destroyed.
This must call the base class destroy method.
<dt>init<dt>
<dd>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.</dd>
<dt>process<dt>
<dd>
This again is a virtual method of PVRecord.
</dd>
<dt>ExampleCounter<dt>
<dd>For the example this is private.</dd>
<dt>pvValue</dt>
<dd>This is the field of the top level structure that process
accesses.
</dd>
<dl>
<p>The implementation of create method is:</p>
<pre>
ExampleCounterPtr ExampleCounter::create(
epics::pvData::String const &amp; recordName)
{
epics::pvData::PVStructurePtr pvStructure =
epics::pvData::getStandardPVField()-&gt;scalar(
epics::pvData::pvDouble,"timeStamp,alarm"");
ExampleCounterPtr pvRecord(
new ExampleCounter(recordName,pvStructure));
if(!pvRecord-&gt;init()) pvRecord.reset();
return pvRecord;
}
</pre>
This:
<ul>
<li>Creates the top level structure.</li>
<li>Creates a ExampleCounterPtr via the constructor.</li>
<li>Calls init and if it fails resets the shared pointer.</li>
<li>Returns the shared pointer to the newly created record.</li>
</ul>
<p>The private constructor method is:</p>
<pre>
ExampleCounter::ExampleCounter(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure)
: PVRecord(recordName,pvStructure)
{
pvTimeStamp.attach(pvStructure->getSubField("timeStamp"));
}
</pre>
The example is very simple. It just calls the base class constructor.
<p>The destructor and destroy methods are:</p>
<pre>
ExampleCounter::~ExampleCounter()
{
destroy();
}
void ExampleCounter::destroy()
{
PVRecord::destroy();
}
</pre>
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.
<p>The implementation of init is:</p>
<pre>
bool ExampleCounter::init()
{
initPVRecord();
epics::pvData::PVFieldPtr pvField;
pvValue = getPVStructure()-&gt;getLongField("value");
if(pvValue.get()==NULL) return false;
return true;
}
</pre>
This:
<ul>
<li>Calls initRecord which is implemented by the base class.
It MUST be called.</li>
<li>Calls getLongField to get the interface to the value field,
which must be a scalar with type long.</li>
<li>If a long value field was not found it returns false.</li>
<li>Returns true</li>
</ul>
<p>The implementation of process is:</p>
<pre>
void ExampleCounter::process()
{
pvValue-&gt;put(pvValue-&gt;get() + 1.0);
timeStamp.getCurrent();
pvTimeStamp.set(timeStamp);
}
</pre>
It adds 1.0 to the current value.
It then sets the timeStamp to the current time.
<h4>exampleCounterMain.cpp</h4>
<p>This is in test/server.
The main program is:</p>
<pre>
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-&gt;addRecord(pvRecord);
cout &lt;&lt; "result of addRecord " &lt;&lt; recordName &lt;&lt; " " &lt;&lt; result &lt;&lt; endl;
pvRecord.reset();
cout &lt;&lt; "exampleServer\n";
string str;
while(true) {
cout &lt;&lt; "Type exit to stop: \n";
getline(cin,str);
if(str.compare("exit")==0) break;
}
return 0;
}
</pre>
This:
<ul>
<li>Gets a pointer to the master database.</li>
<li>Creates the local Channel Provider. This starts the pvAccess server.</li>
<li>Creates a ExampleCounter record with the name exampleCounter
</li>
<li>Prints exampleCounter on standard out.</li>
<li>Runs forever until the user types exit on standard in.</li>
</ul>
<h3>Phased Development</h3>
<p>This documentation describes the first phase of a phased implementation of pvDatabaseCPP:</pp>
<dl>
<dt>pvRecord</d>
<dd>Wrapper on PVStructure that implements methods required by Local Channel Provider.</dd>
<dt>pvDatabase</d>
<dd>Database of PVRecords. Has methods find, add, and remove.</dd>
<dt>Local Channel Provider</dt>
<dd>Complete implementation of ChannelProvider and Channel.
This means that pvCopy and monitor are also implemented.</dd>
</dl>
<p>Future phases of pvDatabaseCPP might include:</p>
<dl>
<dt>Install</dt>
<dd>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.
</dd>
<dt>Field support</dt>
<dd>Add the ability to optionally add support to fields.
In addition some of the basic support defined in pvIOCJava could also be implemented.</dd>
<dt>XML parser</dt>
<dd>This provides the ability to create record instances without writing any code.</dd>
</dl>
<p>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.</p>
<h3>Features Required for localChannelProvider</h3>
<dl>
<dt>pvCopy</dt>
<dd>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.<dd>
<dt>monitor</dt>
<dd>This provides the ability to monitor changes to fields of a record.</dd>
<dt>PVRecord and PVDatabase</dt>
<dd>Defined below.</dd>
<dt>The localChannelProvider itself</dt>
<dd>This is the pvAccess package in pvIOCJava.
The localChannelProvider will access data from PVRecords.
It will implement all channel methods.</dd>
</dl>
<h3>Minumum Features Required for pvRecord</h3>
<p>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</p>
<dl>
<dt>PVDatabase</dt>
<dd>This holds a set of PVRecords. It has methods to find, add, and remove records.</dd>
<dt>PVRecord</dt>
<dd>This, and a set of related interfaces, provides the following:
<dl>
<dt>Access to top level PVStructure</dt>
<dd>PVRecord is a wrapper on a top level pvStructure.</dd>
<dt>Record locking</dt>
<dd>A record can be locked and unlocked.
A record must be locked whenever data in the pvStructure is accessed.</dd>
<dt>Trapping data changes</dt>
<dd>A client can request to be notified when data in the pvStructure is modified.
It can do this on a field by field basis.</dd>
</dl>
</dd>
</dl>
<p>The following sections describes the classes required for the first phase.</p>
<h2>database</h2>
<p>This directory has the following files:</p>
<dl>
<dt>pvDatabase.h</dt>
<dd>
This is what is described in this section.
</dd>
<dt>pvDatabase.cpp</dt>
<dd>
The implementation of PVDatabase.
</dd>
<dt>pvRecord.cpp</dt>
<dd>
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.
</dd>
<dt>exampleCounter.h</dt>
<dd>
This was described in the introduction.
</dd>
<dt>powerSupplyRecordTest.h</dt>
<dd>
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.
</dd>
</dl>
<p>The classes in pvDatabase.h describe a database of memory resident
smart records.
It describes the following classes:</p>
<dl>
<dt>PVRecord</dt>
<dd>This provides the methods required by localChannelProvider to implement Channel.</dd>
<dt>PVRecordField</dt>
<dt>PVRecordStructure</dt>
<dd>These wrap PVField and PVStructure so that pvCopy and monitor
can be implemented.</dd>
<dt>PVRecordClient</dt>
<dd>This is called by anything that acceses PVRecord.</dd>
<dt>PVListener</dt>
<dd>This is implemented by anything that wants to trap calls to PVRecord::message.</dd>
<dt>PVDatabase</dt>
<dd>This is a database of PVRecords.</dd>
</dl>
<p>Each class is described in a separate subsection.</p>
<h3>C++ namespace and typedefs</h3>
<pre>
namespace epics { namespace pvDatabase {
class PVRecord;
typedef std::tr1::shared_ptr&lt;PVRecord&gt; PVRecordPtr;
typedef std::map&lt;epics::pvData::String,PVRecordPtr&gt; PVRecordMap;
class PVRecordField;
typedef std::tr1::shared_ptr&lt;PVRecordField&gt; PVRecordFieldPtr;
typedef std::vector&lt;PVRecordFieldPtr&gt; PVRecordFieldPtrArray;
typedef std::tr1::shared_ptr&lt;PVRecordFieldPtrArray&gt; PVRecordFieldPtrArrayPtr;
class PVRecordStructure;
typedef std::tr1::shared_ptr&lt;PVRecordStructure&gt; PVRecordStructurePtr;
class PVRecordClient;
typedef std::tr1::shared_ptr&lt;PVRecordClient&gt; PVRecordClientPtr;
class PVListener;
typedef std::tr1::shared_ptr&lt;PVListener&gt; PVListenerPtr;
class RecordProcessRequester;
typedef std::tr1::shared_ptr&lt;RecordProcessRequester&gt; RecordProcessRequesterPtr;
class RecordPutRequester;
typedef std::tr1::shared_ptr&lt;RecordPutRequester&gt; RecordPutRequesterPtr;
class PVDatabase;
typedef std::tr1::shared_ptr&lt;PVDatabase&gt; PVDatabasePtr;
</pre>
<h3>class PVRecord</h3>
<p>NOTES:
<ul>
<li>This section uses the name record instead of "an instance of PVRecord".</li>
<li>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.</li>
<li>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.
</ul>
<hr>PVRecord Methods</h4>
<pre>
class PVRecord
public epics::pvData::Requester,
public std::tr1::enable_shared_from_this&lt;PVRecord&gt;
{
public:
POINTER_DEFINITIONS(PVRecord);
virtual bool init() {initPVRecord(); return true;}
virtual void process() {}
static PVRecordPtr create(
epics::pvData::String const &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
virtual ~PVRecord();
virtual void destroy();
epics::pvData::String getRecordName();
PVRecordStructurePtr getPVRecordStructure();
PVRecordFieldPtr findPVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField);
bool addRequester(epics::pvData::RequesterPtr const &amp; requester);
bool removeRequester(epics::pvData::RequesterPtr const &amp; requester);
inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
void lock();
void unlock();
bool tryLock();
void lockOtherRecord(PVRecordPtr const &amp; otherRecord);
bool addPVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
bool removePVRecordClient(PVRecordClientPtr const &amp; pvRecordClient);
void detachClients();
bool addListener(PVListenerPtr const &amp; pvListener);
bool removeListener(PVListenerPtr const &amp; pvListener);
void beginGroupPut();
void endGroupPut();
epics::pvData::String getRequesterName() {return getRecordName();}
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
void message(
PVRecordFieldPtr const &amp; pvRecordField,
epics::pvData::String const &amp; 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 &amp; recordName,
epics::pvData::PVStructurePtr const &amp; pvStructure);
void initPVRecord();
epics::pvData::PVStructurePtr getPVStructure();
PVRecordPtr getPtrSelf()
{
return shared_from_this();
}
private:
...
}
</pre>
<p>The methods are:</h3>
<dl>
<dt>init</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
This method Must call initPVRecord.</p>
</dd>
<dt>process</dt>
<dd>Virtual method.
<p>Derived classes must implement this method.
The base implementation does nothing.</p>
</dd>
<dt>create</dt>
<dd>Static method to create dumb records, i.e. records with a process method
that does nothing.</dd>
<dt>~PVRecord</dt>
<dd>The destructor which must be virtual. A derived class must also have
a virtual destructor.</dd>
<dt>destroy</dt>
<dd>This is a virtual method.
</dd>
<dt>getRecordName</dt>
<dd>Return the recordName.</dd>
<dt>getPVRecordStructure</dt>
<dd>Get the top level PVStructure.</dd>
<dt>findPVRecordField</dt>
<dd>Given a PVFieldPtr return the PVRecordFieldPtr for the field.</dd>
<dt>addRequester</dt>
<dd>Add a requester to receive messages.</dd>
<dt>removeRequester</dt>
<dd>Remove a message requester.</dd>
<dt>lock_guard</dt>
<dd>This is an inline method that locks the record. The record will automatically
be unlocked when control leaves the block that has the call.
<dt>lock</dt>
<dt>unlock</dt>
<dd>Lock and Unlock the record.
Any code accessing the data in the record or calling other PVRecord methods
must have the record locked.</dd>
<dt>tryLock</dt>
<dd>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.
</dd>
<dt>lockOtherRecord</dt>
<dd>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.
</dd>
<dt>addPVRecordClient</dt>
<dd>Every client that accesses the record must call this so that the client can be notified when the record is deleted.</dd>
<dt>removePVRecordClient</dt>
<dd>Client is no longer accessing the record.</dd>
<dt>detachClients</dt>
<dd>Ask all clients to detach from the record</dd>
<dt>addListener</dt>
<dd>Add a PVListener. This must be called before calling pvRecordField.addListener.</dd>
<dt>removeListener</dt>
<dd>Removes a listener. The listener will also be removed from all fields to which it is attached.</dd>
<dt>beginGroupPut</dt>
<dd>Begin a group of puts.
This results in all registered PVListeners being called</dd>
<dt>endGroupPut</dt>
<dd>End a group of puts.
This results in all registered PVListeners being called.</dd>
<dt>getRequesterName</dt>
<dd>virtual method of Requester
</dd>
<dt>message</dt>
<dd>Can be called by implementation code.
The message will be sent to every requester.</dd>
<dt>toString</dt>
<dd>Just calls the top level PVStructure toString method.</dd>
<dt>PVRecord</dt>
<dd>The constructor. It requires a recordName and a top level PVStructure.</dd>
<dt>initPVRecord</dt>
<dd>This method must be called by derived class.</dd>
<dt>getPVStructure</dt>
<dd>Called by derived class.</dd>
</dl>
<h3>class PVRecordField</h3>
<pre>
class PVRecordField {
public virtual epics::pvData::PostHandler,
public std::tr1::enable_shared_from_this&lt;PVRecordField&gt;
public:
POINTER_DEFINITIONS(PVRecordField);
PVRecordField(
epics::pvData::PVFieldPtr const &amp; pvField,
PVRecordStructurePtr const &amp;parent,
PVRecordPtr const &amp; 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 &amp; pvListener);
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
virtual void message(
epics::pvData::String const &amp; message,
epics::pvData::MessageType messageType);
protected:
PVRecordFieldPtr getPtrSelf()
{
return shared_from_this();
}
virtual void init();
private:
...
};
</pre>
<p>When PVRecord is created it creates a PVRecordField for every field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordField</dt>
<dd>The constructor.</dd>
<dt>~PVRecordField</dt>
<dd>The destructor.</dd>
<dt>destroy</dt>
<dd>Called by PVRecordStructure when it's destroy method is called.</dd>
<dt>getParent</dt>
<dd>Get the parent PVRecordStructure for this field.</dd>
<dt>getPVField</dt>
<dd>Get the PVField associated with this PVRecordField.</dd>
<dt>getFullFieldName</dt>
<dd>This gets the full name of the field, i.e. field,field,..</dd>
<dt>getFullName</dt>
<dd>This gets recordName plus the full name of the field, i.e. recordName.field,field,..</dd>
<dt>getPVRecord</dt>
<dd>Returns the PVRecord to which this field belongs.</dd>
<dt>addListener</dt>
<dd>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.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<dt>message</dt>
<dd>Called by implementation code. It calls PVRecord::message after prepending the full
fieldname.</dd>
</dl>
<h3>class PVRecordStructure</h3>
<pre>
class PVRecordStructure : public PVRecordField {
public:
POINTER_DEFINITIONS(PVRecordStructure);
PVRecordStructure(
epics::pvData::PVStructurePtr const &amp; pvStructure,
PVRecordFieldPtrArrayPtr const &amp; pvRecordField);
virtual ~PVRecordStructure();
virtual void destroy();
PVRecordFieldPtrArrayPtr getPVRecordFields();
epics::pvData::PVStructurePtr getPVStructure();
virtual void removeListener(PVListenerPtr const &amp; pvListener);
virtual void postPut();
protected:
virtual void init();
private:
...
};
</pre>
<p>When PVRecord is created it creates a PVRecordStructure for every structure field in the PVStructure
that holds the data. It has the following methods:
</p>
<dl>
<dt>PVRecordStructure</dt>
<dd>The constructor.</dd>
<dt>~PVRecordStructure</dt>
<dd>The destructor.</dd>
<dt>getPVRecordFields</dt>
<dd>Get the PVRecordField array for the subfields</dd>
<dt>getPVStructure</dt>
<dd>Get the PVStructure for this field.</dd>
<dt>removeListener</dt>
<dd>Remove a PVListener.</dd>
<dt>postPut</dt>
<dd>This is called by the code that implements the data interface.
It is called whenever the put method is called.</dd>
<h3>class PVRecordClient</h3>
<pre>
class PVRecordClient {
POINTER_DEFINITIONS(PVRecordClient);
virtual ~PVRecordClient();
virtual void detach(PVRecordPtr const &amp; pvRecord);
};
</pre>
<p>where</p>
<dl>
<dt>~PVRecordClient</dt>
<dd>The destructor.</dd>
<dt>detach</dt>
<dd>The record is being removed from the master database,</dd>
</dl>
</dl>
<h3>class PVListener</h3>
<pre>
class PVListener {
virtual public PVRecordClient
public:
POINTER_DEFINITIONS(PVListener);
virtual ~PVListener();
virtual void dataPut(PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField) = 0;
virtual void beginGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
virtual void endGroupPut(PVRecordPtr const &amp; pvRecord) = 0;
};
</pre>
<p>where</p>
<dl>
<dt>~PVListener</dt>
<dd>The destructor.</dd>
<dt>dataPut(PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
This is called if the listener has called PVRecordField::addListener for pvRecordField.</dd>
<dt>dataPut(
PVRecordStructurePtr const &amp;
requested,PVRecordFieldPtr const &amp; pvRecordField)</dt>
<dd>pvField has been modified.
Requested is the field to which the requester issued a pvField-&amp;addListener.
This is called if the listener has called PVRecordField-&amp;addListener for requested.</dd>
<dt>beginGroupPut</dt>
<dd>A related set of changes is being started.</dd>
<dt>endGroupPut</dt>
<dd>A related set of changes is done.</dd>
</dl>
<h3>class PVDatabase</h3>
<pre>
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&amp; recordName);
bool addRecord(PVRecordPtr const &amp; record);
bool removeRecord(PVRecordPtr const &amp; record);
virtual epics::pvData::String getRequesterName();
virtual void message(
epics::pvData::String const &amp;message,
epics::pvData::MessageType messageType);
private:
PVDatabase();
};
</pre>
<p>where</p>
<dl>
<dt>getMaster</dt>
<dd>Get the master database. This is the database that localChannelProvider access.</dd>
<dt>~PVDatabase</dt>
<dd>The destructor.</dd>
<dt>destroy</dt>
<dd>This is called by remote channelAccess when process exits.
This destroys and removes all records in the PVDatabase.</dd>
<dt>findRecord</dt>
<dd>Find a record. An empty pointer is returned if the record is not in the database.</dd>
<dt>addRecord</dt>
<dd>Add a record to the database.
If the record already exists it is not modified and false is returned.</dd>
<dt>removeRecord</dt>
<dd>Remove a record from the database.
If the record was not in the database false is returned.</dd>
<dt>getRequesterName</dt>
<dd>Virtual method of Requester</dd>
<dt>message</dt>
<dd>Virtual message of Requester.</dd>
</dl>
<h2>pvAccess</h2>
<p>Not yet described.
It is only of interest to someone who wants to understand how it works.
</p>
<p>A brief description is that it implements the following components of pvIOCJava:</p>
<dl>
<dt>pvCopy</dt>
<dt>monitor</dt>
<dt>local ChannelProvider and Channel</dt>
</dl>
</div>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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=</path/name/to/install/top>
INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv
USR_INCLUDES += -I $(INSTALL_LOCATION)/include
-include $(TOP)/../../CONFIG_SITE.local
-include $(TOP)/../configure/CONFIG_SITE.local

View File

@ -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

View File

@ -1,8 +0,0 @@
TOP=..
include $(TOP)/configure/CONFIG
TARGETS = $(CONFIG_TARGETS)
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
include $(TOP)/configure/RULES

View File

@ -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

View File

@ -1,6 +0,0 @@
# RULES
include $(CONFIG)/RULES
# Library should be rebuilt because LIBOBJS may have changed.
$(LIBNAME): ../Makefile

View File

@ -1,2 +0,0 @@
#RULES.ioc
include $(CONFIG)/RULES.ioc

View File

@ -1,2 +0,0 @@
#RULES_DIRS
include $(CONFIG)/RULES_DIRS

View File

@ -1,3 +0,0 @@
#RULES_TOP
include $(CONFIG)/RULES_TOP

View File

@ -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 <anyname>.db template is not named <anyname>*.template add
# <anyname>_TEMPLATE = <templatename>
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@ -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")
}

View File

@ -1,20 +0,0 @@
record(calc, "${name}")
{
field(DESC, "Counter")
field(SCAN,"1 second")
field(CALC, "(A<B)?(A+C):D")
field(INPA, "${name} NPP NMS")
field(INPB, "9")
field(INPC, "1")
field(INPD, "0")
field(EGU, "Counts")
field(HOPR, "10")
field(HIHI, "8")
field(HIGH, "6")
field(LOW, "4")
field(LOLO, "2")
field(HHSV, "MAJOR")
field(HSV, "MINOR")
field(LSV, "MINOR")
field(LLSV, "MAJOR")
}

View File

@ -1,3 +0,0 @@
record(ao, "$(name)")
{
}

View File

@ -1,14 +0,0 @@
record(mbbo, "$(name)")
{
field(NOBT,"2")
field(ZRVL,"0")
field(ONVL,"1")
field(TWVL,"2")
field(THVL,"3")
field(ZRST,"zero")
field(ONST,"one")
field(TWST,"two")
field(THST,"three")
field(TWSV,"MINOR")
field(THSV,"MAJOR")
}

View File

@ -1,16 +0,0 @@
record($(type), "$(name)")
{
field(EGU, "Counts")
field(HOPR, "10")
field(LOPR, "0")
field(DRVH, "9")
field(DRVL, "0")
field(HIHI, "8")
field(HIGH, "6")
field(LOW, "4")
field(LOLO, "2")
field(HHSV, "MAJOR")
field(HSV, "MINOR")
field(LSV, "MINOR")
field(LLSV, "MAJOR")
}

View File

@ -1,17 +0,0 @@
record($(type), "$(name)")
{
field(PREC, "1")
field(EGU, "Counts")
field(HOPR, "10")
field(LOPR, "0")
field(DRVH, "9.9")
field(DRVL, "-0.1")
field(HIHI, "8")
field(HIGH, "6")
field(LOW, "4")
field(LOLO, "2")
field(HHSV, "MAJOR")
field(HSV, "MINOR")
field(LSV, "MINOR")
field(LLSV, "MAJOR")
}

View File

@ -1,3 +0,0 @@
record(stringout, "$(name)")
{
}

View File

@ -1,5 +0,0 @@
record(waveform, "${name}")
{
field(NELM,"5")
field(FTVL,"STRING")
}

View File

@ -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

View File

@ -1,43 +0,0 @@
TOP=../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================
#==================================================
# Build an IOC support library
#
DBD += exampleDatabase.dbd
#=============================
# build an ioc application
PROD_IOC += exampleDatabase
# <name>_registerRecordDeviceDriver.cpp will be created from <name>.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

View File

@ -1,6 +0,0 @@
include "base.dbd"
include "PVAServerRegister.dbd"
include "registerChannelProviderLocal.dbd"
include "dbPv.dbd"
include "powerSupplyRegister.dbd"
registrar("exampleDatabaseRegister")

View File

@ -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 <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#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);
}

View File

@ -1,7 +0,0 @@
TOP = ..
include $(TOP)/configure/CONFIG
DIRS += $(wildcard *ioc*)
DIRS += $(wildcard as*)
DIRS += $(wildcard example*)
include $(CONFIG)/RULES_DIRS

View File

@ -1,5 +0,0 @@
TOP = ../..
include $(TOP)/configure/CONFIG
ARCH = $(EPICS_HOST_ARCH)
TARGETS = envPaths
include $(TOP)/configure/RULES.ioc

View File

@ -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

View File

@ -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

View File

@ -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 <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <vector>
#include <iostream>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/channelProviderLocal.h>
#include <pv/traceRecord.h>
#include <pv/powerSupply.h>
#define epicsExportSharedSymbols
#include <pv/exampleDatabase.h>
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;
}
}

View File

@ -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 <pv/pvDatabase.h>
#ifdef exampledatabaseEpicsExportSharedSymbols
# define epicsExportSharedSymbols
# undef exampledatabaseEpicsExportSharedSymbols
#endif
#include <shareLib.h>
namespace epics { namespace pvDatabase {
class epicsShareClass ExampleDatabase{
public:
static void create();
};
}}
#endif /* EXAMPLEDATABASE_H */

View File

@ -1 +0,0 @@
registrar("exampleDatabaseRegister")

View File

@ -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 <iostream>
#include <pv/channelProviderLocal.h>
#include <pv/exampleDatabase.h>
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;
}

View File

@ -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 <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <cantProceed.h>
#include <epicsStdio.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <iocsh.h>
#include <pv/pvIntrospect.h>
#include <pv/pvData.h>
#include <pv/pvAccess.h>
#include <pv/pvDatabase.h>
#include <epicsExport.h>
#include <pv/exampleDatabase.h>
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);
}

View File

@ -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

View File

@ -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

View File

@ -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=</path/name/to/install/top>
# 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 = </IOC/path/to/application/top>
INSTALL_INCLUDE = $(INSTALL_LOCATION)/include/pv
USR_INCLUDES += -I $(INSTALL_LOCATION)/include
-include $(TOP)/../../CONFIG_SITE.local
-include $(TOP)/../configure/CONFIG_SITE.local

View File

@ -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

View File

@ -1,8 +0,0 @@
TOP=..
include $(TOP)/configure/CONFIG
TARGETS = $(CONFIG_TARGETS)
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
include $(TOP)/configure/RULES

View File

@ -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

View File

@ -1,6 +0,0 @@
# RULES
include $(CONFIG)/RULES
# Library should be rebuilt because LIBOBJS may have changed.
$(LIBNAME): ../Makefile

View File

@ -1,2 +0,0 @@
#RULES.ioc
include $(CONFIG)/RULES.ioc

View File

@ -1,2 +0,0 @@
#RULES_DIRS
include $(CONFIG)/RULES_DIRS

View File

@ -1,3 +0,0 @@
#RULES_TOP
include $(CONFIG)/RULES_TOP

View File

@ -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 <anyname>.db template is not named <anyname>*.template add
# <anyname>_TEMPLATE = <templatename>
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@ -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")
}

View File

@ -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

View File

@ -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
# <name>_registerRecordDeviceDriver.cpp will be created from <name>.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

View File

@ -1,5 +0,0 @@
include "base.dbd"
include "PVAServerRegister.dbd"
include "PVAClientRegister.dbd"
include "registerChannelProviderLocal.dbd"
registrar("exampleLinkRegister")

View File

@ -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 <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#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);
}

View File

@ -1,7 +0,0 @@
TOP = ..
include $(TOP)/configure/CONFIG
DIRS += $(wildcard *ioc*)
DIRS += $(wildcard as*)
DIRS += $(wildcard example*)
include $(CONFIG)/RULES_DIRS

View File

@ -1,5 +0,0 @@
TOP = ../..
include $(TOP)/configure/CONFIG
ARCH = $(EPICS_HOST_ARCH)
TARGETS = envPaths
include $(TOP)/configure/RULES.ioc

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More