diff --git a/documentation/copyandmonitor.html b/documentation/copyandmonitor.html new file mode 100644 index 0000000..e4901a0 --- /dev/null +++ b/documentation/copyandmonitor.html @@ -0,0 +1,679 @@ + + + + + + EPICS pvDataCPP: copy and monitor + + + + + + + + + +
+

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 knowlege 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 arbitrarary 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 faculity 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(String const &request);
+     String getMessage();
+};
+
+

An example of how it is used is:

+
+CreateRequestPtr createRequest = CreateRequest::create();
+PVStructurePtr pvRequest = createRequest->createRequest(request);
+if(pvRequest==NULL) {
+    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,
+        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 sructure 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 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 corrseponding field in pvMaster. +
+
updateMaster
+
+ For each set bit in bitSet set the field in pvMaster to the value + of the corrseponding 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 montors.
+
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 inital monitor that has the current value + of all fields. +
+
stop
+
+ Stop monitoring. +
+
poll
+
+ Called to get a monitor element. + If no new elemants are available then a null pointer is returned. +
+
release
+
+ Release the monotor 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 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 String const & getName() = 0;
+}
+
+class MonitorPluginManager
+{
+    static MonitorPluginManagerPtr get();
+    bool addPlugin(
+         String const &pluginName,
+         MonitorPluginCreatorPtr const &creator);
+    MonitorPluginCreatorPtr findPlugin(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 rerurn the single instance. +
+
addPlugin
+
+ Add a new plugin. +
+
findPlugin
+
+ Find a plugin. A NULL shared pointer is reurned if it has not been added. +
+
showNames
+
+ Show the names of all puugins that have been added. +
+
+

NOTE: +Should the method causeMonitor +have arguments pvField and pvTop +be defined so that they can not be modfied. +This would be posssible 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 sufficent. +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
+       struvture display
+    structure voltage
+       double value
+       structure alarm
+       struvture display
+    structure current
+       double value
+       structure alarm
+       struvture 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 monotor +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:

+
+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) {
+                    String value = pvString->get();
+                    if(value.compare("false")==0) raiseMonitor = false;
+                }
+        }
+        return true;
+   }
+   virtual 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 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);
+    }
+}
+
+ + +
+ + diff --git a/pvDataApp/Makefile b/pvDataApp/Makefile index cf66ef2..0cbc9b5 100644 --- a/pvDataApp/Makefile +++ b/pvDataApp/Makefile @@ -109,8 +109,9 @@ INC += bitSetUtil.h LIBSRCS += bitSetUtil.cpp SRC_DIRS += $(PVDATA)/monitor -INC += monitor.h +INC += monitorPlugin.h LIBSRCS += monitor.cpp +LIBSRCS += monitorPlugin.cpp LIBRARY = pvData pvData_LIBS += Com diff --git a/pvDataApp/copy/createRequest.h b/pvDataApp/copy/createRequest.h index 33b5a4c..6d4e429 100644 --- a/pvDataApp/copy/createRequest.h +++ b/pvDataApp/copy/createRequest.h @@ -28,9 +28,16 @@ namespace epics { namespace pvData { +/** + * Class to create pvRequest structures to pass to pvAccess Channel methods. + */ class epicsShareClass CreateRequest { public: POINTER_DEFINITIONS(CreateRequest); + /** + * Create s new instance of CreateRequest + * @returns A shared pointer to the new instance. + */ static CreateRequest::shared_pointer create(); virtual ~CreateRequest() {}; /** diff --git a/pvDataApp/copy/pvCopy.cpp b/pvDataApp/copy/pvCopy.cpp index 0f4a836..a743c7a 100644 --- a/pvDataApp/copy/pvCopy.cpp +++ b/pvDataApp/copy/pvCopy.cpp @@ -127,9 +127,9 @@ PVStructurePtr PVCopy::createPVStructure() return pvStructure; } -PVStructurePtr PVCopy::getOptions( - PVStructurePtr const ©PVStructure,std::size_t fieldOffset) +PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset) { + if(fieldOffset==0) return headNode->options; CopyNodePtr node = headNode; while(true) { if(!node->isStructure) { @@ -158,6 +158,7 @@ PVStructurePtr PVCopy::getOptions( size_t PVCopy::getCopyOffset(PVFieldPtr const &masterPVField) { + if(masterPVField->getFieldOffset()==0) return 0; if(!headNode->isStructure) { CopyMasterNodePtr masterNode = static_pointer_cast(headNode); if((masterNode->masterPVField.get())==masterPVField.get()) { diff --git a/pvDataApp/copy/pvCopy.h b/pvDataApp/copy/pvCopy.h index 9d36ed3..14d2d96 100644 --- a/pvDataApp/copy/pvCopy.h +++ b/pvDataApp/copy/pvCopy.h @@ -24,11 +24,19 @@ namespace epics { namespace pvData{ class PVCopyTraverseMasterCallback; typedef std::tr1::shared_ptr PVCopyTraverseMasterCallbackPtr; +/** + * Callback for traversing master structure + * Must be implemented by code that creates pvCopy. + */ class epicsShareClass PVCopyTraverseMasterCallback { public: POINTER_DEFINITIONS(PVCopyTraverseMasterCallback); virtual ~PVCopyTraverseMasterCallback() {} + /** + * Called once for each field in master. + * @param pvField The field in master. + */ virtual void nextMasterPVField(epics::pvData::PVFieldPtr const &pvField) = 0; }; @@ -44,43 +52,120 @@ struct CopyStructureNode; typedef std::tr1::shared_ptr CopyStructureNodePtr; +/** + * Class that manages one or more PVStructures that holds an arbitrary subset of the fields + * in another PVStructure called master. + */ class epicsShareClass PVCopy : public std::tr1::enable_shared_from_this { public: POINTER_DEFINITIONS(PVCopy); + /** + * Create a new pvCopy + * @param pvMaster The top level sructure for which a copy of + * an arbritary subset of the fields in master will be created and managed. + * @param pvRequest Selects the set of subfields desired and options for each field. + * @param structureName The name for the top level of any PVStructure created. + */ static PVCopyPtr create( PVStructurePtr const &pvMaster, PVStructurePtr const &pvRequest, String const & structureName); virtual ~PVCopy(){} virtual void destroy(); + /** + * Get the top level structure of master + * @returns The master top level structure. + * This should not be modified. + */ PVStructurePtr getPVMaster(); + /** + * Traverse all the fields in master. + * @param callback This is called for each field on master. + */ void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback) { traverseMaster(headNode,callback); } + /** + * Get the introspection interface for a PVStructure for e copy. + */ StructureConstPtr getStructure(); + /** + * Create a copy instance. Monitors keep a queue of monitor elements. + * Since each element needs a PVStructure, multiple top level structures will be created. + */ PVStructurePtr createPVStructure(); + /** + * Given a field in pvMaster. return the offset in copy for the same field. + * A value of String::npos means that the copy does not have this field. + * @param masterPVField The field in master. + */ std::size_t getCopyOffset(PVFieldPtr const &masterPVField); + /** + * Given a field in pvMaster. return the offset in copy for the same field. + * A value of String::npos means that the copy does not have this field. + * @param masterPVStructure A structure in master that has masterPVField. + * @param masterPVField The field in master. + */ std::size_t getCopyOffset( PVStructurePtr const &masterPVStructure, PVFieldPtr const &masterPVField); + /** + * Given a offset in the copy get the corresponding field in pvMaster. + * @param offset The offset in the copy. + */ PVFieldPtr getMasterPVField(std::size_t structureOffset); + /** + * 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. + * @param copyPVStructure A copy top level structure. + * @param bitSet A bitSet for copyPVStructure. + */ void initCopy( PVStructurePtr const ©PVStructure, BitSetPtr const &bitSet); + /** + * 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. + * @param copyPVStructure A copy top level structure. + * @param bitSet A bitSet for copyPVStructure. + */ void updateCopySetBitSet( PVStructurePtr const ©PVStructure, BitSetPtr const &bitSet); + /** + * For each set bit in bitSet + * set the field in copyPVStructure to the value of the corrseponding field in pvMaster. + * @param copyPVStructure A copy top level structure. + * @param bitSet A bitSet for copyPVStructure. + */ void updateCopyFromBitSet( PVStructurePtr const ©PVStructure, BitSetPtr const &bitSet); + /** + * For each set bit in bitSet + * set the field in pvMaster to the value of the corrseponding field in copyPVStructure + * @param copyPVStructure A copy top level structure. + * @param bitSet A bitSet for copyPVStructure. + */ void updateMaster( PVStructurePtr const ©PVStructure, BitSetPtr const &bitSet); - PVStructurePtr getOptions( - PVStructurePtr const ©PVStructure,std::size_t fieldOffset); + /** + * Get the options for the field at the specified offset. + * @param offset the offset in copy. + * @returns A NULL is returned if no options were specified for the field. + * If options were specified,PVStructurePtr is a structures + * with a set of PVString subfields that specify name,value pairs.s + * name is the subField name and value is the subField value. + */ + PVStructurePtr getOptions(std::size_t fieldOffset); + /** + * For debugging. + */ String dump(); private: void dump( diff --git a/pvDataApp/factory/PVField.cpp b/pvDataApp/factory/PVField.cpp index 0e80c08..8a342df 100644 --- a/pvDataApp/factory/PVField.cpp +++ b/pvDataApp/factory/PVField.cpp @@ -129,26 +129,12 @@ namespace format String PVField::getFullName() const { - size_t size=fieldName.size(); - + String ret(fieldName); for(PVField *fld=getParent(); fld; fld=fld->getParent()) { - size+=fld->fieldName.size()+1; + if(fld->getFieldName().size()==0) break; + ret = fld->getFieldName() + '.' + ret; } - - String ret(size, '.'); - size_t pos=size - fieldName.size(); - - ret.replace(pos, String::npos, fieldName); - - for(PVField *fld=getParent(); fld; fld=fld->getParent()) - { - const String& nref = fld->fieldName; - assert(pos >= nref.size()+1); - pos -= nref.size()+1; - ret.replace(pos, String::npos, nref); - } - assert(pos==0); return ret; } diff --git a/pvDataApp/monitor/monitorPlugin.cpp b/pvDataApp/monitor/monitorPlugin.cpp new file mode 100644 index 0000000..4c87a2c --- /dev/null +++ b/pvDataApp/monitor/monitorPlugin.cpp @@ -0,0 +1,82 @@ +/* monitorPlugin.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 + */ + +#include + +namespace epics { namespace pvData { +using std::cout; +using std::endl; + +MonitorPluginManagerPtr MonitorPluginManager::get() +{ + static MonitorPluginManagerPtr pluginManager; + static Mutex mutex; + Lock xx(mutex); + if(pluginManager==NULL) { + pluginManager = MonitorPluginManagerPtr(new MonitorPluginManager()); + } + return pluginManager; +} + +bool MonitorPluginManager::addPlugin( + String const &pluginName, + MonitorPluginCreatorPtr const &creator) +{ + mutex.lock(); + std::list::iterator iter; + for (iter = monitorPluginList.begin();iter!=monitorPluginList.end();iter++) + { + if(*iter==creator) + { + mutex.unlock(); + return false; + } + if(((*iter)->getName().compare(pluginName))==0) + { + mutex.unlock(); + return false; + } + } + monitorPluginList.push_back(creator); + mutex.unlock(); + return true; +} + + +MonitorPluginCreatorPtr MonitorPluginManager::findPlugin( + String const &pluginName) +{ + mutex.lock(); + std::list::iterator iter; + for (iter = monitorPluginList.begin();iter!=monitorPluginList.end();++iter) + { + if(((*iter)->getName().compare(pluginName))==0) + { + mutex.unlock(); + return *iter; + } + } + mutex.unlock(); + return MonitorPluginCreatorPtr(); +} + +void MonitorPluginManager::showNames() +{ + mutex.lock(); + std::list::iterator iter; + for (iter = monitorPluginList.begin();iter!=monitorPluginList.end();++iter) + { + std::cout << (*iter)->getName() << std::endl; + } + mutex.unlock(); +} + + +}} diff --git a/pvDataApp/monitor/monitorPlugin.h b/pvDataApp/monitor/monitorPlugin.h new file mode 100644 index 0000000..b106c24 --- /dev/null +++ b/pvDataApp/monitor/monitorPlugin.h @@ -0,0 +1,177 @@ +/* monitorPlugin.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 + */ +#ifndef MONITORPLUGIN_H +#define MONITORPLUGIN_H + +#include +#include +#include +#include +#include + +#include + +namespace epics { namespace pvData { + +/** + * typedef for a pointer to a MonitorPlugin + */ +class MonitorPlugin; +typedef std::tr1::shared_ptr MonitorPluginPtr; + + +/** + * typedef for a pointer to a MonitorPluginCreator + */ +class MonitorPluginCreator; +typedef std::tr1::shared_ptr MonitorPluginCreatorPtr; + + +/** + * typedef for a pointer to a MonitorPluginManager + */ +class MonitorPluginManager; +typedef std::tr1::shared_ptr MonitorPluginManagerPtr; + + +/** A plugin for raising monitors; + * This is for use by pvAccess servers that support monitors. + * Since the interface has only a dependence on pvData it + * can be used for other purposes. + * A monitor is assumed to be associated with a field of a top level + * structure. + */ +class MonitorPlugin +{ +public: + virtual ~MonitorPlugin(){} + /** + * getName + * @returns The name of the plugin + */ + virtual String const & getName() = 0; + /** + * Should a monitor be raised? + * @param pvField The field being monitored. + * @param pvTop The top level sructure in which the field resides. + * @param monitorElement The client data and bitSets. + * @returns true or false. + * True is returned if the change to this field should cause a monitor. + * False is returned in a change only to this field should not cause a + * monitor. + */ + virtual bool causeMonitor( + PVFieldPtr const &pvField, + PVStructurePtr const &pvTop, + MonitorElementPtr const &monitorElement) = 0; + /** + * A monitor will be sent to the client. + * @param pvField The copy of the field being monitored. + * The plugin can modify the data. + * @param pvTop The top level sructure in which the field resides. + * @param monitorElement The data for the client. + * The plugin is allowed to change the data values. + */ + virtual void monitorDone( + MonitorElementPtr const &monitorElement) + {} + /** + * Begin monitoring + */ + virtual void startMonitoring(){} + /** + * Stop monitoring + */ + virtual void stopMonitoring(){} + /** + * Begin a set of puts. + */ + virtual void beginGroupPut() {}; + /** + * End a set of puts. + */ + virtual void endGroupPut() {}; +}; + +/** A class that creates a plugin. + * Normlly a plugin is created for a single client. + */ +class MonitorPluginCreator +{ +public: + virtual ~MonitorPluginCreator() {} + /** + * Create a monitor plugin. + * @param field The introspection interface for the field monitored. + * @param top The introspsction interface for the client structure. + * @param pvFieldOptions The options the client requested. + * The structure has a set of PVString subfields. + * The options are a set of name,value pairs. + * The subfield name is the name and the subfield value is the value. + * @returns shared pointer to a MonitorPluginCreator. + */ + virtual MonitorPluginPtr create( + FieldConstPtr const &field, + StructureConstPtr const &top, + PVStructurePtr const &pvFieldOptions) = 0; + /** + * getName + * @returns The name of the plugin + */ + virtual String const & getName() = 0; +}; + + +/** + * This manages a set of monitor plugins. + * @author mrk + */ +class epicsShareClass MonitorPluginManager +{ +public: + POINTER_DEFINITIONS(MonitorPluginManager); + /** + * Factory to get the manager. + * @return shared pointer to manager. + */ + static MonitorPluginManagerPtr get(); + /** destructor + */ + ~MonitorPluginManager(){} + /* add plugin + * @param pluginName The name of the plugin. + * @param creator The creator. + * @returns true or false + * false is returned if a plugin with that name is already present + */ + bool addPlugin( + String const &pluginName, + MonitorPluginCreatorPtr const &creator); + /* find plugin + * + * @param plugin name + * @returns share pointer to plugin creator. + * If a plugin with that name is not found NULL is returned. + */ + MonitorPluginCreatorPtr findPlugin(String const &pluginName); + /* showNames + * + */ + void showNames(); +private: + MonitorPluginManager(){} + std::list monitorPluginList; + epics::pvData::Mutex mutex; +}; + + + +}} +#endif /* MONITORPLUGIN_H */ diff --git a/testApp/pv/testPVData.cpp b/testApp/pv/testPVData.cpp index e37069a..0468293 100644 --- a/testApp/pv/testPVData.cpp +++ b/testApp/pv/testPVData.cpp @@ -413,11 +413,20 @@ pvTop->toString(&buffer); cout << buffer.c_str() << endl; cout << pvTop->dumpValue(cout) << endl; +String subName("record._options.process"); +PVFieldPtr pvField = pvTop->getSubField(subName); +String fieldName = pvField->getFieldName(); +String fullName = pvField->getFullName(); +cout << "fieldName " << fieldName << " fullName " << fullName << endl; + + testOk1(fieldName.compare("process")==0); + testOk1(fullName.compare(subName)==0); + } MAIN(testPVData) { - testPlan(187); + testPlan(189); fieldCreate = getFieldCreate(); pvDataCreate = getPVDataCreate(); standardField = getStandardField();