From 90a96f4ee428491a1258a9c89e031d4c2d724afd Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Fri, 2 Oct 2015 08:48:30 -0400 Subject: [PATCH] update documentation; remove recordList --- README.md | 2 +- configure/ExampleRELEASE.local | 10 +- documentation/RELEASE_NOTES.html | 6 + documentation/RELEASE_NOTES.md | 11 + documentation/TODO.html | 5 - documentation/TODO.md | 9 - documentation/pvDatabaseCPP.html | 72 +- documentation/pvDatabaseCPP_20151002.html | 2052 +++++++++++++++++++ exampleDatabase/src/exampleDatabase.cpp | 9 - examplePowerSupply/src/powerSupplyMain.cpp | 6 - exampleServer/src/exampleServerMain.cpp | 8 +- exampleServer/src/exampleServerRegister.cpp | 9 - src/special/Makefile | 2 - src/special/recordList.cpp | 98 - src/special/recordList.h | 72 - 15 files changed, 2082 insertions(+), 289 deletions(-) create mode 100644 documentation/pvDatabaseCPP_20151002.html delete mode 100644 src/special/recordList.cpp delete mode 100644 src/special/recordList.h diff --git a/README.md b/README.md index 90d4679..b8ac347 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,6 @@ For example: Status ------ -* The API is for release 4.5.0-pre1 +* The API is for EPICS Version 4 release 4.5.0 diff --git a/configure/ExampleRELEASE.local b/configure/ExampleRELEASE.local index 7e5e5d7..1912f46 100644 --- a/configure/ExampleRELEASE.local +++ b/configure/ExampleRELEASE.local @@ -2,7 +2,9 @@ TEMPLATE_TOP=$(EPICS_BASE)/templates/makeBaseApp/top EPICS_BASE=/home/install/epics/base -PVCOMMON=/home/hg/pvCommonCPP -PVDATA=/home/hg/pvDataCPP -PVACCESS=/home/hg/pvAccessCPP -PVASRV=/home/hg/pvaSrv +V4BASE=/home/epicsv4 +PVCOMMON=${V4BASE}/pvCommonCPP +PVDATA=${V4BASE}/pvDataCPP +NORMATIVETYPES=${V4BASE}/normativeTypesCPP +PVACCESS=${V4BASE}/pvAccessCPP +PVASRV=${V4BASE}/pvaSrv diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 72bc56d..40c32bd 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -1,3 +1,9 @@ +

EPICS V4 release 4.5

+

This releaase is one component of EPICS V4 release 4.5.

+

The main change since release 4.0 is:

+

Release 4.0 IN DEVELOPMENT

The main changes since release 3.0.2 are:

@@ -1489,9 +1430,6 @@ or via PVAccess.

An array record that is an instance of a record with a process method that does nothing. It can be tested like exampleDouble. In addition channelArray can also be used.
-
laptoprecordListPGRPC
-
Implements the record expected by swtshell channelList. - It can also be used via channelPutGet.
traceRecordPGRPC
This can be used via channelPutGet to set the trace level of another record.
diff --git a/documentation/pvDatabaseCPP_20151002.html b/documentation/pvDatabaseCPP_20151002.html new file mode 100644 index 0000000..c151993 --- /dev/null +++ b/documentation/pvDatabaseCPP_20151002.html @@ -0,0 +1,2052 @@ + + + + + + pvDatabaseCPP + + + + + + + + + +
+

pvDatabaseCPP

+ + +

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

+
+
Latest version:
+
pvDatabaseCPP.html +
+
This version:
+
pvDatabaseCPP20151002.html +
+
Previous version:
+
pvDatabaseCPP20140811.html +
+
Editors:
+
Marty Kraimer, BNL
+
+ + +
+ +

Abstract

+ +

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

+ +

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

+ + +

Status of this Document

+ +

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

+

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

+
+ +
+

Table of Contents

+
+
+ + +

Introduction

+

Overview

+

The main purpose of this project to make it easier to implement services that are accessed via pvAccess. +This project supplies a complete implementation of the server side of pvAccess. +All that a service has to provide is a top level PVStructure and a process method. +A service can be run as a main process or can be part of a V3 IOC. +Thus services can be developed that interact with V3 records, asynDriver, +areaDetector, etc. +

+ +

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

+ +

This document describes components that provide the following features:

+
+
database
+
This encapsulates the concept of a database of memory resident smart records. + The two main components are: +
+
pvRecord
+
This encapsulates the concept of a smart record. It can be processed. + Changes to field values can be trapped. A record can be locked.
+
pvDatabase
+
This is a database of pvRecords. + Records can be added and removed from a database.
+
+
+
pvAccess
+
This is a complete implementation of ChannelProvider and Channel + as defined by pvAccess. + It is used by the server side of pvAccess to attach to pvRecords. +
+
Main and V3IOC
+
The pvDatabase can be provided via a Main program or can be part + of a V3IOC. In the later case the IOC has both a database of V3 Records + and a pvDatabase.
+
+

Base classes make it easy to create record instances. +The code attached to each record must create the top +level PVStructure and the following three methods:

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

Doxygen documentation is available at doxygenDoc

+

Getting started

+

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

+

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

+

To start it as a main program do the following:

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

You should see something like the following:

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

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

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

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

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

You will see the following:

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

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

+

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

+

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

+ +

Relationship with pvIOCJava.

+

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

+

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

+

Phased Development

+

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

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

Future phases of pvDatabaseCPP might include:

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

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

+

Features Required for localChannelProvider

+
+
copy and monitor
+
pvDataCPP provides facilities copy and monitor. + This facilities allow a client to access an arbitrary subset + of the fields in the top level structure associated with a channel, + and to monitor changes in the top level structure. + pvDatabaseCPP uses what pvDataCPP provides and has code that + associates these facilities with a PVRecord. +
+
PVRecord and PVDatabase
+
Defined below.
+
The localChannelProvider itself
+
This is the C++ implementation of package pvAccess in pvIOCJava. + The localChannelProvider accesses data from PVRecords. + It implements all channel methods except channelRPC, + which is implemented by pvAccessCPP. +
+
+

Minimum Features Required for pvRecord

+

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

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

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

+ +

Building pvDatabaseCPP

+

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

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

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

+
+mrk> cd ..
+mrk> pwd
+/home/hg/pvDatabaseCPP
+mrk> make
+
+

This builds pvDatabaseCPP and also the tests and all examples.

+

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

+

+Each is a separate top for the following reasons:

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

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

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

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

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

This builds the example.

+ +

iocshell commands

+

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

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

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

+

Commands From pvAccessCPP

+

The following iocsh commands are provided for a V3IOC:

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

Commands implemented by pvDatabaseCPP

+

The following iocsh commands are provided for a V3IOC:

+
+
pvdbl
+
Provides a list of all the pvRecords in database master +
+
+

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

+

Commands implemented by pvaSrv

+

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

+

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

+
+include "dbPv.dbd"
+
+ +

database

+

src/database

+

This Directory has the following files:

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

src/special

+

This directory has the following files:

+
+
traceRecord.h
+
This implements a PVRecord that can set the trace level for + another record. See below for a discussion of trace level.
+ +
+

pvDatabase.h

+

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

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

Each class is described in a separate subsection.

+

C++ namespace and typedefs

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

class PVRecord

+

NOTES:

+ +

PVRecord Methods

+
+class PVRecord
+     public std::tr1::enable_shared_from_this<PVRecord>
+{
+public:
+    POINTER_DEFINITIONS(PVRecord);
+
+    virtual bool init() ;
+    virtual void start() {}
+    virtual void process() {}
+    virtual void destroy();
+
+    static PVRecordPtr create(
+        std::string const & recordName,
+        epics::pvData::PVStructurePtr const & pvStructure);
+    virtual ~PVRecord();
+    std::string getRecordName();
+    PVRecordStructurePtr getPVRecordStructure();
+    PVRecordFieldPtr findPVRecordField(
+        epics::pvData::PVFieldPtr const & pvField);
+    void lock();
+    void unlock();
+    bool tryLock();
+    void lockOtherRecord(PVRecordPtr const & otherRecord);
+    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
+    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
+    void detachClients();
+    bool addListener(PVListenerPtr const & pvListener);
+    bool removeListener(PVListenerPtr const & pvListener);
+    void beginGroupPut();
+    void endGroupPut();
+    int getTraceLevel();
+    void setTraceLevel(int level);
+protected:
+    PVRecord(
+        std::string const & recordName,
+        epics::pvData::PVStructurePtr const & pvStructure);
+    void initPVRecord();
+    epics::pvData::PVStructurePtr getPVStructure();
+    PVRecordPtr getPtrSelf()
+    {
+        return shared_from_this();
+    }
+private:
+...
+}
+
+

The methods are:

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

The protected methods are:

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

class PVRecordField

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

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

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

class PVRecordStructure

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

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

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

class PVRecordClient

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

where

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

class PVListener

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

where

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

class PVDatabase

+
+class PVDatabase : virtual public epics::pvData::Requester {
+public:
+    POINTER_DEFINITIONS(PVDatabase);
+    static PVDatabasePtr getMaster();
+    virtual ~PVDatabase();
+    virtual void destroy();
+    PVRecordPtr findRecord(std::string const& recordName);
+    bool addRecord(PVRecordPtr const & record);
+    epics::pvData::PVStringArrayPtr getRecordNames();
+    bool removeRecord(PVRecordPtr const & record);
+private:
+    PVDatabase();
+};
+
+

where

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

pvAccess

+

This is code that provides an implementation of channelProvider as +defined by pvAccess. +It provides access to PVRecords and is accessed by the server side of remote pvAccess. +It uses the copy and monitor facilities from pvDataCPP and connects +them to a PVRecord. +

+

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

+

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

+

channelProcessLocal

+

Needs to be described.

+

channelGetLocal

+

Needs to be described.

+

channelPutLocal

+

Needs to be described.

+

channelPutGetLocal

+

Needs to be described.

+

channelArrayLocal

+

Needs to be described.

+

MonitorLocal

+

This is the code that implements monitors on changes to fields of a PVRecord. +Because it is called by pvAccess client (monitor methods) and by +PVRecord (when postPut is called), it must be careful to prevent deadlocks. +The implementation is via class MonitorLocal (implemented in monitorFactory.cpp) +and PVCopyMonitor. +MonitorLocal is the interface between pvAccess and PVCopyMonitor. +PVCopyMonitor is the interface between MonitorLocal and PVRecord. +MonitorLocal manages a MonitorElement queue. +While monitoring is active (between start and stop) it keeps an active element +for use by PVCopyMonitor. +While monitoring is active PVCopyMonitor updates the active monitor element whenever +a postPut is issued to any field being monitored. +

+

The following two sections provide a few more details about MonitorLocal +and PVCopyMonitor.

+

MonitorLocal

+

MonitorLocal implements the following abstract base classes:

+
+
Monitor
+
This is described by pvDataCPP. + It has methods start, stop, poll, and release. + These methods are called by the pvAccess client +
+
PVCopyMonitorRequester
+
This has methods releaseActiveElement and unlisten. + These methods are called by PVCopyMonitor. +
+
+MonitorLocal manages the following: +
+
MonitorElementQueue
+
This is a queue of monitor elements. + A Queue is implemented by pvDataCPP and used by MonitorLocal. + It is a finite queue. + A monitor element is described by pvDataCPP. + It has fields pvStructure, changedBitSet, and overrunBitSet. + The pvStructure holds data for a subset of the fields in a PVRecord. + The changedBitSet and overrunBitSet describe changes between + monitor event. + MonitorLocal creates an instance of PVCopy (implemented by pvDataCPP), + which manages the interaction between the set of fields being + monitored and the fields in the top level PVStructure of the PVRecord. + pvCopy is also used to create the pvStructure for each monitor element. +
+
activeElement
+
Whenever monitoring is active monitorLocal + keeps an active element for use by pvCopyMonitor. + It changes the active element based on calls to poll (by the + client) and calls to releaseActiveElement (by pvCopyMonitor). + If there are no free element when releaseActiveElement is + called the current active element is returned. + If a free element is available the client is notified that a new + monitor element is available and the free element becomes the + active element. +
+
+

A brief description on each method in MonitorLocal is:

+
+
start
+
+ Called by client. + With a lock held it clears the monitorElement queue + and allocates an active element. + With no lock held calls pvCopyMonitor->startMonitoring(activeElement) +
+
stop
+
+ Called by client. + With no lock held calls pvCopyMonitor->stopMonitoring(activeElement) +
+
poll
+
+ Called by client. + With a lock held it calls queue->getUsed(); +
+
release
+
+ Called by client. + With a lock held it calls queue->releaseUsed(); +
+
releaseActiveElement
+
+ Called by PVCopyMonitor with no locks held. + With a lock held it tries to get a new free element. + If it can't it just returns the current active element. + Otherwise it does the following. + Using the activeElement it updates the pvStructure + and compresses the changed and overrun bitSet. + It then calls queue->setUsed(activeElement); + It then sets the active element to the new free element. + With no lock held it calls monitorRequester->monitorEvent(getPtrSelf()) + and finally returns the new active element, +
+
unlisten
+
+ With no lock held it calls monitorRequester->unlisten(getPtrSelf()); +
+
+

PVCopyMonitor

+

+pvCopyMonitor is the code that manages changes to +fields in the record. +It is called by PVRecord whenever a postPut is issued to a field. +pvCopyMonitor uses the active monitor element provided by monitorFactory. +Note that this method is called with the record locked. +It only modifies the changedBitSet and overrunBitSet of the +active element but never modifies the pvStructure. +

+

A brief description of the pvCopyMonitor methods is:

+
+
startMonitoring
+
With no lock held it sets its monitorElement to the + startElement passed by monitorLocal and calls pvRecord->addListener(getPtrSelf()). + It locks the pvRecord. + It calls calls addListener for every field in the record that is being + monitored. + It clears the overrun and changed bit sets. + It sets bit 0 of the changed bit set and calls + pvCopyMonitorRequester->releaseActiveElement(); + Thus the client will get the initial values for every field being monitored. + The record is unlocked and the method returns to the caller. +
+
stopMonitoring
+
+ With no lock held it calls pvRecord->removeListener(getPtrSelf()); +
+
dataPut
+
+ This is called because of a call to postPut. + It is called with the record locked. + It updates the changed and overrun bitSets. + It isGroupPut is false it calls + pvCopyMonitorRequester->releaseActiveElement(). + Otherwise it sets dataChanged true. +
+
beginGroupPut
+
+ With a lock held it + sets isGroupPut true and dataChanged false. +
+
endGroupPut
+
+ With a lock held it sets isGroupPut false. + With no lock held and dataChanged true it calls + pvCopyMonitorRequester->releaseActiveElement() +
+
unlisten
+
+ Just calls pvCopyMonitorRequester->unlisten(); +
+
+ +

special

+

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

+

traceRecord

+

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

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

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

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

exampleServer

+

Overview

+

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

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

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

+

+The example can be run on linux as follows:

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

Directory Layout

+

+The directory layout is: +

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

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

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

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

+

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

+

exampleServer.h

+

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

+

The description consists of

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

where

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

The implementation of create method is:

+
+ExampleServerPtr ExampleServer::create(
+    std::string const & recordName)
+{
+    StandardFieldPtr standardField = getStandardField();
+    FieldCreatePtr fieldCreate = getFieldCreate();
+    PVDataCreatePtr pvDataCreate = getPVDataCreate();
+    StructureConstPtr  topStructure = fieldCreate->createFieldBuilder()->
+        addNestedStructure("argument")->
+            add("value",pvString)->
+            endNested()->
+        addNestedStructure("result") ->
+            add("value",pvString) ->
+            add("timeStamp",standardField->timeStamp()) ->
+            endNested()->
+        createStructure();
+    PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure);
+
+    ExampleServerPtr pvRecord(
+        new ExampleServer(recordName,pvStructure));
+    if(!pvRecord->init()) pvRecord.reset();
+    return pvRecord;
+}
+
+This: + +

The private constructor method is:

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

The destructor and destroy methods are:

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

The implementation of init is:

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

The implementation of process is:

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

src/exampleServerMain.cpp

+

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

+

The main program is:

+
+int main(int argc,char *argv[])
+{
+    PVDatabasePtr master = PVDatabase::getMaster();
+    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
+    String recordName("exampleServer");
+    PVRecordPtr pvRecord = ExampleServer::create(recordName);
+    bool result = master->addRecord(pvRecord);
+    if(!result) cout<< "record " << recordName << " not added" << endl;
+    recordName = "traceRecordPGRPC";
+    pvRecord = TraceRecord::create(recordName);
+    result = master->addRecord(pvRecord);
+    if(!result) cout<< "record " << recordName << " not added" << endl;
+    ServerContext::shared_pointer pvaServer =
+        startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true);
+    PVStringArrayPtr pvNames = master->getRecordNames();
+    shared_vector<const string> names = pvNames->view();
+    for(size_t i=0; i<names.size(); ++i) cout << names[i] << endl;
+    string str;
+    while(true) {
+        cout << "Type exit to stop: \n";
+
+    }
+    return 0;
+}
+
+This: + +

V3IOC exampleServer

+

To start exampleServer as part of a V3IOC:

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

You can then issue the commands dbl and pvdbl:

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

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

+ +

exampleDatabase

+

The exampleServer pvDatabase has many records including the following:

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

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

+

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

+

exampleLink

+

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

+

Discussion

+

Access Alternatives

+

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

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

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

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

Data synchronization

+

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

+

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

+

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

+

Some details

+

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

+

A main pvDatabase server issues the following calls:

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

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

+

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

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

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

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

Directory Layout

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

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

+

To start the example:

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

then in another window:

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

exampleLink Implementation

+

exampleLink.h contains the following:

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

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

+

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

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

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

+

The init method is:

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

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

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

Next it makes sure the channelProvider exists.

+

Next it creates the channel and waits until it connects.

+

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

+

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

+

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

+

Look at the code for more details.

+

examplePowerSupply

+

This is an example of creating a service that requires a somewhat complicated +top level PVStructure. +It is similar to the powerSupply example that is provided with pvIOCJava. +Look at the code for details. +

+

Array Performance and Memory Example

+

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

+

Brief Summary

+

The programs are:

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

Each has support for -help.

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

Example output

+

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

+

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

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

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

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

arrayPerformance

+

The arguments for arrayPerforamanceMain are:

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

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

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

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

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

longArrayMonitor

+

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

+

The arguments for longArrayMonitorMain are:

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

longArrayGet

+

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

+ +

The arguments for longArrayGetMain are:

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

longArrayPut

+

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

+ +

The arguments for longArrayPutMain are:

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

Some results

+

array performance

+

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

+

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

+

arrayPerformance results

+

The simplest test to run arrayPerformance with the defaults:

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

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

+

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

+

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

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

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

+

memory leaks

+

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

+

arrayPerformanceMain shows the following:

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

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

+

Vector Performance

+

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

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

Consider the following:

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

As more threads are running the slower each thread runs.

+

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

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

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

+ +
+ + diff --git a/exampleDatabase/src/exampleDatabase.cpp b/exampleDatabase/src/exampleDatabase.cpp index 452633d..6396186 100644 --- a/exampleDatabase/src/exampleDatabase.cpp +++ b/exampleDatabase/src/exampleDatabase.cpp @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -156,13 +155,5 @@ void ExampleDatabase::create() result = master->addRecord(psr); if(!result) cout<< "record " << recordName << " not added" << endl; } - recordName = "laptoprecordListPGRPC"; - pvRecord = RecordListRecord::create(recordName); - if(!pvRecord) { - cout << "RecordListRecord::create failed" << endl; - } else { - result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; - } } diff --git a/examplePowerSupply/src/powerSupplyMain.cpp b/examplePowerSupply/src/powerSupplyMain.cpp index 62ff72b..f344d39 100644 --- a/examplePowerSupply/src/powerSupplyMain.cpp +++ b/examplePowerSupply/src/powerSupplyMain.cpp @@ -13,7 +13,6 @@ #include #include -#include #include #include #include @@ -43,11 +42,6 @@ int main(int argc,char *argv[]) result = master->addRecord(pvRecord); if (!result) cout<< "record " << recordName << " not added" << endl; - recordName = "laptoprecordListPGRPC"; - pvRecord = RecordListRecord::create(recordName); - result = master->addRecord(pvRecord); - if (!result) cout<< "record " << recordName << " not added" << endl; - ContextLocal::shared_pointer contextLocal = ContextLocal::create(); contextLocal->start(true); diff --git a/exampleServer/src/exampleServerMain.cpp b/exampleServer/src/exampleServerMain.cpp index 095df09..118150f 100644 --- a/exampleServer/src/exampleServerMain.cpp +++ b/exampleServer/src/exampleServerMain.cpp @@ -15,7 +15,6 @@ #include #include -#include #include using namespace std; @@ -41,12 +40,7 @@ int main(int argc,char *argv[]) result = master->addRecord(pvRecord); if(!result) cout<< "record " << recordName << " not added" << endl; - recordName = "laptoprecordListPGRPC"; - pvRecord = RecordListRecord::create(recordName); - result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; - - + ContextLocal::shared_pointer contextLocal = ContextLocal::create(); contextLocal->start(); diff --git a/exampleServer/src/exampleServerRegister.cpp b/exampleServer/src/exampleServerRegister.cpp index 5c674ab..2a5f4cb 100644 --- a/exampleServer/src/exampleServerRegister.cpp +++ b/exampleServer/src/exampleServerRegister.cpp @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -56,14 +55,6 @@ static void exampleServerCallFunc(const iocshArgBuf *args) ExampleServerPtr record = ExampleServer::create(recordName); bool result = master->addRecord(record); if(!result) cout << "recordname" << " not added" << endl; - PVRecordPtr pvRecord = RecordListRecord::create( - "laptoprecordListPGRPC"); - if(!pvRecord) { - cout << "RecordListRecord::create failed" << endl; - } else { - result = master->addRecord(pvRecord); - if(!result) cout<< "record " << recordName << " not added" << endl; - } } static void exampleServerRegister(void) diff --git a/src/special/Makefile b/src/special/Makefile index aecb83b..30f6d2a 100644 --- a/src/special/Makefile +++ b/src/special/Makefile @@ -2,8 +2,6 @@ SRC_DIRS += $(PVDATABASE_SRC)/special -INC += recordList.h INC += traceRecord.h -LIBSRCS += recordList.cpp LIBSRCS += traceRecord.cpp diff --git a/src/special/recordList.cpp b/src/special/recordList.cpp deleted file mode 100644 index 78a044f..0000000 --- a/src/special/recordList.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* recordList.cpp */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.04.18 - */ - -#define epicsExportSharedSymbols - -#include - -using std::tr1::static_pointer_cast; -using namespace epics::pvData; -using namespace std; - -namespace epics { namespace pvDatabase { - -RecordListRecordPtr RecordListRecord::create( - std::string const & recordName) -{ - FieldCreatePtr fieldCreate = getFieldCreate(); - PVDataCreatePtr pvDataCreate = getPVDataCreate(); - StructureConstPtr topStructure = fieldCreate->createFieldBuilder()-> - addNestedStructure("argument")-> - add("database",pvString)-> - add("regularExpression",pvString)-> - endNested()-> - addNestedStructure("result") -> - add("status",pvString) -> - addArray("names",pvString) -> - endNested()-> - createStructure(); - PVStructurePtr pvStructure = pvDataCreate->createPVStructure(topStructure); - RecordListRecordPtr pvRecord( - new RecordListRecord(recordName,pvStructure)); - if(!pvRecord->init()) pvRecord.reset(); - return pvRecord; -} - -RecordListRecord::RecordListRecord( - std::string const & recordName, - epics::pvData::PVStructurePtr const & pvStructure) -: PVRecord(recordName,pvStructure) -{ -} - -RecordListRecord::~RecordListRecord() -{ -} - -void RecordListRecord::destroy() -{ - PVRecord::destroy(); -} - -bool RecordListRecord::init() -{ - initPVRecord(); - PVStructurePtr pvStructure = getPVStructure(); - database = pvStructure->getSubField("argument.database"); - if(database.get()==NULL) return false; - regularExpression = pvStructure->getSubField( - "argument.regularExpression"); - if(regularExpression.get()==NULL) return false; - status = pvStructure->getSubField("result.status"); - if(status.get()==NULL) return false; - PVFieldPtr pvField = pvStructure->getSubField("result.names"); - if(pvField.get()==NULL) { - std::cerr << "no result.names" << std::endl; - return false; - } - name = pvStructure->getSubField("result.names"); - if(name.get()==NULL) return false; - return true; -} - -void RecordListRecord::process() -{ - PVStringArrayPtr pvNames = PVDatabase::getMaster()->getRecordNames(); - name->replace(pvNames->view()); - string message(""); - if(database->get().compare("master")!=0) { - message += " can only access master "; - } - string regEx = regularExpression->get(); - if(regEx.compare("")!=0 && regEx.compare(".*")!=0) { - message += " regularExpression not implemented "; - } - status->put(message); -} - - -}} - diff --git a/src/special/recordList.h b/src/special/recordList.h deleted file mode 100644 index 5fecd57..0000000 --- a/src/special/recordList.h +++ /dev/null @@ -1,72 +0,0 @@ -/* recordListTest.h */ -/** - * Copyright - See the COPYRIGHT that is included with this distribution. - * EPICS pvData is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - */ -/** - * @author mrk - * @date 2013.04.18 - */ -#ifndef RECORDLIST_H -#define RECORDLIST_H - -#include - -#include - -namespace epics { namespace pvDatabase { - -class RecordListRecord; -typedef std::tr1::shared_ptr RecordListRecordPtr; - -/** - * @brief List records in PVDatabase. - * - * @deprecated no longer needed because of pvlist command for pvAccess. - * - * This is a record that provides a PVStringArray that - * has the record names of all records in the local PVDatabase. - * It is meant to be used by a channelPutGet request. - */ -class epicsShareClass RecordListRecord : - public PVRecord -{ -public: - POINTER_DEFINITIONS(RecordListRecord); - /** - * Factory methods to create RecordListRecord. - * @param recordName The name for the RecordListRecord. - * @return A shared pointer to RecordListRecord.. - */ - static RecordListRecordPtr create( - std::string const & recordName); - /** - * destructor - */ - virtual ~RecordListRecord(); - /** - * Clean up any resources used. - */ - virtual void destroy(); - /** - * standard init method required by PVRecord - * @return true unless record name already exists. - */ - virtual bool init(); - /* - * Generated the list of record names. - */ - virtual void process(); -private: - RecordListRecord(std::string const & recordName, - epics::pvData::PVStructurePtr const & pvStructure); - epics::pvData::PVStringPtr database; - epics::pvData::PVStringPtr regularExpression; - epics::pvData::PVStringPtr status; - epics::pvData::PVStringArrayPtr name; -}; - -}} - -#endif /* RECORDLIST_H */