pvDatabaseCPP

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

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

Abstract

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

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

Status of this Document

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

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

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

Table of Contents

Introduction

Overview

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

This document describes components that provide the following features:

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

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

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

Getting started

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

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

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

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

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

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

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

The database has the following records:

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

Relationship with pvIOCJava.

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

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

Example PVRecord Extension

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

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

The example consists of two components:

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

ExampleCounter.h

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

The description consists of

class ExampleCounter;
typedef std::tr1::shared_ptr<ExampleCounter> ExampleCounterPtr;

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

where

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

The implementation of create method is:

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

The private constructor method is:

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

The destructor and destroy methods are:

ExampleCounter::~ExampleCounter()
{
}

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

The implementation of init is:

bool ExampleCounter::init()
{

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

The implementation of process is:

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

exampleCounterMain.cpp

This is in test/server.

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

The main program is:

int main(int argc,char *argv[])
{
    PVDatabasePtr master = PVDatabase::getMaster();
    ChannelProviderLocalPtr channelProvider = ChannelProviderLocal::create();
    String recordName("exampleCounter");
    PVRecordPtr pvRecord = ExampleCounter::create(recordName);
    bool result = master->addRecord(pvRecord);
    cout << "result of addRecord " << recordName << " " << result << endl;
    pvRecord.reset();
    cout << "exampleServer\n";
    string str;
    while(true) {
        cout << "Type exit to stop: \n";
        getline(cin,str);
        if(str.compare("exit")==0) break;

    }
    return 0;
}
This:
  • Gets a pointer to the master database.
  • Creates the local Channel Provider. This starts the pvAccess server.
  • Creates a ExampleCounter record with the name exampleCounter
  • Prints exampleCounter on standard out.
  • Runs forever until the user types exit on standard in.

Phased Development

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

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

Future phases of pvDatabaseCPP might include:

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

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

Features Required for localChannelProvider

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

Minumum Features Required for pvRecord

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

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

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

database

This directory has the following files:

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

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

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

Each class is described in a separate subsection.

C++ namespace and typedefs

namespace epics { namespace pvDatabase {

class PVRecord;
typedef std::tr1::shared_ptr<PVRecord> PVRecordPtr;
typedef std::map<epics::pvData::String,PVRecordPtr> PVRecordMap;

class PVRecordField;
typedef std::tr1::shared_ptr<PVRecordField> PVRecordFieldPtr;
typedef std::vector<PVRecordFieldPtr> PVRecordFieldPtrArray;
typedef std::tr1::shared_ptr<PVRecordFieldPtrArray> PVRecordFieldPtrArrayPtr;

class PVRecordStructure;
typedef std::tr1::shared_ptr<PVRecordStructure> PVRecordStructurePtr;

class PVRecordClient;
typedef std::tr1::shared_ptr<PVRecordClient> PVRecordClientPtr;

class PVListener;
typedef std::tr1::shared_ptr<PVListener> PVListenerPtr;

class RecordPutRequester;
typedef std::tr1::shared_ptr<RecordPutRequester> RecordPutRequesterPtr;

class PVDatabase;
typedef std::tr1::shared_ptr<PVDatabase> PVDatabasePtr;

class PVRecord

NOTES:

  • This section uses the name record instead of "an instance of PVRecord".
  • Most clients will access a record via the local channel provider, i. e. via pvAccess. Thus this section is mainly of interest to the local channel provider and record implementers.
  • Most readers will not care about most of the PVRecord methods. Most of the methods are used by the pvAccess code. Service implementers will mostly be interested in methods init and process. These are described first.

PVRecord Methods
class PVRecord
     public epics::pvData::Requester,
     public std::tr1::enable_shared_from_this<PVRecord>
{
public:
    POINTER_DEFINITIONS(PVRecord);

    virtual bool init() {initPVRecord(); return true;}
    virtual void process() {}
    virtual void destroy();

    static PVRecordPtr create(
        epics::pvData::String const & recordName,
        epics::pvData::PVStructurePtr const & pvStructure);
    virtual ~PVRecord();
    epics::pvData::String getRecordName();
    PVRecordStructurePtr getPVRecordStructure();
    PVRecordFieldPtr findPVRecordField(
        epics::pvData::PVFieldPtr const & pvField);
    bool addRequester(epics::pvData::RequesterPtr const & requester);
    bool removeRequester(epics::pvData::RequesterPtr const & requester);
    inline void lock_guard() { epics::pvData::Lock theLock(mutex); }
    void lock();
    void unlock();
    bool tryLock();
    void lockOtherRecord(PVRecordPtr const & otherRecord);
    bool addPVRecordClient(PVRecordClientPtr const & pvRecordClient);
    bool removePVRecordClient(PVRecordClientPtr const & pvRecordClient);
    void detachClients();
    bool addListener(PVListenerPtr const & pvListener);
    bool removeListener(PVListenerPtr const & pvListener);
    void beginGroupPut();
    void endGroupPut();
    epics::pvData::String getRequesterName() {return getRecordName();}
    virtual void message(
        epics::pvData::String const & message,
        epics::pvData::MessageType messageType);
    void message(
        PVRecordFieldPtr const & pvRecordField,
        epics::pvData::String const & message,
        epics::pvData::MessageType messageType);
    void toString(epics::pvData::StringBuilder buf);
    void toString(epics::pvData::StringBuilder buf,int indentLevel);
    int getTraceLevel();
    void setTraceLevel(int level);
protected:
    PVRecord(
        epics::pvData::String const & recordName,
        epics::pvData::PVStructurePtr const & pvStructure);
    void initPVRecord();
    epics::pvData::PVStructurePtr getPVStructure();
    PVRecordPtr getPtrSelf()
    {
        return shared_from_this();
    }
private:
...
}

The methods are:

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

The protected methods are:

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

class PVRecordField

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

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

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

class PVRecordStructure

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

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

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

class PVRecordClient

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

where

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

class PVListener

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

where

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

class PVDatabase

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

where

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

pvAccess

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

channelProviderLocal

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

pvCopy

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

monitorAlgorithm

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

monitorFactory

Overview

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

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

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

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

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

MonitorFactory

MonitorFactory provides the following methods:

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

where

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

special

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

traceRecord

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

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

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

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

recordList

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

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

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

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

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

powerSupplyRecordTest

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