copy and monitor are not used in this project. They are intended for use by pvAccess and by pvAccess servers. They are provided with this project because the code depends only on pvData itself.
This document describes C++ specific code. pvRequest.html provides a language independent overview of copy and monitor.
NOTE:pvRequest.html must be updated since it is based on an earlier version of pvCopy that had knowledge of PVRecord. The C++ version was implemented in pvDatabaseCPP and the Java version on pvIOCJava. At present only the C++ version of the new API for pvCopy is implemented.
Copy provides:
copy provides the ability to create a structure that has a copy of an arbitrary subset of the fields in an existing top level structure. In addition it allows global options and field specific options. It has two main components: createRequest and pvCopy. Given a string createRequest creates a pvRequest, which is a PVStructure that has the format expected by pvCopy.
This is mainly used by pvAccess clients. Given a request string it creates a pvRequest structure that can be passed to the pvAccess create methods. In turn pvAccess passes the pvRequest to a local channel provider which then passes it to pvCopy.
The definition of the public members is:
class CreateRequest { ... static CreateRequestPtr create(); virtual PVStructurePtr createRequest(std::string const &request); std::string getMessage(); };
An example of how it is used is:
CreateRequestPtr createRequest = CreateRequest::create(); PVStructurePtr pvRequest = createRequest->createRequest(request); if(pvRequest==NULL) { std::string error = createRequest->getMessage(); // take some action } else { //success do something }
The definition of the public members is:
class epicsShareClass PVCopyTraverseMasterCallback { ... virtual void nextMasterPVField(PVFieldPtr const &pvField); }; class class epicsShareClass PVCopy { ... static PVCopyPtr create( PVStructurePtr const &pvMaster, PVStructurePtr const &pvRequest, std::string const & structureName); PVStructurePtr getPVMaster(); void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback); StructureConstPtr getStructure(); PVStructurePtr createPVStructure(); size_t getCopyOffset(PVFieldPtr const &masterPVField); size_t getCopyOffset( PVStructurePtr const &masterPVStructure, PVFieldPtr const &masterPVField); PVFieldPtr getMasterPVField(std::size_t structureOffset); void initCopy( PVStructurePtr const ©PVStructure, BitSetPtr const &bitSet); void updateCopySetBitSet( PVStructurePtr const ©PVStructure, BitSetPtr const &bitSet); void updateCopyFromBitSet( PVStructurePtr const ©PVStructure, BitSetPtr const &bitSet); void updateMaster( PVStructurePtr const ©PVStructure, BitSetPtr const &bitSet); PVStructurePtr getOptions(std::size_t fieldOffset); ... };where
This consists of two components:
class MonitorElement { MonitorElement(PVStructurePtr const & pvStructurePtr); PVStructurePtr pvStructurePtr; BitSetPtr changedBitSet; BitSetPtr overrunBitSet; }; class 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 { 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; };
MonitorElement holds the data for one element of a monitor queue. It has the fields:
A queue of monitor elements must be implemented by any channel provider that implements Channel::createMonitor. For an example implementation look at pvDatabaseCPP. It has the following:
typedef Queue<MonitorElement> MonitorElementQueue; typedef std::tr1::shared_ptr<MonitorElementQueue> MonitorElementQueuePtr; class MultipleElementQueue : public ElementQueue { public: POINTER_DEFINITIONS(MultipleElementQueue); virtual ~MultipleElementQueue(){} MultipleElementQueue( MonitorLocalPtr const &monitorLocal, MonitorElementQueuePtr const &queue, size_t nfields); virtual void destroy(){} virtual Status start(); virtual Status stop(); virtual bool dataChanged(); virtual MonitorElementPtr poll(); virtual void release(MonitorElementPtr const &monitorElement); ... };
Monitor must be implemented by any channel provider that implements Channel::createMonitor. Remote PVAccess also implements Monitor on the client side. Note that each client has it's own queue that is not shared with other client.
Monitor has the following methods:
This must be implemented by a pvAccess client. It has the methods:
class MonitorPlugin { virtual std::string const & getName() = 0; virtual bool causeMonitor( PVFieldPtr const &pvField, PVStructurePtr const &pvTop, MonitorElementPtr const &monitorElement) = 0; virtual void monitorDone( MonitorElementPtr const &monitorElement); virtual void startMonitoring(); virtual void stopMonitoring(); virtual void beginGroupPut(); virtual void endGroupPut(); }; class MonitorPluginCreator { virtual MonitorPluginPtr create( FieldConstPtr const &field, StructureConstPtr const &top, PVStructurePtr const &pvFieldOptions) = 0; virtual std::string const & getName() = 0; } class MonitorPluginManager { static MonitorPluginManagerPtr get(); bool addPlugin( std::string const &pluginName, MonitorPluginCreatorPtr const &creator); MonitorPluginCreatorPtr findPlugin(std::string const &pluginName); void showNames(); };
MonitorPlugin must be implemented by the plugin implementation. It has methods:
MonitorPluginCreator must also be implemented by the plugin implementation. It is called for each field instance that has options of the from [plugin=name...] where name is the name of the plugin. Note that a plugin instance will belong to a single client. It has methods:
MonitorPluginManager has the methods:
NOTE: Should the method causeMonitor have arguments pvField and pvTop be defined so that they can not be modified. This would be possible if the following was defined:
typedef std::tr1::shared_ptr<const PVField> PVFieldConstPtr; typedef std::tr1::shared_ptr<const PVStructure> PVStructureConstPtr;then the definition for causeMonitor could be:
virtual bool causeMonitor( PVFieldConstPtr const &pvField, PVStructureConstPtr const &pvTop, MonitorElementPtr const &monitorElement) = 0;But just adding these definitions is not sufficient. In addition all methods defined in pvDataCPP must be checked. In particular many of the methods in Convert must have their arguments modified. Big job.
This section describes an example plugin that:
As an example assume that a channel provided by pvAccess has a top level structure that represents a power supply.
structure powerSupply structure alarm structure timeStamp structure power double value structure alarm structure display structure voltage double value structure alarm structure display structure current double value structure alarm structure display
A pvAccess client wants to create a monitor on the powerSupply as follows: The client wants a top level structure that looks like:
structure powerSupply structure alarm structure timeStamp structure power double value structure voltage double value structure current double valueIn addition the client wants monitors to occur only when one of the monitored fields changes value but not just because a put occured. Also if only the timeStamp changes value then that should not cause a monitor.
The example monitor plugin implements the semantics the client wants. It can be attached to any field via the following options:
[plugin=onChange,raiseMonitor=value]This plugin will trigger a monitor for the field only if the field changes value. In addition value equals false means do not raise a monitor for changes to this field. But if a change to another field does cause a monitor the change to this field will be passed to the client.
Assume that the client has already connected to the channel. The client can then issue the commands:
std::string request("field(alarm[plugin=onChange]"); request += ",timeStamp[plugin=onChange,raiseMonitor=false]"; request += ",power.value[plugin=onChange"; request += ",voltage.value[plugin=onChange"; request += ",current.value[plugin=onChange"; PVStructurePtr pvRequest = createRequest->createRequest(request); MonitorPtr monitor = channel->createMonitor(monitorRequester,pvRequest);
The header file to create the example has the definition:
class ExampleMonitorPlugin{ public: static void create(); };
The implementation is:
class OnChangePlugin : public MonitorPlugin { public: virtual ~OnChangePlugin(){} OnChangePlugin() {} bool init( FieldConstPtr const &field, StructureConstPtr const &top, PVStructurePtr const &pvFieldOptions) { pvField = getPVDataCreate()->createPVField(field); raiseMonitor = true; if(pvFieldOptions!=NULL) { PVStringPtr pvString = pvFieldOptions->getSubField<PVString>("raiseMonitor"); if(pvString!=NULL) { std::string value = pvString->get(); if(value.compare("false")==0) raiseMonitor = false; } } return true; } virtual std::string &getName(){return pluginName;} virtual bool causeMonitor( PVFieldPtr const &pvNew, PVStructurePtr const &pvTop, MonitorElementPtr const &monitorElement) { bool isSame = convert->equals(pvNew,pvField); if(isSame) return false; convert->copy(pvNew,pvField); return raiseMonitor; } private: PVFieldPtr pvField; bool raiseMonitor; }; class OnChangePluginCreator : public MonitorPluginCreator { public: virtual std::string &getName(){return pluginName;} virtual MonitorPluginPtr create( FieldConstPtr const &field, StructureConstPtr const &top, PVStructurePtr const &pvFieldOptions) { OnChangePluginPtr plugin(new OnChangePlugin()); bool result = plugin->init(field,top,pvFieldOptions); if(!result) return MonitorPluginPtr(); return plugin; } }; void ExampleMonitorPlugin::create() { static OnChangePluginCreatorPtr plugin; static Mutex mutex; Lock xx(mutex); if(plugin==NULL) { plugin = OnChangePluginCreatorPtr(new OnChangePluginCreator()); MonitorPluginManager::get()->addPlugin(pluginName,plugin); } }