pvAccessCPP is the C++ implementation of pvAccess, which is network support for transporting structured data as defined by pvData.
pvAccess is one of a related set of products: relatedDocumentsV4.html
This is the 12-December-2015 version of the C++ implementation of pvAccess. The code is a complete implementation of pvAccess.
This product is available via an open source license
This document describes the pvAccess Application Program Interface (API).
The reader is assumed to have a basic understanding of EPICS V4 as described in:
EPICS V4 Developer's Guide
The pvAccess API is callback based and uses Status to report problems to the
client, which means that it can be complex to use.
If your primary interest is client access then, instead of reading this document,
read:
pvaClientCPP
If your primary interest is implementing support for PVRecords then,
before reading this documement read:
pvDatabaseCPP
Doxygen documentation is available at doxygenDoc
This document briefly describes the most important classes, class methods, and global methods used by client and/or service code. Not all classes and methods are described. When source code from include files is shown it is often a simplified version. Ptr is shorthand for ::shared_pointer For example:
ChannelFindPtrinstead of
ChannelFind::shared_pointer
pvAccess provides network support for structured data as described by pvData.
pvAccess has the following basic concepts:
The client and server can exists in the same process or if the provider is pva the provider can be at some remote location in the network.
Each provider must have a providerName that is unique in the local process.
An arbitrary number of channelProviders can be implemented on both the client and server side.
Three global methods are available:
ChannelProviderRegistry provides the method:
Provides the following methods:
Channel provides methods to create the following:
Since both the client and server side of pvAccess use the same ChannelProviderRegistry an an arbitrary number of providers can register. Note in particular that both the client and server sides of pva can both register. This allows server code to also use pva client to communicate with other servers.
The server side of the pva network protocal. allows an arbitrary number of providers to register with it. Existing examples are:
pvAccessCPP provides the following command line utilities:
pvlist, pvinfo, pvget, pvput, and eget.
In order to use these commands a path to the pvAccessCPP bin directory must exists. For example, on my linux workstation .bash_profile includes the statements:
export EPICSV4=/home/epicsv4 export PATH=$PATH:${EPICSV4}/pvAccessCPP/bin/${EPICS_HOST_ARCH}
This document gives a VERY brief explaination if each command but each provides a -help option. For example:
mrk> pvlist -help Usage: pvlist [options] [server address or GUID starting with '0x']... -h: Help: Print this message options: -i Print server info (when server address list/GUID is given) -w <sec>: Wait time, specifies timeout, default is 3.000000 second(s) -q: Quiet mode, print only error messages -d: Enable debug output examples: pvlist pvlist ioc0001 pvlist 10.5.1.205:10000 pvlist 0x83DE3C540000000000BF351F
A longer explanation of the commands is in:
EPICS V4 Developer's GuideShows all servers avaliable via the pva network protocal and also a list of all channels for a particular server.
Shows the connection status and introspection interface for channels.
Returns data for a channel via channelGet or monitor.
Puts data to a channel via channelPut.
pvget on steroids. Also has support for channelRPC and some of the normative types.
The following are the include files that are of most interest to clients:
enum AccessRights {none,read,readWrite}; class Lockable... class ScopedLock...
The following are of interest to service code:
To start both the pva and ca client providers issue the commands:
ClientFactory::start(); CAClientFactory::start();
To see examples of how to start a pvAccess server look at the examples provided in exampleDatabaseCPP. It shows examples for both a standalone main server and a V4 server that runs as part of a V3 IOC. The following is taken from exampleDatabaseMain.cpp that is in example database:
int main(int argc,char *argv[]) { ... ChannelProviderLocalPtr channelProvider = getChannelProviderLocal(); ServerContext::shared_pointer ctx = startPVAServer(PVACCESS_ALL_PROVIDERS,0,true,true); ... ctx->destroy(); return 0; }
class ChannelProviderRegistry { public: virtual ~ChannelProviderRegistry(); virtual ChannelProviderPtr getProvider(string const & providerName); virtual ChannelProviderPtr createProvider(string const & providerName); virtual std::auto_ptr<vector<string> > getProviderNames(); }; epicsShareExtern ChannelProviderRegistryPtr getChannelProviderRegistry(); epicsShareExtern void registerChannelProviderFactory(ChannelProviderFactoryPtr const & channelProviderFactory); epicsShareExtern void unregisterChannelProviderFactory(ChannelProviderFactoryPtr const & channelProviderFactor
The global methods are:
The methods for ChannelProviderRegistry are:
class ChannelProvider { public: static const short PRIORITY_MIN = 0; static const short PRIORITY_MAX = 99; static const short PRIORITY_DEFAULT = PRIORITY_MIN; static const short PRIORITY_LINKS_DB = PRIORITY_MAX; static const short PRIORITY_ARCHIVE = (PRIORITY_MAX + PRIORITY_MIN) / 2; static const short PRIORITY_OPI = PRIORITY_MIN; virtual destroy() {} virtual std::string getProviderName() = 0; virtual ChannelFindPtr channelFind( std::string const & channelName, ChannelFindRequesterPtr const & channelFindRequester) = 0; virtual ChannelFindPtr channelList( ChannelListRequesterPtr const & channelListRequester) = 0; virtual ChannelPtr createChannel( std::string const & channelName, ChannelRequesterPtr const & channelRequester, virtual ChannelPtr createChannel( std::string const & channelName, ChannelRequesterPtr const & channelRequester, short priority, std::string const & address); /// experimental methods virtual void configure(PVStructurePtr /*configuration*/) {}; virtual void flush() {}; virtual void poll() {}; }; class ChannelFind { public: virtual ChannelProviderPtr getChannelProvider(); virtual void cancel(); }; class ChannelFindRequester { public: virtual ~ChannelFindRequester() {} virtual void channelFindResult( const Status& status, ChannelFindPtr const & channelFind, bool wasFound) = 0; }; class ChannelListRequester { public: virtual ~ChannelListRequester() {}; virtual void channelListResult( const Status& status, ChannelFindPtr const & channelFind, PVStringArray::const_svector const & channelNames, bool hasDynamic) = 0; };
The methods of ChannelProvider are:
The methods of ChannelFind are:
The method of ChannelFindRequester are:
The method of ChannelListRequester are:
This must be implemented by a client. It shows the result of a ChannelProvider::createChannel request and also the connection state of the channel.
class ChannelRequester : Requester { public: virtual void channelCreated( const Status& status, ChannelPtr const & channel) = 0; virtual void channelStateChange( ChannelPtr const & channel, Channel::ConnectionState connectionState) = 0; };
The methods of ChannelRequester are:
class Channel : Requester ... { public: POINTER_DEFINITIONS(Channel); enum ConnectionState { NEVER_CONNECTED, CONNECTED, DISCONNECTED, DESTROYED }; static const char* ConnectionStateNames[]; virtual destroy() {} virtual ChannelProviderPtr getProvider() = 0; virtual std::string getRemoteAddress() = 0; virtual ConnectionState getConnectionState() = 0; virtual std::string getChannelName() = 0; virtual ChannelRequesterPtr getChannelRequester() = 0; virtual bool isConnected() = 0; virtual void getField( GetFieldRequesterPtr const & requester, std::string const & subField) = 0; virtual AccessRights getAccessRights(PVFieldPtr const & pvField) = 0; virtual ChannelProcessPtr createChannelProcess( ChannelProcessRequesterPtr const & channelProcessRequester, PVStructurePtr const & pvRequest) = 0; virtual ChannelGetPtr createChannelGet( ChannelGetRequesterPtr const & channelGetRequester, PVStructurePtr const & pvRequest) = 0; virtual ChannelPutPtr createChannelPut( ChannelPutRequesterPtr const & channelPutRequester, PVStructurePtr const & pvRequest) = 0; virtual ChannelPutGetPtr createChannelPutGet( ChannelPutGetRequesterPtr const & channelPutGetRequester, PVStructurePtr const & pvRequest) = 0; virtual ChannelRPCPtr createChannelRPC( ChannelRPCRequesterPtr const & channelRPCRequester, PVStructurePtr const & pvRequest) = 0; virtual MonitorPtr createMonitor( MonitorRequesterPtr const & monitorRequester, PVStructurePtr const & pvRequest) = 0; virtual ChannelArrayPtr createChannelArray( ChannelArrayRequesterPtr const & channelArrayRequester, PVStructurePtr const & pvRequest) = 0; virtual void printInfo() = 0; virtual void printInfo(std::ostream& out) = 0; };where:
class GetFieldRequester : virtual public Requester { public: virtual void getDone( const Status& status, FieldConstPtr const & field) = 0; };where:
This is a base class for ChannelGet, ChannelPut, etc.
class ChannelRequest { public: virtual ChannelPtr getChannel() = 0; virtual void cancel() = 0; virtual void lastRequest() = 0; };where:
This is used to get data from a server.
class ChannelGet : public ChannelRequest { public: virtual void get() = 0; };where
class ChannelGetRequester : virtual public Requester { public: virtual void channelGetConnect( const Status& status, ChannelGetPtr const & channelGet, Structure::const_shared_pointer const & structure) = 0; virtual void getDone( const Status& status, ChannelGetPtr const & channelGet, PVStructurePtr const & pvStructure, BitSetPtr const & bitSet) = 0; };where:
This is used to put data to a server.
class ChannelPut : public ChannelRequest { public: virtual void put( PVStructurePtr const & pvPutStructure, BitSetPtr const & putBitSet) = 0; virtual void get() = 0; };where:
class ChannelPutRequester : virtual public Requester { public: virtual void channelPutConnect( const Status& status, ChannelPutPtr const & channelPut, Structure::const_shared_pointer const & structure) = 0; virtual void putDone( Status & status, ChannelPutPtr const & channelPut) = 0; virtual void getDone( const Status& status, ChannelPutPtr const & channelPut, PVStructurePtr const & pvStructure, BitSetPtr const & bitSet) = 0; };where:
This is used to:
put data to a server process get data from the server
class ChannelPutGet : public ChannelRequest { public: virtual void putGet( PVStructurePtr const & pvPutStructure, BitSetPtr const & putBitSet) = 0; virtual void getPut() = 0; virtual void getGet() = 0; };where:
class ChannelPutGetRequester : virtual public Requester { public: virtual void channelPutGetConnect( const Status& status, ChannelPutGetPtr const & channelPutGet, Structure::const_shared_pointer const & putStructure, Structure::const_shared_pointer const & getStructure) = 0; virtual void putGetDone( const Status& status, ChannelPutGetPtr const & channelPutGet, PVStructurePtr const & pvGetStructure, BitSetPtr const & getBitSet) = 0; virtual void getPutDone( const Status& status, ChannelPutGetPtr const & channelPutGet, PVStructurePtr const & pvPutStructure, BitSetPtr const & putBitSet) = 0; virtual void getGetDone( const Status& status, ChannelPutGetPtr const & channelPutGet, PVStructurePtr const & pvGetStructure, BitSetPtr const & getBitSet) = 0; };where:
Get/Put a subset of an array. This works for all of scalarArray, unionArray, and structureArray.
class ChannelArray : public ChannelRequest { public: virtual void putArray( PVArrayPtr const & putArray, size_t offset = 0, size_t count = 0, size_t stride = 1) = 0; virtual void getArray( size_t offset = 0, size_t count = 0, size_t stride = 1) = 0; virtual void getLength() = 0; virtual void setLength(size_t length) = 0; };where:
class ChannelArrayRequester : virtual public Requester { public: virtual void channelArrayConnect( const Status& status, ChannelArrayPtr const & channelArray, Array::const_shared_pointer const & array) = 0; virtual void putArrayDone( const Status& status, ChannelArrayPtr const & channelArray) = 0; virtual void getArrayDone( const Status& status, ChannelArrayPtr const & channelArray, PVArrayPtr const & pvArray) = 0; virtual void getLengthDone( const Status& status, ChannelArrayPtr const & channelArray, size_t length) = 0; virtual void setLengthDone( const Status& status, ChannelArrayPtr const & channelArray) = 0; };where:
Described in pvDataCPP. See: EPICS pvDataCPP
For convenience the classes are shown here.
class MonitorElement { public: MonitorElement(){} MonitorElement(PVStructurePtr const & pvStructurePtr); PVStructurePtr pvStructurePtr; BitSetPtr changedBitSet; BitSetPtr overrunBitSet; };
class Monitor : public Destroyable{ public: virtual ~Monitor(){} virtual Status start() = 0; virtual Status stop() = 0; virtual MonitorElementPtr poll() = 0; virtual void release(MonitorElementPtr const & monitorElement) = 0; };
class MonitorRequester : public virtual Requester { public: virtual ~MonitorRequester(){} virtual void monitorConnect(Status const & status, MonitorPtr const & monitor, StructureConstPtr const & structure) = 0; virtual void monitorEvent(MonitorPtr const & monitor) = 0; virtual void unlisten(MonitorPtr const & monitor) = 0; };
class ChannelRPC : public ChannelRequest { public: virtual void request(PVStructurePtr const & pvArgument) = 0; };where:
class ChannelRPCRequester : virtual public Requester { public: virtual void channelRPCConnect( const Status& status, ChannelRPCPtr const & channelRPC) = 0; virtual void requestDone( const Status& status, ChannelRPCPtr const & channelRPC, PVStructurePtr const & pvResponse) = 0; };where:
This package provides implementation of the client side of a ChannelProvider that uses the pvAccess network protocol to communicate between client and server. The provider name is pva.
class ClientFactory { static void start(); static void stop(); };
where
This provides an implementation of ChannelProvider that uses the Channel Access network protocol. It converts between DBR data and pvData. The provider name is ca.
class CAClientFactory { static void start(); static void stop(); };
where
This implements the server side of the pva network protocal and also provides a context for running remote providers.
class ServerContext { public: virtual ~ServerContext() {}; virtual const GUID& getGUID() = 0; virtual const Version& getVersion() = 0; virtual void initialize(ChannelProviderRegistryPtr const & channelProviderRegistry) = 0; virtual void run(int32 seconds) = 0; virtual void shutdown() = 0; virtual void destroy() = 0; virtual void printInfo() = 0; virtual void printInfo(std::ostream& str) = 0; virtual void dispose() = 0; virtual epicsTimeStamp& getStartTime() = 0; virtual void setBeaconServerStatusProvider(BeaconServerStatusProviderPtr const & beaconServerStatusProvider) = 0; }; ServerContextPtr startPVAServer( std::string const & providerNames = PVACCESS_ALL_PROVIDERS, int timeToRun = 0, bool runInSeparateThread = false, bool printInfo = false);where
ChannelProviderRegistry
implementation and initialize server.0
the method would block until destroy()
is called.
destroy()
and silently handles all exceptions.
RPCClient is an interface class that is used by a service client.
class RPCClient { virtual ~RPCClient() {} static shared_pointer create(const string & serviceName); static PVStructurePtr sendRequest(const string & serviceName, PVStructurePtr const & request, double timeOut = RPCCLIENT_DEFAULT_TIMEOUT); void issueConnect(); bool waitConnect(double timeout = RPCCLIENT_DEFAULT_TIMEOUT); PVStructurePtr request( PVStructurePtr const & pvArgument, double timeout = RPCCLIENT_DEFAULT_TIMEOUT, bool lastRequest = false); void issueRequest( PVStructurePtr const & pvArgument, bool lastRequest = false); PVStructurePtr waitResponse(double timeout = RPCCLIENT_DEFAULT_TIMEOUT); };where
Provides the context for the server side of channelRPC.
class RPCServer : { RPCServer(); virtual ~RPCServer(); void registerService(string const & serviceName, RPCService::shared_pointer const & service); void registerService(string const & serviceName, RPCServiceAsync::shared_pointer const & service); void unregisterService(string const & serviceName); void run(int seconds = 0); void runInNewThread(int seconds = 0); void destroy(); void printInfo(); }; // private helper method, will (can) be removed in the future ChannelPtr createRPCChannel(ChannelProviderPtr const & provider, string const & channelName, ChannelRequesterPtr const & channelRequester, ServicePtr const & rpcService);where
Base class for channelRPC services. To implement a rpcService you need to implement RPCService or RPCServiceAsync interface.
class RPCRequestException { RPCRequestException(Status::StatusType status, string const & message); Status::StatusType getStatus() const; }; class RPCService { virtual ~RPCService() {}; virtual PVStructurePtr request( PVStructurePtr const & args ) = 0; }; class RPCResponseCallback { virtual ~RPCResponseCallback() {}; virtual void requestDone( Status const & status, PVStructurePtr const & result ) = 0; }; class epicsShareClass RPCServiceAsync : public virtual Service { virtual ~RPCServiceAsync() {}; virtual void request( PVStructurePtr const & args, RPCResponseCallbackPtr const & callback ) = 0; };where
A pipeline server supports reliable monitor support. This is implemented by allowing the client to make the server delay when the client can not keep up with the server.
The server implememts Channel::createMonitor but none of the other create methods. When the client creates a monitor the following request options can be specified:
"record[pipeline=true,queueSize=size,ackAny=n]"where
Each time the client calls Monitor::release the client sends an ack message to the server every ackAny events. The server uses this to delay sending new monitors if the monitor queue is full.
pvAccessCPP/testApp/remote/pipelineServiceExample.cpp Is an example. It has an additional record option record[limit=n] which makes the server call unlisten aften n events.
This is a class used by a pipeline service. It implements channelProvider for the service and provides a context for running the service.
class PipelineServer { void registerService(string const & serviceName, PipelineServicePtr const & service); void unregisterService(string const & serviceName); void run(int seconds = 0); /// Method requires usage of std::tr1::shared_ptr<PipelineServer>. This instance must be /// owned by a shared_ptr instance. void runInNewThread(int seconds = 0); void destroy(); void printInfo(); }; // private helper method, will (can) be removed in the future ChannelPtr createPipelineChannel(ChannelProviderPtr const & provider, string const & channelName, ChannelRequesterPtr const & channelRequester, PipelineServicePtr const & pipelineService);where
An instance of PipelineControl is created by PipelineServer and passed to PipelineService::request.
class PipelineControl { virtual size_t getFreeElementCount() = 0; virtual size_t getRequestedCount() = 0; virtual epics::pvData::MonitorElement::shared_pointer getFreeElement() = 0; virtual void putElement(epics::pvData::MonitorElement::shared_pointer const & element) = 0; virtual void done() = 0; };where
An instance of PipelineSession is created PipelineService for each instance of the service.
class PipelineSession { virtual size_t getMinQueueSize() const = 0; virtual epics::pvData::Structure::const_shared_pointer getStructure() const = 0; virtual void request(PipelineControl::shared_pointer const & control, size_t elementCount) = 0; virtual void cancel() = 0; };where
This is called to create an instance of a service. Note that it returns an instance of PipelineSession, which must be implemented by the service.
class PipelineService { virtual PipelineSessionPtr createPipeline(PVStructurePtr const & pvRequest) = 0; };where