From 3e0282d95619294ca1134f915dd5c7d8d44b5c8f Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Thu, 12 Jul 2018 14:05:15 -0400 Subject: [PATCH] implement copy instead of taking it from pvDataCPP; implement plugin support --- documentation/RELEASE_NOTES.md | 5 + documentation/pvDatabaseCPP.html | 9 +- src/Makefile | 1 + src/copy/Makefile | 15 + src/copy/pv/pvArrayPlugin.h | 100 +++++ src/copy/pv/pvCopy.h | 238 +++++++++++ src/copy/pv/pvDeadbandPlugin.h | 104 +++++ src/copy/pv/pvPlugin.h | 108 +++++ src/copy/pv/pvTimestampPlugin.h | 103 +++++ src/copy/pvArrayPlugin.cpp | 187 +++++++++ src/copy/pvCopy.cpp | 701 +++++++++++++++++++++++++++++++ src/copy/pvDeadbandPlugin.cpp | 125 ++++++ src/copy/pvPlugin.cpp | 33 ++ src/copy/pvTimestampPlugin.cpp | 115 +++++ src/pv/pvDatabase.h | 6 +- src/pvAccess/channelLocal.cpp | 26 +- src/pvAccess/monitorFactory.cpp | 4 +- 17 files changed, 1863 insertions(+), 17 deletions(-) create mode 100644 src/copy/Makefile create mode 100644 src/copy/pv/pvArrayPlugin.h create mode 100644 src/copy/pv/pvCopy.h create mode 100644 src/copy/pv/pvDeadbandPlugin.h create mode 100644 src/copy/pv/pvPlugin.h create mode 100644 src/copy/pv/pvTimestampPlugin.h create mode 100644 src/copy/pvArrayPlugin.cpp create mode 100644 src/copy/pvCopy.cpp create mode 100644 src/copy/pvDeadbandPlugin.cpp create mode 100644 src/copy/pvPlugin.cpp create mode 100644 src/copy/pvTimestampPlugin.cpp diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 8b3effc..d563398 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -1,3 +1,8 @@ +LATEST +====== + +* pvCopy is now implemented in pvDatabaseCPP. The version in pvDatacPP can be deprecated. +* plugin support is implemented. EPICS V4 release 4.6 ==================== diff --git a/documentation/pvDatabaseCPP.html b/documentation/pvDatabaseCPP.html index 89533f4..ecf6880 100644 --- a/documentation/pvDatabaseCPP.html +++ b/documentation/pvDatabaseCPP.html @@ -36,7 +36,7 @@

pvDatabaseCPP

-

Release 4.2-SNAPSHOT - 2016.07.14

+

Master - 2018.07.11

Abstract

@@ -66,8 +66,7 @@ The minimum that an extension must provide is a top level PVStructure and a proc

Overview

pvDatabaseCPP is one of the components of - -EPICS Version 4 +EPICS Version 7

This document is only a guide to help locate code and documentation related to pvDatabaseCPP @@ -78,7 +77,7 @@ It is intended for developers that want to use pvDatabaseCPP.

Developer Guide

A guide for developers is available at +href="https://mrkraimer.github.io/website/developerGuide/developerGuide.html"> developerGuide

@@ -169,7 +168,7 @@ provided by iocCore. The following provide EPICS V4 shell commands:

 pvAccessCPP
-pvaSrv
+qsrv
 pvDatabaseCPP
 
diff --git a/src/Makefile b/src/Makefile index 1eca449..1e4ab7c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -18,6 +18,7 @@ INC += pv/removeRecord.h include $(PVDATABASE_SRC)/database/Makefile include $(PVDATABASE_SRC)/pvAccess/Makefile include $(PVDATABASE_SRC)/special/Makefile +include $(PVDATABASE_SRC)/copy/Makefile pvDatabase_LIBS += $(EPICS_BASE_PVA_CORE_LIBS) pvDatabase_LIBS += $(EPICS_BASE_IOC_LIBS) diff --git a/src/copy/Makefile b/src/copy/Makefile new file mode 100644 index 0000000..233946b --- /dev/null +++ b/src/copy/Makefile @@ -0,0 +1,15 @@ +# This is a Makefile fragment, see ../Makefile + +SRC_DIRS += $(PVDATABASE_SRC)/copy + +INC += pv/pvCopy.h +INC += pv/pvPlugin.h +INC += pv/pvArrayPlugin.h +INC += pv/pvDeadbandPlugin.h +INC += pv/pvTimestampPlugin.h + +LIBSRCS += pvCopy.cpp +LIBSRCS += pvPlugin.cpp +LIBSRCS += pvArrayPlugin.cpp +LIBSRCS += pvDeadbandPlugin.cpp +LIBSRCS += pvTimestampPlugin.cpp diff --git a/src/copy/pv/pvArrayPlugin.h b/src/copy/pv/pvArrayPlugin.h new file mode 100644 index 0000000..977811e --- /dev/null +++ b/src/copy/pv/pvArrayPlugin.h @@ -0,0 +1,100 @@ +/* pvArrayPlugin.h */ +/* + * The License for this software can be found in the file LICENSE that is included with the distribution. + */ + +#ifndef PVARRAYPLUGIN_H +#define PVARRAYPLUGIN_H + +#if defined(_WIN32) && !defined(NOMINMAX) +#define NOMINMAX +#endif + +#include +#include +#include +#include +#include + +namespace epics { namespace pvDatabase{ + +class PVArrayPlugin; +class PVArrayFilter; + +typedef std::tr1::shared_ptr PVArrayPluginPtr; +typedef std::tr1::shared_ptr PVArrayFilterPtr; + + +/** + * @brief A plugin for a filter that gets a sub array from a PVScalarArray. + * + * @author mrk + * @since date 2017.02.23 + */ +class PVArrayPlugin : public PVPlugin +{ +private: + PVArrayPlugin(); +public: + POINTER_DEFINITIONS(PVArrayPlugin); + virtual ~PVArrayPlugin(); + /** + * Factory + */ + static void create(); + /** + * Create a PVFilter. + * @param requestValue The value part of a name=value request option. + * @param pvCopy The PVCopy to which the PVFilter will be attached. + * @param master The field in the master PVStructure to which the PVFilter will be attached + * @return The PVFilter. + * Null is returned if master or requestValue is not appropriate for the plugin. + */ + virtual PVFilterPtr create( + const std::string & requestValue, + const PVCopyPtr & pvCopy, + const epics::pvData::PVFieldPtr & master); +}; + +/** + * @brief A filter that gets a sub array from a PVScalarArray. + */ +class PVArrayFilter : public PVFilter +{ +private: + long start; + long increment; + long end; + epics::pvData::PVScalarArrayPtr masterArray; + + PVArrayFilter(long start,long increment,long end,const epics::pvData::PVScalarArrayPtr & masterArray); +public: + POINTER_DEFINITIONS(PVArrayFilter); + virtual ~PVArrayFilter(); + /** + * Create a PVArrayFilter. + * @param requestValue The value part of a name=value request option. + * @param master The field in the master PVStructure to which the PVFilter will be attached. + * @return The PVFilter. + * A null is returned if master or requestValue is not appropriate for the plugin. + */ + static PVArrayFilterPtr create(const std::string & requestValue,const epics::pvData::PVFieldPtr & master); + /** + * Perform a filter operation + * @param pvCopy The field in the copy PVStructure. + * @param bitSet A bitSet for copyPVStructure. + * @param toCopy (true,false) means copy (from master to copy,from copy to master) + * @return if filter (modified, did not modify) destination. + * Null is returned if master or requestValue is not appropriate for the plugin. + */ + bool filter(const epics::pvData::PVFieldPtr & pvCopy,const epics::pvData::BitSetPtr & bitSet,bool toCopy); + /** + * Get the filter name. + * @return The name. + */ + std::string getName(); +}; + +}} +#endif /* PVARRAYPLUGIN_H */ + diff --git a/src/copy/pv/pvCopy.h b/src/copy/pv/pvCopy.h new file mode 100644 index 0000000..d335524 --- /dev/null +++ b/src/copy/pv/pvCopy.h @@ -0,0 +1,238 @@ +/* pvCopy.h */ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ +/** + * @author Marty Kraimer + * @date 2013.04 + */ +#ifndef PVCOPY_H +#define PVCOPY_H +#include +#include +#include + +#include + +#include +#include + +namespace epics { namespace pvDatabase{ + +class PVCopyTraverseMasterCallback; +typedef std::tr1::shared_ptr PVCopyTraverseMasterCallbackPtr; + +/** + * @brief 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; +}; + + +class PVCopy; +typedef std::tr1::shared_ptr PVCopyPtr; + +struct CopyNode; +typedef std::tr1::shared_ptr CopyNodePtr; + +struct CopyStructureNode; +typedef std::tr1::shared_ptr CopyStructureNodePtr; + + +/** + * @brief Support for subset of fields in a pvStructure. + * + * 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 structure for which a copy of + * an arbitrary 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( + epics::pvData::PVStructurePtr const &pvMaster, + epics::pvData::PVStructurePtr const &pvRequest, + std::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. + */ + epics::pvData::PVStructurePtr getPVMaster(); + /** + * Traverse all the fields in master. + * @param callback This is called for each field on master. + */ + void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback); + /** + * Get the introspection interface for a PVStructure for e copy. + */ + epics::pvData::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. + */ + epics::pvData::PVStructurePtr createPVStructure(); + /** + * 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. + * @param masterPVField The field in master. + */ + std::size_t getCopyOffset(epics::pvData::PVFieldPtr const &masterPVField); + /** + * 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. + * @param masterPVStructure A structure in master that has masterPVField. + * @param masterPVField The field in master. + */ + std::size_t getCopyOffset( + epics::pvData::PVStructurePtr const &masterPVStructure, + epics::pvData::PVFieldPtr const &masterPVField); + /** + * Given an offset in the copy get the corresponding field in pvMaster. + * @param structureOffset The offset in the copy. + */ + epics::pvData::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( + epics::pvData::PVStructurePtr const ©PVStructure, + epics::pvData::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. + * @returns (false,true) if client (should not,should) receive changes. + */ + bool updateCopySetBitSet( + epics::pvData::PVStructurePtr const ©PVStructure, + epics::pvData::BitSetPtr const &bitSet); + /** + * For each set bit in bitSet + * set the field in copyPVStructure to the value of the corresponding field in pvMaster. + * @param copyPVStructure A copy top-level structure. + * @param bitSet A bitSet for copyPVStructure. + * @returns (false,true) if client (should not,should) receive changes. + */ + bool updateCopyFromBitSet( + epics::pvData::PVStructurePtr const ©PVStructure, + epics::pvData::BitSetPtr const &bitSet); + /** + * For each set bit in bitSet + * set the field in pvMaster to the value of the corresponding field in copyPVStructure + * @param copyPVStructure A copy top-level structure. + * @param bitSet A bitSet for copyPVStructure. + */ + void updateMaster( + epics::pvData::PVStructurePtr const ©PVStructure, + epics::pvData::BitSetPtr const &bitSet); + /** + * Get the options for the field at the specified offset. + * @param fieldOffset 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. + */ + epics::pvData::PVStructurePtr getOptions(std::size_t fieldOffset); + /** + * For debugging. + */ + std::string dump(); +private: + + PVCopyPtr getPtrSelf() + { + return shared_from_this(); + } + + epics::pvData::PVStructurePtr pvMaster; + epics::pvData::StructureConstPtr structure; + CopyNodePtr headNode; + epics::pvData::PVStructurePtr cacheInitStructure; + epics::pvData::BitSetPtr ignorechangeBitSet; + + void traverseMaster( + CopyNodePtr const &node, + PVCopyTraverseMasterCallbackPtr const & callback); + void updateCopySetBitSet( + epics::pvData::PVFieldPtr const &pvCopy, + epics::pvData::PVFieldPtr const &pvMaster, + epics::pvData::BitSetPtr const &bitSet); + void updateCopySetBitSet( + epics::pvData::PVFieldPtr const &pvCopy, + CopyNodePtr const &node, + epics::pvData::BitSetPtr const &bitSet); + void updateCopyFromBitSet( + epics::pvData::PVFieldPtr const &pvCopy, + CopyNodePtr const &node, + epics::pvData::BitSetPtr const &bitSet); + void updateMaster( + epics::pvData::PVFieldPtr const &pvCopy, + CopyNodePtr const &node, + epics::pvData::BitSetPtr const &bitSet); + + PVCopy(epics::pvData::PVStructurePtr const &pvMaster); + bool init(epics::pvData::PVStructurePtr const &pvRequest); + epics::pvData::StructureConstPtr createStructure( + epics::pvData::PVStructurePtr const &pvMaster, + epics::pvData::PVStructurePtr const &pvFromRequest); + CopyNodePtr createStructureNodes( + epics::pvData::PVStructurePtr const &pvMasterStructure, + epics::pvData::PVStructurePtr const &pvFromRequest, + epics::pvData::PVStructurePtr const &pvFromField); + void initPlugin( + CopyNodePtr const & node, + epics::pvData::PVStructurePtr const & pvOptions, + epics::pvData::PVFieldPtr const & pvMasterField); + void traverseMasterInitPlugin(); + void traverseMasterInitPlugin(CopyNodePtr const & node); + + CopyNodePtr getCopyOffset( + CopyStructureNodePtr const &structureNode, + epics::pvData::PVFieldPtr const &masterPVField); + bool checkIgnore( + epics::pvData::PVStructurePtr const & copyPVStructure, + epics::pvData::BitSetPtr const & bitSet); + void setIgnore(CopyNodePtr const & node); + CopyNodePtr getMasterNode( + CopyStructureNodePtr const &structureNode, + std::size_t structureOffset); + void dump( + std::string *builder, + CopyNodePtr const &node, + int indentLevel); + friend class PVCopyMonitor; +}; + +}} + +#endif /* PVCOPY_H */ diff --git a/src/copy/pv/pvDeadbandPlugin.h b/src/copy/pv/pvDeadbandPlugin.h new file mode 100644 index 0000000..e088419 --- /dev/null +++ b/src/copy/pv/pvDeadbandPlugin.h @@ -0,0 +1,104 @@ +/* pvDeadbandPlugin.h */ +/* + * The License for this software can be found in the file LICENSE that is included with the distribution. + */ + +#ifndef PVDEADBANDPLUGIN_H +#define PVDEADBANDPLUGIN_H + +#if defined(_WIN32) && !defined(NOMINMAX) +#define NOMINMAX +#endif + +#include +#include +#include +#include +#include + +namespace epics { namespace pvDatabase{ + +class PVDeadbandPlugin; +class PVDeadbandFilter; + +typedef std::tr1::shared_ptr PVDeadbandPluginPtr; +typedef std::tr1::shared_ptr PVDeadbandFilterPtr; + + +/** + * @brief A plugin for a filter that gets a sub array from a PVScalarDeadband. + * + * @author mrk + * @since date 2017.02.23 + */ +class PVDeadbandPlugin : public PVPlugin +{ +private: + PVDeadbandPlugin(); +public: + POINTER_DEFINITIONS(PVDeadbandPlugin); + virtual ~PVDeadbandPlugin(); + /** + * Factory + */ + static void create(); + /** + * Create a PVFilter. + * @param requestValue The value part of a name=value request option. + * @param pvCopy The PVCopy to which the PVFilter will be attached. + * @param master The field in the master PVStructure to which the PVFilter will be attached + * @return The PVFilter. + * Null is returned if master or requestValue is not appropriate for the plugin. + */ + virtual PVFilterPtr create( + const std::string & requestValue, + const PVCopyPtr & pvCopy, + const epics::pvData::PVFieldPtr & master); +}; + +/** + * @brief A Plugin for a filter that gets a sub array from a PVScalarDeadband. + */ +class PVDeadbandFilter : public PVFilter +{ +private: + bool absolute; + double deadband; + epics::pvData::PVScalarPtr master; + bool firstTime; + double lastReportedValue; + + + PVDeadbandFilter(bool absolute,double deadband,epics::pvData::PVScalarPtr const & master); +public: + POINTER_DEFINITIONS(PVDeadbandFilter); + virtual ~PVDeadbandFilter(); + /** + * Create a PVDeadbandFilter. + * @param requestValue The value part of a name=value request option. + * @param master The field in the master PVStructure to which the PVFilter will be attached. + * @return The PVFilter. + * A null is returned if master or requestValue is not appropriate for the plugin. + */ + static PVDeadbandFilterPtr create( + const std::string & requestValue, + const epics::pvData::PVFieldPtr & master); + /** + * Perform a filter operation + * @param pvCopy The field in the copy PVStructure. + * @param bitSet A bitSet for copyPVStructure. + * @param toCopy (true,false) means copy (from master to copy,from copy to master) + * @return if filter (modified, did not modify) destination. + * Null is returned if master or requestValue is not appropriate for the plugin. + */ + bool filter(const epics::pvData::PVFieldPtr & pvCopy,const epics::pvData::BitSetPtr & bitSet,bool toCopy); + /** + * Get the filter name. + * @return The name. + */ + std::string getName(); +}; + +}} +#endif /* PVDEADBANDPLUGIN_H */ + diff --git a/src/copy/pv/pvPlugin.h b/src/copy/pv/pvPlugin.h new file mode 100644 index 0000000..4159c9e --- /dev/null +++ b/src/copy/pv/pvPlugin.h @@ -0,0 +1,108 @@ +/* pvPlugin.h */ +/* + * The License for this software can be found in the file LICENSE that is included with the distribution. + */ +/** + * @author Marty Kraimer + * @date 2017.03 + */ +#ifndef PVPLUGIN_H +#define PVPLUGIN_H + +#if defined(_WIN32) && !defined(NOMINMAX) +#define NOMINMAX +#endif + +#include +#include +#include +#include + +namespace epics { namespace pvDatabase{ + +class PVPlugin; +class PVFilter; +class PVPluginRegistry; + +typedef std::tr1::shared_ptr PVPluginPtr; +typedef std::tr1::shared_ptr PVFilterPtr; +typedef std::map PVPluginMap; + + +/** + * @brief A filter plugin that attaches to a field of a PVStrcture. + * + * PVCopy looks for plugins defined in pvRequest and calls the filter when a pvCopy is updated. + * @author mrk + * @since 2017.03.17 + * + * Interface for a filter plugin for PVCopy. + * + */ + +class epicsShareClass PVPlugin { +public: + POINTER_DEFINITIONS(PVPlugin); + virtual ~PVPlugin() {} + /** + * Create a PVFilter. + * @param requestValue The value part of a name=value request option. + * @param pvCopy The PVCopy to which the PVFilter will be attached. + * @param master The field in the master PVStructure to which the PVFilter will be attached. + * @return The PVFilter. + * A null is returned if master or requestValue is not appropriate for the plugin. + */ + virtual PVFilterPtr create( + const std::string & requestValue, + const PVCopyPtr & pvCopy, + const epics::pvData::PVFieldPtr & master) = 0; +}; + +/** + * @brief A Filter that is called when a copy PVStructure is being updated. + * + * This interface defines a filter to update a copy of a field from a master PVStructure. + * of the data in the master. + */ +class epicsShareClass PVFilter { +public: + POINTER_DEFINITIONS(PVFilter); + virtual ~PVFilter() {} + /** + * Update copy or master. + * @param copy The data for copy. + * @param bitSet The BitSet for copy. + * @param toCopy (true,false) means copy (from master to copy,from copy to master) + * @return (true,false) if filter modified destination. + */ + virtual bool filter(const epics::pvData::PVFieldPtr & copy,const epics::pvData::BitSetPtr & bitSet,bool toCopy) = 0; + /** + * Get the filter name. + * This is the name part of a request name=value pair. + * @return The name. + */ + virtual std::string getName() = 0; +}; +/** + * @brief A registry for filter plugins for PVCopy. + * + */ +class epicsShareClass PVPluginRegistry { +public: + /** + * Register a plugin. + * @param name The name that appears in [name=value] of a field request option. + * @param pvPlugin The implementation for the plugin. + */ + static void registerPlugin(const std::string & name,const PVPluginPtr & pvPlugin); + /** + * Find a plugin. + * @param name The name that appears in [name=value] of a field request option. + * @return The plugin implementation or null if no pluging by that name has been registered. + */ + static PVPluginPtr find(const std::string & name); +}; + +}} + +#endif /* PVPLUGIN_H */ diff --git a/src/copy/pv/pvTimestampPlugin.h b/src/copy/pv/pvTimestampPlugin.h new file mode 100644 index 0000000..89468e9 --- /dev/null +++ b/src/copy/pv/pvTimestampPlugin.h @@ -0,0 +1,103 @@ +/* pvTimeStampPlugin.h */ +/* + * The License for this software can be found in the file LICENSE that is included with the distribution. + */ + +#ifndef PVTIMESTAMPPLUGIN_H +#define PVTIMESTAMPPLUGIN_H + +#if defined(_WIN32) && !defined(NOMINMAX) +#define NOMINMAX +#endif + +#include +#include +#include +#include +#include +#include + +namespace epics { namespace pvDatabase{ + +class PVTimestampPlugin; +class PVTimestampFilter; + +typedef std::tr1::shared_ptr PVTimestampPluginPtr; +typedef std::tr1::shared_ptr PVTimestampFilterPtr; + + +/** + * @brief A plugin for a filter that sets a timeStamp to the current time. + * + * @author mrk + * @since date 2017.03.24 + */ +class PVTimestampPlugin : public PVPlugin +{ +private: + PVTimestampPlugin(); +public: + POINTER_DEFINITIONS(PVTimestampPlugin); + virtual ~PVTimestampPlugin(); + /** + * Factory + */ + static void create(); + /** + * Create a PVFilter. + * @param requestValue The value part of a name=value request option. + * @param pvCopy The PVCopy to which the PVFilter will be attached. + * @param master The field in the master PVStructure to which the PVFilter will be attached + * @return The PVFilter. + * Null is returned if master or requestValue is not appropriate for the plugin. + */ + virtual PVFilterPtr create( + const std::string & requestValue, + const PVCopyPtr & pvCopy, + const epics::pvData::PVFieldPtr & master); +}; + +/** + * @brief A filter that sets a timeStamp to the current time. + */ +class PVTimestampFilter : public PVFilter +{ +private: + epics::pvData::PVTimeStamp pvTimeStamp; + epics::pvData::TimeStamp timeStamp; + bool current; + bool copy; + epics::pvData::PVFieldPtr master; + + + PVTimestampFilter(bool current,bool copy,epics::pvData::PVFieldPtr const & pvField); +public: + POINTER_DEFINITIONS(PVTimestampFilter); + virtual ~PVTimestampFilter(); + /** + * Create a PVTimestampFilter. + * @param requestValue The value part of a name=value request option. + * @param master The field in the master PVStructure to which the PVFilter will be attached. + * @return The PVFilter. + * A null is returned if master or requestValue is not appropriate for the plugin. + */ + static PVTimestampFilterPtr create(const std::string & requestValue,const epics::pvData::PVFieldPtr & master); + /** + * Perform a filter operation + * @param pvCopy The field in the copy PVStructure. + * @param bitSet A bitSet for copyPVStructure. + * @param toCopy (true,false) means copy (from master to copy,from copy to master) + * @return if filter (modified, did not modify) destination. + * Null is returned if master or requestValue is not appropriate for the plugin. + */ + bool filter(const epics::pvData::PVFieldPtr & pvCopy,const epics::pvData::BitSetPtr & bitSet,bool toCopy); + /** + * Get the filter name. + * @return The name. + */ + std::string getName(); +}; + +}} +#endif /* PVTIMESTAMPPLUGIN_H */ + diff --git a/src/copy/pvArrayPlugin.cpp b/src/copy/pvArrayPlugin.cpp new file mode 100644 index 0000000..0d20e81 --- /dev/null +++ b/src/copy/pvArrayPlugin.cpp @@ -0,0 +1,187 @@ +/* pvArrayPlugin.cpp */ +/* + * The License for this software can be found in the file LICENSE that is included with the distribution. + */ + +#include + +#include +#include +#include + +using std::string; +using std::size_t; +using std::cout; +using std::endl; +using std::tr1::static_pointer_cast; +using std::vector; +using namespace epics::pvData; + +namespace epics { namespace pvDatabase{ + +static ConvertPtr convert = getConvert(); +static std::string name("array"); + +PVArrayPlugin::PVArrayPlugin() +{ +} + +PVArrayPlugin::~PVArrayPlugin() +{ +} + +void PVArrayPlugin::create() +{ + PVArrayPluginPtr pvPlugin = PVArrayPluginPtr(new PVArrayPlugin()); + PVPluginRegistry::registerPlugin(name,pvPlugin); +} + +PVFilterPtr PVArrayPlugin::create( + const std::string & requestValue, + const PVCopyPtr & pvCopy, + const PVFieldPtr & master) +{ + return PVArrayFilter::create(requestValue,master); +} + +PVArrayFilter::~PVArrayFilter() +{ +} + +static vector split(string const & colonSeparatedList) { + string::size_type numValues = 1; + string::size_type index=0; + while(true) { + string::size_type pos = colonSeparatedList.find(':',index); + if(pos==string::npos) break; + numValues++; + index = pos +1; + } + vector valueList(numValues,""); + index=0; + for(size_t i=0; igetField()->getType(); + if(type!=scalarArray) { + PVArrayFilterPtr filter = PVArrayFilterPtr(); + return filter; + } + long start =0; + long increment =1; + long end = -1; + vector values(split(requestValue)); + long num = values.size(); + bool ok = true; + string value; + if(num==1) { + value = values[0]; + start = strtol(value.c_str(),0,10); + } else if(num==2) { + value = values[0]; + start = strtol(value.c_str(),0,10); + value = values[1]; + end = strtol(value.c_str(),0,10); + } else if(num==3) { + value = values[0]; + start = strtol(value.c_str(),0,10); + value = values[1]; + increment = strtol(value.c_str(),0,10); + value = values[2]; + end = strtol(value.c_str(),0,10); + } else { + ok = false; + } + if(!ok) { + PVArrayFilterPtr filter = PVArrayFilterPtr(); + return filter; + } + PVArrayFilterPtr filter = + PVArrayFilterPtr( + new PVArrayFilter( + start,increment,end,static_pointer_cast(master))); + return filter; +} + +PVArrayFilter::PVArrayFilter(long start,long increment,long end,const PVScalarArrayPtr & masterArray) +: start(start), + increment(increment), + end(end), + masterArray(masterArray) +{ +} + + +bool PVArrayFilter::filter(const PVFieldPtr & pvCopy,const BitSetPtr & bitSet,bool toCopy) +{ + PVScalarArrayPtr copyArray = static_pointer_cast(pvCopy); + long len = 0; + long start = this->start; + long end = this->end; + long no_elements = masterArray->getLength(); + if(start<0) { + start = no_elements+start; + if(start<0) start = 0; + } + if (end < 0) { + end = no_elements + end; + if (end < 0) end = 0; + + } + if(toCopy) { + if (end >= no_elements) end = no_elements - 1; + if (end - start >= 0) len = 1 + (end - start) / increment; + if(len<=0 || start>=no_elements) { + copyArray->setLength(0); + return true; + } + long indfrom = start; + long indto = 0; + copyArray->setCapacity(len); + if(increment==1) { + copy(*masterArray,indfrom,1,*copyArray,indto,1,len); + } else { + for(long i=0; isetLength(len); + bitSet->set(pvCopy->getFieldOffset()); + return true; + } + if (end - start >= 0) len = 1 + (end - start) / increment; + if(len<=0) return true; + if(no_elements<=end) masterArray->setLength(end+1); + long indfrom = 0; + long indto = start; + if(increment==1) { + copy(*copyArray,indfrom,1,*masterArray,indto,1,len); + } else { + for(long i=0; i +#include +#include +#include + +#include + +#define epicsExportSharedSymbols + +#include + +#include +#include +#include +#include + +using std::tr1::static_pointer_cast; +using std::tr1::dynamic_pointer_cast; +using std::string; +using std::size_t; +using std::cout; +using std::endl; +using std::vector; +using namespace epics::pvData; + +namespace epics { namespace pvDatabase { + +/** + * Convenience method for implementing dump. + * It generates a newline and inserts blanks at the beginning of the newline. + * @param builder The std::string * being constructed. + * @param indentLevel Indent level, Each level is four spaces. + */ +static void newLine(string *buffer, int indentLevel) +{ + *buffer += "\n"; + *buffer += string(indentLevel*4, ' '); +} + +static PVCopyPtr NULLPVCopy; +static StructureConstPtr NULLStructure; +static PVStructurePtr NULLPVStructure; + +struct CopyNode { + CopyNode() + : isStructure(false), + structureOffset(0), + nfields(0) + {} + PVFieldPtr masterPVField; + bool isStructure; + size_t structureOffset; // In the copy + size_t nfields; + PVStructurePtr options; + vector pvFilters; +}; + +static CopyNodePtr NULLCopyNode; + +typedef std::vector CopyNodePtrArray; +typedef std::tr1::shared_ptr CopyNodePtrArrayPtr; + +struct CopyStructureNode : public CopyNode { + CopyNodePtrArrayPtr nodes; +}; + +PVCopyPtr PVCopy::create( + PVStructurePtr const &pvMaster, + PVStructurePtr const &pvRequest, + string const & structureName) +{ + static bool firstTime = true; + if(firstTime) { + firstTime = false; + PVArrayPlugin::create(); + PVTimestampPlugin::create(); + PVDeadbandPlugin::create(); + } + PVStructurePtr pvStructure(pvRequest); + if(structureName.size()>0) { + if(pvStructure->getStructure()->getNumberFields()>0) { + pvStructure = pvRequest->getSubField(structureName); + if(!pvStructure) return NULLPVCopy; + } + } else if(pvRequest->getSubField("field")) { + pvStructure = pvRequest->getSubField("field"); + } + PVCopyPtr pvCopy = PVCopyPtr(new PVCopy(pvMaster)); + bool result = pvCopy->init(pvStructure); + if(!result) return PVCopyPtr(); + pvCopy->traverseMasterInitPlugin(); +//cout << pvCopy->dump() << endl; + return pvCopy; +} + +PVStructurePtr PVCopy::getPVMaster() +{ + return pvMaster; +} + +void PVCopy::traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback) +{ + traverseMaster(headNode,callback); +} + +StructureConstPtr PVCopy::getStructure() +{ + return structure; +} + +PVStructurePtr PVCopy::createPVStructure() +{ + if(cacheInitStructure) { + PVStructurePtr save = cacheInitStructure; + cacheInitStructure.reset(); + return save; + } + PVStructurePtr pvStructure = + getPVDataCreate()->createPVStructure(structure); + return pvStructure; +} + + +size_t PVCopy::getCopyOffset(PVFieldPtr const &masterPVField) +{ + if(!headNode->isStructure) { + CopyNodePtr node = static_pointer_cast(headNode); + if((node->masterPVField.get())==masterPVField.get()) { + return headNode->structureOffset; + } + PVStructure * parent = masterPVField->getParent(); + size_t offsetParent = parent->getFieldOffset(); + size_t off = masterPVField->getFieldOffset(); + size_t offdiff = off -offsetParent; + if(offdiffnfields) return headNode->structureOffset + offdiff; + return string::npos; + } + CopyStructureNodePtr structNode = static_pointer_cast(headNode); + CopyNodePtr node = getCopyOffset(structNode,masterPVField); + if(node) return node->structureOffset; + return string::npos; +} + +size_t PVCopy::getCopyOffset( + PVStructurePtr const &masterPVStructure, + PVFieldPtr const &masterPVField) +{ + CopyNodePtr node; + if(!headNode->isStructure) { + node = static_pointer_cast(headNode); + if(node->masterPVField.get()!=masterPVStructure.get()) return string::npos; + } else { + CopyStructureNodePtr snode = static_pointer_cast(headNode); + node = getCopyOffset(snode,masterPVField); + } + if(!node) return string::npos; + size_t diff = masterPVField->getFieldOffset() + - masterPVStructure->getFieldOffset(); + return node->structureOffset + diff; +} + +PVFieldPtr PVCopy::getMasterPVField(size_t structureOffset) +{ + CopyNodePtr node; + if(!headNode->isStructure) { + node = headNode; + } else { + CopyStructureNodePtr snode = static_pointer_cast(headNode); + node = getMasterNode(snode,structureOffset); + } + if(!node) { + throw std::invalid_argument( + "PVCopy::getMasterPVField: structureOffset not valid"); + } + size_t diff = structureOffset - node->structureOffset; + PVFieldPtr pvMasterField = node->masterPVField; + if(diff==0) return pvMasterField; + PVStructurePtr pvStructure + = static_pointer_cast(pvMasterField); + return pvStructure->getSubField( + pvMasterField->getFieldOffset() + diff); +} + +void PVCopy::initCopy( + PVStructurePtr const ©PVStructure, + BitSetPtr const &bitSet) +{ + for(size_t i=0; i< copyPVStructure->getNumberFields(); ++i) { + bitSet->set(i,true); + } + updateCopyFromBitSet(copyPVStructure,headNode,bitSet); +} + + +bool PVCopy::updateCopySetBitSet( + PVStructurePtr const ©PVStructure, + BitSetPtr const &bitSet) +{ + updateCopySetBitSet(copyPVStructure,headNode,bitSet); + return checkIgnore(copyPVStructure,bitSet); +} + +bool PVCopy::updateCopyFromBitSet( + PVStructurePtr const ©PVStructure, + BitSetPtr const &bitSet) +{ + if(bitSet->get(0)) { + for(size_t i=0; i< copyPVStructure->getNumberFields(); ++i) { + bitSet->set(i,true); + } + } + updateCopyFromBitSet(copyPVStructure,headNode,bitSet); + return checkIgnore(copyPVStructure,bitSet); +} + +void PVCopy::updateMaster( + PVStructurePtr const ©PVStructure, + BitSetPtr const &bitSet) +{ + if(bitSet->get(0)) { + for(size_t i=0; i< copyPVStructure->getNumberFields(); ++i) { + bitSet->set(i,true); + } + } + updateMaster(copyPVStructure,headNode,bitSet); +} + + +PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset) +{ + if(fieldOffset==0) return headNode->options; + CopyNodePtr node = headNode; + while(true) { + if(node->structureOffset==fieldOffset) return node->options; + if(!node->isStructure) return NULLPVStructure; + CopyStructureNodePtr structNode = static_pointer_cast(node); + CopyNodePtrArrayPtr nodes = structNode->nodes; + boolean okToContinue = false; + for(size_t i=0; i< nodes->size(); i++) { + node = (*nodes)[i]; + size_t soff = node->structureOffset; + if(fieldOffset>=soff && fieldOffsetnfields) { + if(fieldOffset==soff) return node->options; + if(!node->isStructure) { + return NULLPVStructure; + } + okToContinue = true; + break; + } + } + if(okToContinue) continue; + throw std::invalid_argument("fieldOffset not valid"); + } +} + +string PVCopy::dump() +{ + string builder; + dump(&builder,headNode,0); + return builder; +} + +void PVCopy::traverseMaster( + CopyNodePtr const &innode, + PVCopyTraverseMasterCallbackPtr const & callback) +{ + CopyNodePtr node = innode; + if(!node->isStructure) { + callback->nextMasterPVField(node->masterPVField); + return; + } + CopyStructureNodePtr structNode = static_pointer_cast(node); + CopyNodePtrArrayPtr nodes = structNode->nodes; + for(size_t i=0; i< nodes->size(); i++) { + node = (*nodes)[i]; + traverseMaster(node,callback); + } +} + +void PVCopy::updateCopySetBitSet( + PVFieldPtr const & pvCopy, + PVFieldPtr const & pvMaster, + BitSetPtr const & bitSet) +{ + if(pvCopy->getField()->getType()!=epics::pvData::structure) { + if(*pvCopy==*pvMaster) return; + pvCopy->copy(*pvMaster); + bitSet->set(pvCopy->getFieldOffset()); + return; + } + PVStructurePtr pvCopyStructure = static_pointer_cast(pvCopy); + PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields(); + for(size_t i=0; igetFieldOffset()); + updateCopySetBitSet(pvCopyFields[i],master,bitSet); + } +} + +void PVCopy::updateCopySetBitSet( + PVFieldPtr const & pvCopy, + CopyNodePtr const & node, + BitSetPtr const & bitSet) +{ + bool result = false; + for(size_t i=0; i< node->pvFilters.size(); ++i) { + PVFilterPtr pvFilter = node->pvFilters[i]; + if(pvFilter->filter(pvCopy,bitSet,true)) result = true; + } + if(!node->isStructure) { + if(result) return; + updateCopySetBitSet(pvCopy,node->masterPVField,bitSet); + return; + } + CopyStructureNodePtr structureNode = static_pointer_cast(node); + PVStructurePtr pvCopyStructure = static_pointer_cast(pvCopy); + PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields(); + for(size_t i=0; inodes)[i],bitSet); + } +} + + +void PVCopy::updateCopyFromBitSet( + PVFieldPtr const & pvCopy, + CopyNodePtr const & node, + BitSetPtr const & bitSet) +{ + bool result = false; + bool update = bitSet->get(pvCopy->getFieldOffset()); + if(update) { + for(size_t i=0; i< node->pvFilters.size(); ++i) { + PVFilterPtr pvFilter = node->pvFilters[i]; + if(pvFilter->filter(pvCopy,bitSet,true)) result = true; + } + } + if(!node->isStructure) { + if(result) return; + PVFieldPtr pvMaster = node->masterPVField; + pvCopy->copy(*pvMaster); + return; + } + CopyStructureNodePtr structureNode = static_pointer_cast(node); + size_t offset = structureNode->structureOffset; + size_t nextSet = bitSet->nextSetBit(offset); + if(nextSet==string::npos) return; + if(offset>=pvCopy->getNextFieldOffset()) return; + PVStructurePtr pvCopyStructure = static_pointer_cast(pvCopy); + PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields(); + for(size_t i=0; inodes)[i],bitSet); + } +} +void PVCopy::updateMaster( + PVFieldPtr const & pvCopy, + CopyNodePtr const & node, + BitSetPtr const & bitSet) +{ + bool result = false; + bool update = bitSet->get(pvCopy->getFieldOffset()); + if(update) { + for(size_t i=0; i< node->pvFilters.size(); ++i) { + PVFilterPtr pvFilter = node->pvFilters[i]; + if(pvFilter->filter(pvCopy,bitSet,false)) result = true; + } + } + if(!node->isStructure) { + if(result) return; + PVFieldPtr pvMaster = node->masterPVField; + pvMaster->copy(*pvCopy); + return; + } + CopyStructureNodePtr structureNode = static_pointer_cast(node); + size_t offset = structureNode->structureOffset; + size_t nextSet = bitSet->nextSetBit(offset); + if(nextSet==string::npos) return; + if(offset>=pvCopy->getNextFieldOffset()) return; + PVStructurePtr pvCopyStructure = static_pointer_cast(pvCopy); + PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields(); + for(size_t i=0; inodes)[i],bitSet); + } +} + +PVCopy::PVCopy( + PVStructurePtr const &pvMaster) +: pvMaster(pvMaster) +{ +} + +void PVCopy::destroy() +{ + headNode.reset(); +} + +bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest) +{ + PVStructurePtr pvMasterStructure = pvMaster; + size_t len = pvRequest->getPVFields().size(); + bool entireMaster = false; + if(len==0) entireMaster = true; + PVStructurePtr pvOptions; + if(len==1) { + pvOptions = pvRequest->getSubField("_options"); + } + if(entireMaster) { + structure = pvMasterStructure->getStructure(); + CopyNodePtr node(new CopyNode()); + headNode = node; + node->options = pvOptions; + node->isStructure = false; + node->structureOffset = 0; + node->masterPVField = pvMasterStructure; + node->nfields = pvMasterStructure->getNumberFields(); + return true; + } + structure = createStructure(pvMasterStructure,pvRequest); + if(!structure) return false; + cacheInitStructure = createPVStructure(); + ignorechangeBitSet = BitSetPtr(new BitSet(cacheInitStructure->getNumberFields())); + headNode = createStructureNodes( + pvMaster, + pvRequest, + cacheInitStructure); + return true; +} + + +StructureConstPtr PVCopy::createStructure( + PVStructurePtr const &pvMaster, + PVStructurePtr const &pvFromRequest) +{ + if(pvFromRequest->getStructure()->getNumberFields()==0) { + return pvMaster->getStructure(); + } + PVFieldPtrArray const &pvFromRequestFields = pvFromRequest->getPVFields(); + StringArray const &fromRequestFieldNames = pvFromRequest->getStructure()->getFieldNames(); + size_t length = pvFromRequestFields.size(); + if(length==0) return NULLStructure; + FieldConstPtrArray fields; fields.reserve(length); + StringArray fieldNames; fieldNames.reserve(length); + for(size_t i=0; igetSubField(fieldName); + if(!pvMasterField) continue; + FieldConstPtr field = pvMasterField->getField(); + if(field->getType()==epics::pvData::structure) { + PVStructurePtr pvRequestStructure = static_pointer_cast( + pvFromRequestFields[i]); + if(pvRequestStructure->getNumberFields()>0) { + StringArray const &names = pvRequestStructure->getStructure()-> + getFieldNames(); + size_t num = names.size(); + if(num>0 && names[0].compare("_options")==0) --num; + if(num>0) { + if(pvMasterField->getField()->getType()!=epics::pvData::structure) continue; + fieldNames.push_back(fieldName); + fields.push_back(createStructure( + static_pointer_cast(pvMasterField), + pvRequestStructure)); + continue; + } + } + } + fieldNames.push_back(fieldName); + fields.push_back(field); + } + size_t numsubfields = fields.size(); + if(numsubfields==0) return NULLStructure; + return getFieldCreate()->createStructure(fieldNames, fields); +} + +CopyNodePtr PVCopy::createStructureNodes( + PVStructurePtr const &pvMasterStructure, + PVStructurePtr const &pvFromRequest, + PVStructurePtr const &pvFromCopy) +{ + PVFieldPtrArray const & copyPVFields = pvFromCopy->getPVFields(); + PVStructurePtr pvOptions = pvFromRequest->getSubField("_options"); + size_t number = copyPVFields.size(); + CopyNodePtrArrayPtr nodes(new CopyNodePtrArray()); + nodes->reserve(number); + for(size_t i=0; igetFieldName(); + PVStructurePtr requestPVStructure = + pvFromRequest->getSubField(fieldName); + PVStructurePtr pvSubFieldOptions = + requestPVStructure->getSubField("_options"); + PVFieldPtr pvMasterField = pvMasterStructure->getSubField(fieldName); + if(!pvMasterField) { + throw std::logic_error("did not find field in master"); + } + size_t numberRequest = requestPVStructure->getPVFields().size(); + if(pvSubFieldOptions) numberRequest--; + if(numberRequest>0) { + nodes->push_back(createStructureNodes( + static_pointer_cast(pvMasterField), + requestPVStructure, + static_pointer_cast(copyPVField))); + continue; + } + CopyNodePtr node(new CopyNode()); + node->options = pvSubFieldOptions; + node->isStructure = false; + node->masterPVField = pvMasterField; + node->nfields = copyPVField->getNumberFields(); + node->structureOffset = copyPVField->getFieldOffset(); + nodes->push_back(node); + } + CopyStructureNodePtr structureNode(new CopyStructureNode()); + structureNode->masterPVField = pvMasterStructure; + structureNode->isStructure = true; + structureNode->nodes = nodes; + structureNode->structureOffset = pvFromCopy->getFieldOffset(); + structureNode->nfields = pvFromCopy->getNumberFields(); + structureNode->options = pvOptions; + return structureNode; +} + +void PVCopy::initPlugin( + CopyNodePtr const & node, + PVStructurePtr const & pvOptions, + PVFieldPtr const & pvMasterField) +{ + PVFieldPtrArray const & pvFields = pvOptions->getPVFields(); + size_t num = pvFields.size(); + vector pvFilters(num); + size_t numfilter = 0; + for(size_t i=0; i(pvFields[i]); + string name = pvOption->getFieldName(); + string value = pvOption->get(); + PVPluginPtr pvPlugin = PVPluginRegistry::find(name); + if(!pvPlugin) { + if(name.compare("ignore")==0) setIgnore(node); + continue; + } + pvFilters[numfilter] = pvPlugin->create(value,shared_from_this(),pvMasterField); + if(pvFilters[numfilter]) ++numfilter; + } + if(numfilter==0) return; + node->pvFilters.resize(numfilter); + for(size_t i=0; ipvFilters[i] = pvFilters[i]; +} + +void PVCopy::traverseMasterInitPlugin() +{ + traverseMasterInitPlugin(headNode); +} + +void PVCopy::traverseMasterInitPlugin(CopyNodePtr const & node) +{ + PVFieldPtr pvField = node->masterPVField; + PVStructurePtr pvOptions = node->options; + if(pvOptions) initPlugin(node,pvOptions,pvField); + if(!node->isStructure) return; + CopyStructureNodePtr structureNode = static_pointer_cast(node); + CopyNodePtrArrayPtr nodes = structureNode->nodes; + for(size_t i=0; i< nodes->size(); i++) { + traverseMasterInitPlugin((*nodes)[i]); + } +} + +CopyNodePtr PVCopy::getCopyOffset( + CopyStructureNodePtr const &structureNode, + PVFieldPtr const &masterPVField) +{ + size_t offset = masterPVField->getFieldOffset(); + CopyNodePtrArrayPtr nodes = structureNode->nodes; + for(size_t i=0; i< nodes->size(); i++) { + CopyNodePtr node = (*nodes)[i]; + if(!node->isStructure) { + size_t off = node->masterPVField->getFieldOffset(); + size_t nextOffset = node->masterPVField->getNextFieldOffset(); + if(offset>= off && offset(node); + CopyNodePtr node = + getCopyOffset(subNode,masterPVField); + if(node) return node; + } + } + return NULLCopyNode; +} + + + +bool PVCopy::checkIgnore( + PVStructurePtr const & copyPVStructure, + BitSetPtr const & bitSet) +{ + if(!ignorechangeBitSet) { + return (bitSet->nextSetBit(0)<0) ? false : true; + } + int32 numFields = copyPVStructure->getNumberFields(); + BitSet temp(numFields); + temp = *bitSet; + int32 ind = 0; + while(true) { + ind = ignorechangeBitSet->nextSetBit(ind); + if(ind<0) break; + temp.clear(ind); + ind++; + if(ind>=numFields) break; + } + return (temp.nextSetBit(0)<0) ? false : true; +} + +void PVCopy::setIgnore(CopyNodePtr const &node) { + ignorechangeBitSet->set(node->structureOffset); + if(node->isStructure) { + CopyStructureNodePtr structureNode = static_pointer_cast(node); + CopyNodePtrArrayPtr nodes = structureNode->nodes; + for(size_t i=0; isize(); ++i) { + CopyNodePtr node = (*nodes)[i]; + setIgnore(node); } + } else { + size_t num = node->masterPVField->getNumberFields(); + if(num>1) { + for(size_t i=1; iset(node->structureOffset+i); + } + } + } +} + + +CopyNodePtr PVCopy::getMasterNode( + CopyStructureNodePtr const &structureNode, + std::size_t structureOffset) +{ + CopyNodePtrArrayPtr nodes = structureNode->nodes; + for(size_t i=0; isize(); ++i) { + CopyNodePtr node = (*nodes)[i]; + if(structureOffset>=(node->structureOffset + node->nfields)) continue; + if(!node->isStructure) return node; + CopyStructureNodePtr subNode = + static_pointer_cast(node); + return getMasterNode(subNode,structureOffset); + } + return NULLCopyNode; +} + +void PVCopy::dump(string *builder,CopyNodePtr const &node,int indentLevel) +{ + newLine(builder,indentLevel); + std::stringstream ss; + ss << (node->isStructure ? "structureNode" : "node"); + ss << " structureOffset " << node->structureOffset; + ss << " nfields " << node->nfields; + *builder += ss.str(); + PVStructurePtr options = node->options; + if(options) { + newLine(builder,indentLevel +1); + *builder += options->getFieldName(); + PVFieldPtrArray pvFields = options->getPVFields(); + for(size_t i=0; i< pvFields.size() ; ++i) { + PVStringPtr pvString = static_pointer_cast(pvFields[i]); + newLine(builder,indentLevel +2); + *builder += pvString->getFieldName() + " " + pvString->get(); + } + } + string name = node->masterPVField->getFullName(); + newLine(builder,indentLevel +1); + *builder += "masterField " + name; + if(node->pvFilters.size()>0) { + newLine(builder,indentLevel +2); + *builder += "filters:"; + for(size_t i=0; i< node->pvFilters.size(); ++i) { + PVFilterPtr pvFilter = node->pvFilters[i]; + *builder += " " + pvFilter->getName(); + } + } + if(!node->isStructure) return; + CopyStructureNodePtr structureNode = + static_pointer_cast(node); + CopyNodePtrArrayPtr nodes = structureNode->nodes; + for(size_t i=0; isize(); ++i) { + CopyNodePtr node = (*nodes)[i]; + if(!node) { + newLine(builder,indentLevel +1); + ss.str(""); + ss << "node[" << i << "] is null"; + *builder += ss.str(); + continue; + } + dump(builder,node,indentLevel+1); + } +} + + +}} diff --git a/src/copy/pvDeadbandPlugin.cpp b/src/copy/pvDeadbandPlugin.cpp new file mode 100644 index 0000000..4f95def --- /dev/null +++ b/src/copy/pvDeadbandPlugin.cpp @@ -0,0 +1,125 @@ +/* pvDeadbandPlugin.cpp */ +/* + * The License for this software can be found in the file LICENSE that is included with the distribution. + */ +#include + +#include +#include +#include + +using std::string; +using std::size_t; +using std::cout; +using std::endl; +using std::tr1::static_pointer_cast; +using std::vector; +using namespace epics::pvData; + +namespace epics { namespace pvDatabase{ + +static ConvertPtr convert = getConvert(); +static std::string name("deadband"); + +PVDeadbandPlugin::PVDeadbandPlugin() +{ +} + +PVDeadbandPlugin::~PVDeadbandPlugin() +{ +} + +void PVDeadbandPlugin::create() +{ + PVDeadbandPluginPtr pvPlugin = PVDeadbandPluginPtr(new PVDeadbandPlugin()); + PVPluginRegistry::registerPlugin(name,pvPlugin); +} + +PVFilterPtr PVDeadbandPlugin::create( + const std::string & requestValue, + const PVCopyPtr & pvCopy, + const PVFieldPtr & master) +{ + return PVDeadbandFilter::create(requestValue,master); +} + +PVDeadbandFilter::~PVDeadbandFilter() +{ +} + +PVDeadbandFilterPtr PVDeadbandFilter::create( + const std::string & requestValue, + const PVFieldPtr & master) +{ + FieldConstPtr field =master->getField(); + Type type = field->getType(); + if(type!=scalar) return PVDeadbandFilterPtr(); + ScalarConstPtr scalar = static_pointer_cast(field); + if(!ScalarTypeFunc::isNumeric(scalar->getScalarType())) return PVDeadbandFilterPtr(); + bool absolute = false; + if(requestValue.find("abs")>=0) { + absolute = true; + } else if(requestValue.find("rel")>=0) { + absolute = false; + } else { + return PVDeadbandFilterPtr(); + } + size_t ind = requestValue.find(':'); + if(ind==string::npos) return PVDeadbandFilterPtr(); + string svalue = requestValue.substr(ind+1); + double deadband = atof(svalue.c_str()); + if(deadband==0.0) return PVDeadbandFilterPtr(); + PVDeadbandFilterPtr filter = + PVDeadbandFilterPtr( + new PVDeadbandFilter( + absolute,deadband,static_pointer_cast(master))); + return filter; +} + +PVDeadbandFilter::PVDeadbandFilter(bool absolute,double deadband,PVScalarPtr const & master) +: absolute(absolute), + deadband(deadband), + master(master), + firstTime(true), + lastReportedValue(0.0) +{ +} + + +bool PVDeadbandFilter::filter(const PVFieldPtr & pvCopy,const BitSetPtr & bitSet,bool toCopy) +{ + if(!toCopy) return false; + double value = convert->toDouble(master); + double diff = value - lastReportedValue; + if(diff<0.0) diff = - diff; + bool report = true; + if(firstTime) { + firstTime = false; + } else if(absolute) { + if(diff1e-20) { + double percent = (diff/last)*100.0; + if(percent(pvCopy); + convert->fromDouble(copy,value); + if(report) { + lastReportedValue = value; + bitSet->set(pvCopy->getFieldOffset()); + } else { + bitSet->clear(pvCopy->getFieldOffset()); + } + return true; +} + +string PVDeadbandFilter::getName() +{ + return name; +} + +}} + diff --git a/src/copy/pvPlugin.cpp b/src/copy/pvPlugin.cpp new file mode 100644 index 0000000..a81378d --- /dev/null +++ b/src/copy/pvPlugin.cpp @@ -0,0 +1,33 @@ +/* pvPlugin.cpp */ +/* + * The License for this software can be found in the file LICENSE that is included with the distribution. + */ +#include + +using namespace epics::pvData; + +namespace epics { namespace pvDatabase{ + +typedef std::map PVPluginMap; + +static PVPluginMap pluginMap; +static Mutex mutex; + +void PVPluginRegistry::registerPlugin(const std::string & name,const PVPluginPtr & pvPlugin) +{ + Lock xx(mutex); + PVPluginMap::iterator iter = pluginMap.find(name); + if(iter!=pluginMap.end()) throw std::logic_error("plugin already registered"); + pluginMap.insert(PVPluginMap::value_type(name,pvPlugin)); +} + +PVPluginPtr PVPluginRegistry::find(const std::string & name) +{ + Lock xx(mutex); + PVPluginMap::iterator iter = pluginMap.find(name); + if(iter!=pluginMap.end()) return (*iter).second; + return PVPluginPtr(); +} + +}} + diff --git a/src/copy/pvTimestampPlugin.cpp b/src/copy/pvTimestampPlugin.cpp new file mode 100644 index 0000000..045c438 --- /dev/null +++ b/src/copy/pvTimestampPlugin.cpp @@ -0,0 +1,115 @@ +/* pvTimestampPlugin.cpp */ +/* + * The License for this software can be found in the file LICENSE that is included with the distribution. + */ + +#include +#include +#include + +using std::string; +using std::size_t; +using std::cout; +using std::endl; +using std::tr1::static_pointer_cast; +using std::vector; +using namespace epics::pvData; + +namespace epics { namespace pvDatabase{ + +static ConvertPtr convert = getConvert(); +static std::string name("timestamp"); + +PVTimestampPlugin::PVTimestampPlugin() +{ +} + +PVTimestampPlugin::~PVTimestampPlugin() +{ +} + +void PVTimestampPlugin::create() +{ + PVTimestampPluginPtr pvPlugin = PVTimestampPluginPtr(new PVTimestampPlugin()); + PVPluginRegistry::registerPlugin(name,pvPlugin); +} + +PVFilterPtr PVTimestampPlugin::create( + const std::string & requestValue, + const PVCopyPtr & pvCopy, + const PVFieldPtr & master) +{ + return PVTimestampFilter::create(requestValue,master); +} + +PVTimestampFilter::~PVTimestampFilter() +{ +} + + +PVTimestampFilterPtr PVTimestampFilter::create( + const std::string & requestValue, + const PVFieldPtr & master) +{ + PVTimeStamp pvTimeStamp; + if(!pvTimeStamp.attach(master)) return PVTimestampFilterPtr(); + bool current = false; + bool copy = false; + if(requestValue.compare("current")==0) { + current = true; + } else if(requestValue.compare("copy")==0){ + copy = true; + } else { + return PVTimestampFilterPtr(); + } + PVTimestampFilterPtr filter = PVTimestampFilterPtr( + new PVTimestampFilter(current,copy,master)); + return filter; +} + +PVTimestampFilter::PVTimestampFilter(bool current,bool copy,PVFieldPtr const & master) +: current(current), + copy(copy), + master(master) +{ +} + + +bool PVTimestampFilter::filter(const PVFieldPtr & pvCopy,const BitSetPtr & bitSet,bool toCopy) +{ + if(current) { + timeStamp.getCurrent(); + if(toCopy) { + if(!pvTimeStamp.attach(pvCopy)) return false; + } else { + if(!pvTimeStamp.attach(master)) return false; + } + pvTimeStamp.set(timeStamp); + bitSet->set(pvCopy->getFieldOffset()); + return true; + } + if(copy) { + if(toCopy) { + if(!pvTimeStamp.attach(master)) return false; + pvTimeStamp.get(timeStamp); + if(!pvTimeStamp.attach(pvCopy)) return false; + pvTimeStamp.set(timeStamp); + bitSet->set(pvCopy->getFieldOffset()); + } else { + if(!pvTimeStamp.attach(pvCopy)) return false; + pvTimeStamp.get(timeStamp); + if(!pvTimeStamp.attach(master)) return false; + pvTimeStamp.set(timeStamp); + } + return true; + } + return false; +} + +string PVTimestampFilter::getName() +{ + return name; +} + +}} + diff --git a/src/pv/pvDatabase.h b/src/pv/pvDatabase.h index 9bc0930..5622d27 100644 --- a/src/pv/pvDatabase.h +++ b/src/pv/pvDatabase.h @@ -65,7 +65,7 @@ typedef std::tr1::weak_ptr PVDatabaseWPtr; * @date 2012.11.20 */ class epicsShareClass PVRecord : - public epics::pvData::PVCopyTraverseMasterCallback, + public PVCopyTraverseMasterCallback, public std::tr1::enable_shared_from_this { public: @@ -210,7 +210,7 @@ public: */ bool addListener( PVListenerPtr const & pvListener, - epics::pvData::PVCopyPtr const & pvCopy); + PVCopyPtr const & pvCopy); /** * @brief PVCopyTraverseMasterCallback method * @@ -226,7 +226,7 @@ public: */ bool removeListener( PVListenerPtr const & pvListener, - epics::pvData::PVCopyPtr const & pvCopy); + PVCopyPtr const & pvCopy); /** diff --git a/src/pvAccess/channelLocal.cpp b/src/pvAccess/channelLocal.cpp index 6d8a459..62dfdee 100644 --- a/src/pvAccess/channelLocal.cpp +++ b/src/pvAccess/channelLocal.cpp @@ -327,6 +327,7 @@ void ChannelGetLocal::get() PVRecordPtr pvr(pvRecord.lock()); if(!pvr) throw std::logic_error("pvRecord is deleted"); try { + bool notifyClient = true; bitSet->clear(); { epicsGuard guard(*pvr); @@ -335,18 +336,29 @@ void ChannelGetLocal::get() pvr->process(); pvr->endGroupPut(); } - pvCopy->updateCopySetBitSet(pvStructure, bitSet); + notifyClient = pvCopy->updateCopySetBitSet(pvStructure, bitSet); } if(firstTime) { bitSet->clear(); bitSet->set(0); firstTime = false; - } - requester->getDone( - Status::Ok, - getPtrSelf(), - pvStructure, - bitSet); + notifyClient = true; + } + if(notifyClient) { + requester->getDone( + Status::Ok, + getPtrSelf(), + pvStructure, + bitSet); + bitSet->clear(); + } else { + BitSetPtr temp(new BitSet(bitSet->size())); + requester->getDone( + Status::Ok, + getPtrSelf(), + pvStructure, + temp); + } if(pvr->getTraceLevel()>1) { cout << "ChannelGetLocal::get" << endl; diff --git a/src/pvAccess/monitorFactory.cpp b/src/pvAccess/monitorFactory.cpp index 6fbb208..dea5816 100644 --- a/src/pvAccess/monitorFactory.cpp +++ b/src/pvAccess/monitorFactory.cpp @@ -266,8 +266,8 @@ void MonitorLocal::releaseActiveElement() { Lock xx(queueMutex); if(state!=active) return; - pvCopy->updateCopyFromBitSet(activeElement->pvStructurePtr,activeElement->changedBitSet); - if(activeElement->changedBitSet->nextSetBit(0)<0) return; + bool result = pvCopy->updateCopyFromBitSet(activeElement->pvStructurePtr,activeElement->changedBitSet); + if(!result) return; MonitorElementPtr newActive = queue->getFree(); if(!newActive) return; BitSetUtil::compress(activeElement->changedBitSet,activeElement->pvStructurePtr);