From 3129c2de7556ce1e244d00492bb899138adeaede Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 4 Feb 2020 14:37:33 -0800 Subject: [PATCH] doc sharedpv --- documentation/index.rst | 1 + documentation/sharedpv.rst | 77 ++++++++++++++++++++++++++++++++++++++ src/pvxs/sharedpv.h | 42 ++++++++++++++++++++- 3 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 documentation/sharedpv.rst diff --git a/documentation/index.rst b/documentation/index.rst index d245ac5..836cc91 100644 --- a/documentation/index.rst +++ b/documentation/index.rst @@ -11,6 +11,7 @@ This module is under active development and is only suitable for experimental us nt typedef server + sharedpv source util diff --git a/documentation/sharedpv.rst b/documentation/sharedpv.rst new file mode 100644 index 0000000..a6c64e2 --- /dev/null +++ b/documentation/sharedpv.rst @@ -0,0 +1,77 @@ +SharedPV and StaticSource +------------------------- + +A SharedPV is a single data value which may be accessed by multiple clients through a Server. +It is "shared" in the sense that all clients are manipulating a single variable. + +.. code-block:: c++ + + #include + namespace pvxs { namespace server { ... } } + +Each SharedPV instance has (after open() ) an associated data structure +which is later manipulated with post(). + +A simple usage is: + +.. code-block:: c++ + + using namespace pvxs; + + auto initial = nt::NTScalar{TypeCode::Float64}.create(); + initial["value"] = 42.0; + + auto src(server::StaticSource::build()); + + auto pv(server::SharedPV::buildMailbox()); + pv.open(initial); + + src.add(argv[1], pv); + + auto serv = server::Server::Config::from_env() + .build() + .addSource("box", src.source()); + + serv.run(); + +In this context "mailbox" refers to the default onPut() handler, which simply post()s whatever +the client sends. + +An example of a SharedPV with a custom Put handler + +.. code-block:: c++ + + auto pv(server::SharedPV::buildMailbox()); + + pv.opPut([](server::SharedPV& pv, + std::unique_ptr&& op, + Value&& top) + { + // We decide that .value will be present with .open() + auto val = top["value"]; + // is client trying to change .value ? + if(val.isMarked(true, true)) { + auto val = top["value"].as(); + + // clip to range [0, 10] + top["value"] = std::max(0.0, std::min(val, 10.0)); + } + + // update and send to subscribers + pv.post(std::move(top)); + // notify client of success (or call op->error() if not) + op->reply(); + }); + + auto initial = nt::NTScalar{TypeCode::Float64}.create(); + initial["value"] = 42.0; + + auto src(server::StaticSource::build()); + + pv.open(initial); + +.. doxygenstruct:: pvxs::server::SharedPV + :members: + +.. doxygenstruct:: pvxs::server::StaticSource + :members: diff --git a/src/pvxs/sharedpv.h b/src/pvxs/sharedpv.h index 7466f89..9511dd9 100644 --- a/src/pvxs/sharedpv.h +++ b/src/pvxs/sharedpv.h @@ -21,28 +21,58 @@ struct ChannelControl; struct ExecOp; struct Source; +/** A SharedPV is a single data value which may be accessed by multiple clients through a Server. + * + * On creation a SharedPV has no associated data structure, or data type. + * This is set by calling open() to provide a data type, and initial data values. + * Subsequent calls to post() will update this data structure (and send notifications + * so subscribing clients). + * + * A later call to close() will force disconnect all clients, and discard the data value and type + * in preperation for a later call to open() to set a new data value, which may be of a different type. + * + * The onPut() and onRPC() methods attach functors which will be called each time a Put or RPC + * operation is executed by a client. + */ struct PVXS_API SharedPV { + //! Create a new SharedPV with a Put handler which post() s any client provided Value. static SharedPV buildMailbox(); + //! Create a new SharedPV with a Put handler which rejects any client provided Value. static SharedPV buildReadonly(); ~SharedPV(); inline explicit operator bool() const { return !!impl; } - // call from Source::onCreate() + //! Attach this SharedPV with a new client channel. + //! Not necessary when using StaticSource. + //! eg. could call from Source::onCreate() void attach(std::unique_ptr&& op); + //! Callback when the number of attach()d clients becomes non-zero. void onFirstConnect(std::function&& fn); + //! Callback when the number of attach()d clients becomes zero. void onLastDisconnect(std::function&& fn); + //! Callback when a client executes a new Put operation. void onPut(std::function&&, Value&&)>&& fn); + //! Callback when a client executes an RPC operation. + //! @note RPC operations are allowed event when @code !isOpen() @endcode void onRPC(std::function&&, Value&&)>&& fn); - void open(const Value& prototype); + /** Provide data type and initial value. Allows clients to begin connecting. + * @pre !isOpen() + * @param initial Defines data type, and initial value + */ + void open(const Value& initial); + //! Test whether open() has been called w/o matching close() bool isOpen() const; + //! Reverse the effects of open() and force disconnect any remaining clients. void close(); + //! Update the internal data value, and dispatch subscription updates to any clients. void post(Value&& val); + //! query the internal data value. void fetch(Value& val); struct Impl; @@ -50,6 +80,11 @@ private: std::shared_ptr impl; }; +/** Allow clients to find (through a Server) SharedPV instances by name. + * + * A single PV name may only be added once to a StaticSource. + * However, a single SharedPV may be added multiple times with different PV names. + */ struct PVXS_API StaticSource { static StaticSource build(); @@ -58,9 +93,12 @@ struct PVXS_API StaticSource inline explicit operator bool() const { return !!impl; } + //! Fetch the Source interface, which may be used with Server::addSource() std::shared_ptr source() const; + //! Add a new name through which a SharedPV may be addressed. StaticSource& add(const std::string& name, const SharedPV& pv); + //! Remove a single name StaticSource& remove(const std::string& name); struct Impl;