Table of Contents

support for copy and monitor

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:

createRequest
The Channel create methods in pvAccess all have an argument PVStructure pvRequest.
Given an ascii string createRequest creates a PVStructure that provides a pvData representation of the information from the ascii string. It is this structure that can be passed to the channel create methods.
The information in a pvRequest selects an arbitrary subset of the fields in a top level structure that resides in the server. In addition options can be specified. Both global and field specific options can be specified.
pvCopy
This is a facility used by channel providers. It provides client specific code that manages a copy of an arbitrary subset of the fields in a top level structure that resides in the provider. It also allows provider access to options specified by the client.
Monitor provides:
monitor
This is support code for channel providers that implement channel monitor. It, together with the queue facility, provides support for monitor queues.
monitorPlugin
This is support for implementing monitor plugins. A monitor plugin can be developed that has no knowledge of pvAccess but only pvData.

support for copy

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.

createRequest

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
}

pvCopy

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  &copyPVStructure,
        BitSetPtr const  &bitSet);
     void updateCopySetBitSet(
        PVStructurePtr const  &copyPVStructure,
        BitSetPtr const  &bitSet);
    void updateCopyFromBitSet(
        PVStructurePtr const  &copyPVStructure,
        BitSetPtr const  &bitSet);
    void updateMaster(
        PVStructurePtr const  &copyPVStructure,
        BitSetPtr const  &bitSet);
    PVStructurePtr getOptions(std::size_t fieldOffset);
...
};
where
PVCopyTraverseMasterCallback::nextMasterPVField
PVCopyTraverseMasterCallback is a callback which must be implemented by the code that uses pvCopy, normally the channel provider. It has the single method nextMasterPVField
nextMasterPVField is called for each field in the master as a result of a call to traverseMaster.
create
This is the method for creating a PVCopy instance.
pvMaster
the top level structure managed by the server.
pvRequest
selects the set of subfields desired and options for each field.
structureName
the name for the top level of any PVStructure created.
getPVMaster
Gets the top level structure from pvMaster.
traverseMaster
Traverse all fields of the top level structure of pvMaster. For each field the callback is called.
getStructure
Get the introspection interface for a PVStructure for e copy.
createPVStructure
Create a copy instance. Monitors keep a queue of monitor elements. Since each element needs a PVStructure, multiple top level structures will be created.
getCopyOffset
Given a field in pvMaster. return the offset in copy for the same field. A value of std::string::npos means that the copy does not have this field. Two overloaded methods are provided. The first is called if the field of master is not a structure. The second is for subfields of a structure.
getMasterPVField
Given a offset in the copy get the corresponding field in pvMaster.
initCopy
Initialize the fields in copyPVStructure by giving each field the value from the corresponding field in pvMaster. bitSet will be set to show that all fields are changed. This means that bit set will have the value {0}.
updateCopySetBitSet
Set all fields in copyPVStructure to the value of the corresponding field in pvMaster. Each field that is changed has it's corresponding bit set in bitSet.
updateCopyFromBitSet
For each set bit in bitSet set the field in copyPVStructure to the value of the corresponding field in pvMaster.
updateMaster
For each set bit in bitSet set the field in pvMaster to the value of the corresponding field in copyPVStructure.
getOptions
Get the options for the field at the specified offset. A NULL is returned if no options were specified for the field. If options were specified,PVStructurePtr is a structure with a set of PVString subfields that specify name,value pairs. name is the subField name and value is the subField value.

support for monitor

This consists of two components:

monitor
Used by code that implements pvAccess monitors.
monitorPlugin
Code that provides special semantics for monitors.

monitor

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

MonitorElement holds the data for one element of a monitor queue. It has the fields:

pvStructurePtr
A top level structure with data values at the time the monitors occurs.
changedBitSet
Shows which fields have changed since the previous monitor.
overrunBitSet
Shows which fields have changed more han once since the previous monitor.

monitorElement queue

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

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:

start
Start monitoring. This will result in a an initial monitor that has the current value of all fields.
stop
Stop monitoring.
poll
Called to get a monitor element. If no new elements are available then a null pointer is returned.
release
Release the monitor element. The caller owns the monitor element between the calls to poll and release.

MonitorRequester

This must be implemented by a pvAccess client. It has the methods:

monitorConnect
A monitor has either connected of disconnected.
monitorEvent
A new monitor element is available.
unlisten
The channel is going away. The client cam no longer access the monitor.

monitorPlugin

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

MonitorPlugin must be implemented by the plugin implementation. It has methods:

getName
Get the name of the plugin.
causeMonitor
Should the value of pvField cause a monitor to be raised. pvField and pvTop are fields in the top level structure being monitored. monitorElement has the top level structure for the copy. The implementation should not modify the fields in the structure being monitored. Called with pvTop locked.
monitorDone
Called just before monitorElement will be given to client. The plugin can change the data values and bitSets in monitorElement. Called with pvTop unlocked.
startMonitoring
Monitoring is starting.
stopMonitoring
Monitoring is being stopped.
beginGroupPut
A set of puts is starting. Called with pvTop locked.
endGroupPut
The set of puts is complete. Called with pvTop locked.

MonitorPluginCreator

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:

getName
Get the name of the plugin.
create
Create a new plugin instance. If the arguments are not compatible with the plugin a NULL shared pointer is returned.
pvFieldOptions is a structure with a set of PVString subfields that specify name,value pairs. name is the subField name and value is the subField value.
Note that a plugin will below to a single client.

MonitorPluginManager

MonitorPluginManager has the methods:

get
MonitorPluginManager is a singleton. The first call to get will create the single instance. Further calls will return the single instance.
addPlugin
Add a new plugin.
findPlugin
Find a plugin. A NULL shared pointer is returned if it has not been added.
showNames
Show the names of all plugins that have been added.

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.

monitorPlugin example

Example Plugin Overview

This section describes an example plugin that:

  • Only raises monitors when a field changes value.
    If no plugin is provided the default is to raise a monitor when a put is issued to a field.
  • Optionally a change will not raise a monitor.
    The change will, however, appear if a put to another field raise a monitor.

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 value
In 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);

Example Plugin Code

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);
    }
}