/* * Copyright information and license terms for this software can be * found in the file LICENSE that is included with the distribution */ #ifndef PV_SHAREDSTATE_H #define PV_SHAREDSTATE_H #include #include #include #include #include #include #include #include namespace epics{namespace pvData{ class Structure; class PVStructure; class BitSet; class Status; }} // epics::pvData namespace epics{namespace pvAccess{ class ChannelProvider; class Channel; class ChannelRequester; struct ChannelBaseRequester; class GetFieldRequester; void providerRegInit(void*); }} // epics::pvAccess namespace pvas { struct SharedChannel; struct SharedMonitorFIFO; struct SharedPut; struct SharedRPC; struct Operation; /** @addtogroup pvas * @{ */ /** A Shared State Process Variable (PV) * * "Shared" in the sense that all clients/subscribers interact with the * same PVStructure. * * @warning For the purposes of locking, this class is an Operation (see @ref provider_roles_requester_locking). * eg. no locks may be held when calling post(), open(), close(), or connect(). * * This class contains a cached PVStructure, which is updated by post(), * also a list of subscribing clients and in-progress network Operations. * * On construction a SharedPV is in a "disconnected" state. * It has no associated PVStructure (or Structure). No type. * A type is associated via the open() method. * After it has been open()'d. Calls to post() may be made. * Calling close() will close all currently opened client channels. * * Client channels, and operations on them, may be initiated at any time (via connect()). * However, operations will not be fully created until open() is called. * * @note A SharedPV does not have a name. Name(s) are associated with a SharedPV * By a Provider (StaticProvider, DynamicProvider, or any epics::pvAccess::ChannelProvider). * These channel names may be seen via connect() * * @see @ref pvas_sharedptr */ class epicsShareClass SharedPV : public pvas::StaticProvider::ChannelBuilder { friend struct SharedChannel; friend struct SharedMonitorFIFO; friend struct SharedPut; friend struct SharedRPC; public: POINTER_DEFINITIONS(SharedPV); struct epicsShareClass Config { bool dropEmptyUpdates; //!< default true. Drop updates which don't include an field values. epics::pvData::PVRequestMapper::mode_t mapperMode; //!< default Mask. @see epics::pvData::PVRequestMapper::mode_t Config(); }; /** Callbacks associated with a SharedPV. * * @note For the purposes of locking, this class is an Requester (see @ref provider_roles_requester_locking) */ struct epicsShareClass Handler { POINTER_DEFINITIONS(Handler); virtual ~Handler() {} virtual void onFirstConnect(const SharedPV::shared_pointer& pv) {} //! Called when the last client disconnects. May close() virtual void onLastDisconnect(const SharedPV::shared_pointer& pv) {} //! Client requests Put virtual void onPut(const SharedPV::shared_pointer& pv, Operation& op) {} //! Client requests RPC virtual void onRPC(const SharedPV::shared_pointer& pv, Operation& op) {} }; /** Allocate a new PV in the closed state. * @param handler Our callbacks. May be NULL. Stored internally as a shared_ptr<> * @param conf Optional. Extra configuration. If !NULL, will be modified to reflect configuration actually used. * @post In the closed state */ static shared_pointer build(const std::tr1::shared_ptr& handler, Config* conf=0); //! A SharedPV which fails all Put and RPC operations. In closed state. static shared_pointer buildReadOnly(Config* conf=0); //! A SharedPV which accepts all Put operations, and fails all RPC operations. In closed state. static shared_pointer buildMailbox(Config* conf=0); private: explicit SharedPV(const std::tr1::shared_ptr& handler, Config* conf); public: virtual ~SharedPV(); //! Replace Handler given with ctor void setHandler(const std::tr1::shared_ptr& handler); Handler::shared_pointer getHandler() const; //! test open-ness. cf. open() and close() bool isOpen() const; //! Shorthand for @code open(value, pvd::BitSet().set(0)) @endcode void open(const epics::pvData::PVStructure& value); //! Begin allowing clients to connect. //! @param value The initial value of this PV. (any pending Get/Monitor operation will complete with this) //! @param valid Only these marked fields are considered to have non-default values. //! @throws std::logic_error if not in the closed state. //! @post In the opened state //! @note Provider locking rules apply (@see provider_roles_requester_locking). void open(const epics::pvData::PVStructure& value, const epics::pvData::BitSet& valid); //! Shorthand for @code open(*pvd::getPVDataCreate()->createPVStructure(type), pvd::BitSet().set(0)) @endcode void open(const epics::pvData::StructureConstPtr& type); //! Force any clients to disconnect, and prevent re-connection //! @param destroy Indicate whether this close() is permanent for clients. //! If destroy=false, the internal client list is retained, and these clients will see a subsequent open(). //! If destory=true, the internal client list is cleared. //! @post In the closed state //! @note Provider locking rules apply (@see provider_roles_requester_locking). virtual void close(bool destroy=false); //! Create a new container which may be used to prepare to call post(). //! This container will be owned exclusively by the caller. std::tr1::shared_ptr build(); //! Update the cached PVStructure in this SharedPV. //! Only those fields marked as changed will be copied in. //! Makes a light-weight copy. //! @pre isOpen()==true //! @throws std::logic_error if !isOpen() //! @note Provider locking rules apply (@see provider_roles_requester_locking). void post(const epics::pvData::PVStructure& value, const epics::pvData::BitSet& changed); //! Update arguments with current value, which is the initial value from open() with accumulated post() calls. void fetch(epics::pvData::PVStructure& value, epics::pvData::BitSet& valid); //! may call Handler::onFirstConnect() //! @note Provider locking rules apply (@see provider_roles_requester_locking). virtual std::tr1::shared_ptr connect( const std::tr1::shared_ptr& provider, const std::string& channelName, const std::tr1::shared_ptr& requester); void setDebug(int lvl); int isDebug() const; private: friend void epics::pvAccess::providerRegInit(void*); static size_t num_instances; weak_pointer internal_self; // const after build() const Config config; mutable epicsMutex mutex; std::tr1::shared_ptr handler; typedef std::list puts_t; typedef std::list rpcs_t; typedef std::list monitors_t; typedef std::list > getfields_t; typedef std::list channels_t; std::tr1::shared_ptr type; puts_t puts; rpcs_t rpcs; monitors_t monitors; getfields_t getfields; channels_t channels; std::tr1::shared_ptr current; //! mask of fields which are considered to have non-default values. //! Used for initial Monitor update and Get operations. epics::pvData::BitSet valid; bool notifiedConn; // whether onFirstConnect() has been, or is being, called int debugLvl; EPICS_NOT_COPYABLE(SharedPV) }; //! An in-progress network operation (Put or RPC). //! Use value(), changed() to see input data, and //! call complete() when done handling. struct epicsShareClass Operation { POINTER_DEFINITIONS(Operation); struct Impl; private: std::tr1::shared_ptr impl; friend struct SharedPut; friend struct SharedRPC; explicit Operation(const std::tr1::shared_ptr impl); public: Operation() {} //!< create empty op for later assignment //! pvRequest blob, may be used to modify handling. const epics::pvData::PVStructure& pvRequest() const; const epics::pvData::PVStructure& value() const; //!< Input data //! Applies to value(). Which fields of input data are actual valid. Others should not be used. const epics::pvData::BitSet& changed() const; //! The name of the channel through which this request was made (eg. for logging purposes). std::string channelName() const; void complete(); //!< shorthand for successful completion w/o data (Put or RPC with void return) //! Complete with success or error w/o data. void complete(const epics::pvData::Status& sts); //! Sucessful completion with data (RPC only) void complete(const epics::pvData::PVStructure& value, const epics::pvData::BitSet& changed); //! Send info message to client. Does not complete(). void info(const std::string&); //! Send warning message to client. Does not complete(). void warn(const std::string&); int isDebug() const; // escape hatch. never NULL std::tr1::shared_ptr getChannel(); // escape hatch. may be NULL std::tr1::shared_ptr getRequester(); bool valid() const; #if __cplusplus>=201103L explicit operator bool() const { return valid(); } #else private: typedef bool (Operation::*bool_type)() const; public: operator bool_type() const { return valid() ? &Operation::valid : 0; } #endif }; } // namespace pvas //! @} #endif // PV_SHAREDSTATE_H