Client API¶
pvxs::client::Context
represents a PVA protocol client.
#include <pvxs/client.h>
namespace pvxs { namespace client { ... } }
Configuration¶
The recommended starting point is creating new context configured from EPICS_PVA_*
Environment variables.
Use pvxs::client::Context::fromEnv()
.
- EPICS_PVA_ADDR_LIST
A list of destination addresses to which UDP search messages will be sent. May contain unicast and/or broadcast addresses.
- EPICS_PVA_AUTO_ADDR_LIST
If “YES” then all local broadcast addresses will be implicitly appended to $EPICS_PVA_ADDR_LIST. “YES” if unset.
- EPICS_PVA_NAME_SERVERS
A list of the addresses of listening TCP sockets to which search messages will be sent.
- EPICS_PVA_BROADCAST_PORT
Default UDP port to which UDP searches will be sent. 5076 if unset.
- EPICS_PVA_CONN_TMO
Inactivity timeout for TCP connections. For compatibility with pvAccessCPP a multiplier of 4/3 is applied. So a value of 30 results in a 40 second timeout. Prior to 0.2.0 this variable was ignored.
New in version 0.3.0: EPICS_PVA_ADDR_LIST may contain IPv4 multicast, and IPv6 uni/multicast addresses.
New in version 0.2.0: Added EPICS_PVA_NAME_SERVERS.
New in version 0.2.0: Prior to 0.2.0 EPICS_PVA_CONN_TMO was ignored.
using namespace pvxs;
// Context configured from process environment
client::Context ctxt = client::Context::fromEnv();
Programmatic configuration can be accomplished by explicitly filling in a pvxs::client::Config
.
Making Requests¶
A pvxs::client::Context
instance is the entry point for all client network operations.
Begin by calling one of the info(), get(), put(), rpc(), or monitor() methods.
Each of these methods returns a *Builder object which can
be used to provide additional configuration in what are in
effected named arguments.
-
class Context¶
An independent PVA protocol client instance
Typically created with Config::build()
Context ctxt(Config::from_env().build());
Public Functions
-
explicit Context(const Config&)¶
Create/allocate a new client with the provided config. Config::build() is a convenient shorthand.
-
void close()¶
Force close the client.
~Context() will close() automatically. So an explicit call is optional.
Aborts/interrupts all in progress network operations. Blocks until any in-progress callbacks have completed.
- Since
1.1.0
-
inline GetBuilder get(const std::string &pvname)¶
Request the present value of a PV
Simple blocking
Context ctxt(...); auto result = ctxt.get("pv:name") .exec() ->wait();
With completion callback
See GetBuilder and Get/Info for details.Context ctxt(...); auto op = ctxt.get("pv:name") .result([](Result&& prototype){ std::cout<<prototype(); }) .exec(); // store op until completion
-
inline GetBuilder info(const std::string &pvname)¶
Request type information from PV. Results in a Value with no marked fields.
Simple blocking
Context ctxt(...); auto result = ctxt.info("pv:name") .exec() ->wait();
With completion callback
Context ctxt(...); auto op = ctxt.info("pv:name") .result([](Result&& prototype){ std::cout<<prototype(); }) .exec(); // store op until completion
See GetBuilder and Get/Info for details.
-
inline PutBuilder put(const std::string &pvname)¶
Request change/update of PV.
Assign certain values to certain fields and block for completion.
Context ctxt(...); auto result = ctxt.put("pv:name") .set("value", 42) .exec() ->wait();
Alternately, and more generally, using a .build() callback and use .result() callback for completion notification.
Context ctxt(...); auto op = ctxt.put("pv:name") .build([](Value&& prototype) -> Value { auto putval = prototype.cloneEmpty(); putval["value"] = 42; return putval; }) .result([](Result&& prototype){ try { // always returns empty Value on success prototype(); std::cout<<"Success"; }catch(std::exception& e){ std::cout<<"Error: "<<e.what(); } }) .exec(); // store op until completion
See PutBuilder and Put for details.
-
inline RPCBuilder rpc(const std::string &pvname, const Value &arg)¶
Execute “stateless” remote procedure call operation.
Simple blocking
Value arg = ...; Context ctxt(...); auto result = ctxt.rpc("pv:name", arg) .arg("blah", 5) .arg("other", "example") .exec() ->wait();
With completion callback
Value arg = ...; Context ctxt(...); auto op = ctxt.rpc("pv:name", arg) .result([](Result&& prototype){ std::cout<<prototype(); }) .exec(); // store op until completion
See RPCBuilder and RPC for details.
-
inline MonitorBuilder monitor(const std::string &pvname)¶
Create a new subscription for changes to a PV.
MPMCFIFO<std::shared_ptr<Subscription>> workqueue(42u); auto sub = ctxt.monitor("pv:name") .event([&workqueue](Subscription& sub) { // Subscription queue becomes not empty. // Avoid I/O on PVXS worker thread, // delegate to application thread workqueue.push(sub.shared_from_this()); }) .exec(); while(auto sub = workqueue.pop()) { // could workqueue.push(nullptr) to break try { Value update = sub.pop(); if(!update) continue; // Subscription queue empty, wait for another event callback std::cout<<update<<"\n"; } catch(std::exception& e) { // may be Connected(), Disconnect(), Finished(), or RemoteError() std::cerr<<"Error "<<e.what()<<"\n"; } // queue not empty, reschedule workqueue.push(sub); } // store op until completion
See MonitorBuilder and Monitor for details.
-
inline ConnectBuilder connect(const std::string &pvname)¶
Manually add, and maintain, an entry in the Channel cache.
This optional method may be used when it is known that a given PV will be needed in future. ConnectBuilder::onConnect() and ConnectBuilder::onDisconnect() may be used to get asynchronous notification, or the returned Connect object may be used to poll Channel (dis)connect state.
- Since
0.2.0
-
inline DiscoverBuilder discover(std::function<void(const Discovered&)> &&fn)¶
Discover the presence or absence of Servers.
Combines information from periodic Server Beacon messages, and optionally Discover pings, to provide notice when PVA servers appear or disappear from attached networks.
Note that a discover() Operation will never complete with a Value, and so can only end with a timeout or cancellation.
Context ctxt(...); auto op = ctxt.discover([](const Discovered& evt) { std::cout<<evt<<std::endl; }) .pingAll(false) // implied default .exec(); op->wait(10.0); // wait 10 seconds, will always timeout.
- Since
0.3.0
-
void hurryUp()¶
Request prompt search of any disconnected channels.
This method is recommended for use when executing a batch of operations.
Context ctxt = ...; std::vector<std::string> pvnames = ...; std::vector<Operation> ops(pvnames.size()); // Initiate all operations for(size_t i=0; i<pvname.size(); i++) ops[i] = ctxt.get(pvnames[i]).exec(); ctxt.hurryUp(); // indicate end of batch for(size_t i=0; i<pvname.size(); i++) ... = ops[i].wait(); // wait for results
Optional. Equivalent to detection of a new server. This method has no effect if called more often than once per 30 seconds.
Public Static Functions
-
static Context fromEnv()¶
Create new client context based on configuration from $EPICS_PVA* environment variables.
Shorthand for
.Config::fromEnv().build()
- Since
0.2.1
-
static inline RequestBuilder request()¶
Compose a pvRequest independently of a network operation.
This is not a network operation.
Use of request() is optional. pvRequests can be composed with individual network operation Builders.
Value pvReq = Context::request() .pvRequest("field(value)field(blah)") .record("pipeline", true) .build();
-
explicit Context(const Config&)¶
Get/Info¶
pvxs::client::Context::info()
and pvxs::client::Context::get()
return a
pvxs::client::GetBuilder
to prepare either a get() or info() (GET_FIELD)
operation. The practical difference being that info() yields a Value
which will never have any fields marked.
-
class GetBuilder : public pvxs::client::detail::CommonBuilder<GetBuilder, detail::CommonBase>¶
Prepare a remote GET or GET_FIELD (info) operation. See Context::get()
Put¶
pvxs::client::Context::put()
returns a
pvxs::client::PutBuilder
to prepare a put() operation.
In the generic form of put(), the field values to sent have
to be passed to the builder callback.
This is necessary as the server mandated PV type definition
is not known when a Put operation is initiated.
Additionally, a put operation will by default first fetch the present value of the PV and provide it to the builder callback. This allows eg. to perform string to index lookup when writing to an NTEnum.
-
class PutBuilder : public pvxs::client::detail::CommonBuilder<PutBuilder, detail::PRBase>¶
Prepare a remote PUT operation See Context::put()
Public Functions
-
inline PutBuilder &fetchPresent(bool f)¶
If fetchPresent is true (the default). Then the Value passed to the build() callback will be initialized with a previous value for this PV.
This will be necessary for situation like NTEnum to fetch the choices list. But may be undesirable when writing to array fields to avoid the expense of fetching a copy of the array to be overwritten.
-
template<typename T>
inline PutBuilder &set(const std::string &name, const T &val, bool required = true)¶ Utilize default .build() to assign a value to the named field.
- Parameters:
name – The field name to attempt to assign.
val – The value to assign. cf. Value::from()
required – Whether to fail if this value can not be assigned to this field.
-
inline PutBuilder &build(std::function<Value(Value&&)> &&cb)¶
Provide the builder callback.
Once the PV type information is received from the server, this function will be responsible for populating a Value which will actually be sent.
-
inline PutBuilder &result(std::function<void(Result&&)> &&cb)¶
Provide the operation result callback. This callback will be passed a Result which is either an empty Value (success) or an exception on error.
-
inline PutBuilder &fetchPresent(bool f)¶
RPC¶
pvxs::client::Context::rpc()
returns a
pvxs::client::RPCBuilder
to prepare an rpc() operation.
There are two ways to prepare the arguments of an RPC operation.
The recommended way is to use the one argument form of rpc()
and zero or more calls to pvxs::client::RPCBuilder::arg()
to set argument names and values.
These will be combined into a single argument structure
conforming to the pvxs::nt::NTURI
convention.
Alternately, the two argument form of rpc() accepts are arbitrary Value which is passed to the server unaltered.
-
class RPCBuilder : public pvxs::client::detail::CommonBuilder<RPCBuilder, detail::PRBase>¶
Prepare a remote RPC operation. See Context::rpc()
Public Functions
-
inline RPCBuilder &result(std::function<void(Result&&)> &&cb)¶
Callback through which result Value or an error will be delivered. The functor is stored in the Operation returned by exec().
-
template<typename T>
inline RPCBuilder &arg(const std::string &name, const T &val)¶ Provide argument value.
- Parameters:
name – Argument name
val – The value to assign. cf. Value::from()
-
inline RPCBuilder &result(std::function<void(Result&&)> &&cb)¶
Operation and Result¶
The exec() method of the *Builder objects returns a shared_ptr
to an pvxs::client::Operation
handle, which represents the
in-progress network operation. The caller must retain this
handle until completion, or the operation will be implicitly
cancelled.
When an Operation completes, a pvxs::client::Result
is passed
to the result() callback. This object holds either a pvxs::Value
if the operation succeeded, or an exception.
-
struct Operation¶
Handle for in-progress operation.
Public Types
Public Functions
-
virtual const std::string &name() = 0¶
PV name.
-
virtual bool cancel() = 0¶
Explicitly cancel a pending operation. Blocks until an in-progress callback has completed.
- Returns:
true if the operation was canceled, or false if already complete.
-
virtual Value wait(double timeout) = 0¶
Block until Operation completion.
As an alternative to a .result() callback, wait for operation completion, timeout, or interruption (via. interrupt() ).
- Parameters:
timeout – Time to wait prior to throwing TimeoutError. cf. epicsEvent::wait(double)
- Throws:
Timeout – Timeout exceeded
Interrupted – interrupt() called
- Returns:
result Value. Always empty/invalid for put()
-
inline Value wait()¶
wait(double) without a timeout
-
virtual void interrupt() = 0¶
Queue an interruption of a wait() or wait(double) call.
-
virtual const std::string &name() = 0¶
Monitor¶
pvxs::client::Context::monitor()
returns a
pvxs::client::MonitorBuilder
to prepare a MONITOR operation.
The result of this preparation is a pvxs::client::Subscription
which represents the in-progress network operation.
The caller must retain this handle or the operation will be implicitly cancelled.
Until cancelled, a Subscription will attempt to (re)connect to the requested PV.
A Subscription object allows access to a queue of data updates as Value and events/errors as exceptions.
The pvxs::client::Subscription::pop()
method will remove an entry from the queue, or return an empty/invalid Value.
Data updates are returned as a valid Value.
Events/errors are thrown as exceptions.
An pvxs::client::MonitorBuilder::event()
callback is only invoked when the
Subscription queue becomes not-empty.
It will not be called again until pvxs::client::Subscription::pop()
has returned
an empty/invliad Value.
The special exceptions pvxs::client::Connected
, pvxs::client::Disconnect
, and pvxs::client::Finished
have specific meaning when thrown by pvxs::client::Subscription::pop()
.
- Connected
Depending on
pvxs::client::MonitorBuilder::maskConnected()
(default true). Queued when a Subscription becomes connected. The Connected object include the server host:port as well as a (client) time of connection.- Disconnect
Depending on
pvxs::client::MonitorBuilder::maskDisconnected()
(default false). Queued when a Subscription becomes disconnected.- Finished
Depending on
pvxs::client::MonitorBuilder::maskDisconnected()
(default false). Queued when the server indicates that Subscription will receive no more date updates as a normal completion. Finished is a sub-class of Disconnect.
There are several aspects of a Subscription which may be selected through the MonitorBuilder.
The special pvxs::client::Connected
and pvxs::client::Disconnect
“errors” may appear in
the event queue
-
class MonitorBuilder : public pvxs::client::detail::CommonBuilder<MonitorBuilder, detail::CommonBase>¶
Prepare a remote subscription See Context::monitor()
Public Functions
-
inline MonitorBuilder &event(std::function<void(Subscription&)> &&cb)¶
Install FIFO not-empty event callback.
This functor will be called each time the Subscription event queue becomes not empty. A Subscription becomes empty when Subscription::pop() returns an empty/invalid Value.
The functor is stored in the Subscription returned by exec().
-
inline MonitorBuilder &maskConnected(bool m = true)¶
Include Connected exceptions in queue (default false).
-
inline MonitorBuilder &maskDisconnected(bool m = true)¶
Include Disconnected exceptions in queue (default true).
-
std::shared_ptr<Subscription> exec()¶
Submit request to subscribe.
-
inline MonitorBuilder &event(std::function<void(Subscription&)> &&cb)¶
-
struct Subscription¶
Handle for monitor subscription.
Public Functions
-
inline const std::string &name()¶
PV name.
-
virtual bool cancel() = 0¶
Explicitly cancel a active subscription. Blocks until any in-progress callback has completed.
-
virtual void pause(bool p = true) = 0¶
Ask a server to stop (true) or re-start (false), sending updates to this Subscription.
-
inline void resume()¶
Shorthand for.
pause(false)
-
virtual Value pop() = 0¶
De-queue update from subscription event queue.
If the queue is empty, return an empty/invalid Value (Value::valid()==false). A data update is returned as a Value. An error or special event is thrown.
std::shared_ptr<Subscription> sub(...); try { while(auto update = sub.pop()) { // have data update ... } // queue empty } catch(Connected& con) { // if MonitorBuilder::maskConnected(false) } catch(Finished& con) { // if MonitorBuilder::maskDisconnected(false) } catch(Disconnect& con) { // if MonitorBuilder::maskDisconnected(false) } catch(RemoteError& con) { // error message from server } catch(std::exception& con) { // client side error }
- Throws:
Connected – (depending on MonitorBuilder::maskConnected())
Disconnect – (depending on MonitorBuilder::maskDisconnect())
Finished – (depending on MonitorBuilder::maskDisconnect())
RemoteError – For server signaled errors
std::exception – For client side failures.
- Returns:
A valid Value until the queue is empty
-
virtual void stats(SubscriptionStat&, bool reset = false) = 0¶
Poll statistics
- Since
1.1.0
Return strong internal reference which will not prevent implicit cancellation when the last reference returned by exec() is released.
- Since
0.2.0
-
inline const std::string &name()¶
Connect¶
Request that a Channel be created now which may be used by other Operations, allowing them to complete more quickly.
-
class ConnectBuilder¶
- Since
0.2.0
Public Functions
-
inline ConnectBuilder &onConnect(std::function<void()> &&cb)¶
Handler to be invoked when channel becomes connected.
-
inline ConnectBuilder &onDisconnect(std::function<void()> &&cb)¶
Handler to be invoked when channel becomes disconnected.
-
inline ConnectBuilder &syncCancel(bool b)¶
Controls whether Connect::~Connect() synchronizes.
When true (the default) explicit or implicit cancel blocks until any in progress callback has completed. This makes safe some use of references in callbacks.
- Since
0.2.0
-
struct Connect¶
Handle for entry in Channel cache.
Public Functions
-
virtual const std::string &name() const = 0¶
Name passed to Context::connect()
-
virtual bool connected() const = 0¶
Poll (momentary) connection status.
-
virtual const std::string &name() const = 0¶
Threading¶
A client Context will invoke user callback functions from one or more internal worker threads. However, it is guaranteed that callbacks relating to a given Channel (PV name + priority) will never be executed concurrently. This implies that callbacks for a single operation will also never be executed concurrently.
User code must avoid doing unnecessary work from within a callback function as this will prevent other callbacks from be executed.
Ownership¶
User provided callbacks are in the form of std::function which may,
directly or indirectly, store shared_ptr<>
instances.
The returned Operation and Subscription instances should be treated as
storing the std::function instance(s) and thus any shared_ptr<>
captured in them.
Therefore, in order to avoid a resource leak, it is advisable to consider whether a returned Operation or Subscription may participate in a reference loop.
For example, the following creates a reference loop between the Operation instance and the “mystruct” instance.
struct mystruct {
std::shared_ptr<Operation> op; // <-- Danger!
};
auto myptr = std::make_shared<mystruct>();
Context ctxt(...);
myptr->op = ctxt.get("pv:name")
.result([myptr](Result&& result) { // <-- Danger!
})
.exec();
While such loops can be explicitly broken (eg. by NULLing ‘myptr->op’) it is strongly recommended to avoid such situations as unexpected (exceptional) conditions can easily lead to resource leaks which are quite difficult to detect and isolate.
Where possible it is recommended to capture weak_ptr<> instances.
pvRequest¶
All operations except info() (GET_FIELD) take a Value which servers may use to modify or qualify the operation. Conventionally, the two ways this may be done is to provide a mask to limit the (sub)fields for which data is returned. Secondly, to provide certain well-known options to modify the operation.
The pvRequest conditions may be specified in three ways through the methods of pvxs::client::detail::CommonBuilder
exposed through the individual *Builder types.
- Programmatic
The field() and record() methods.
- Textual
The pvRequest() method accepts a string which is parsed into calls to the field() and record() methods. These two approaches may be intermixed.
- Fallback
The rawRequest() method accepts an externally assembled Value which is sent without modification.
-
template<typename SubBuilder, typename Base>
class CommonBuilder : public Base¶ Options common to all operations.
Public Functions
-
inline SubBuilder &field(const std::string &fld)¶
Add field to pvRequest blob. A more efficient alternative to
pvRequest("field(name)")
-
template<typename T>
inline SubBuilder &record(const std::string &name, const T &val)¶ Add a key/value option to the request.
Well known options include:
queueSize : positive integer
block : bool
process : bool or string “true”, “false”, or “passive”
pipeline : bool
A more efficient alternative to
pvRequest("record[key=value]")
-
inline SubBuilder &pvRequest(const std::string &expr)¶
Parse pvRequest string.
Supported syntax is a list of zero or more entities separated by zero or more spaces.
field(<fld.name>)
record[<key>=\<value>]
-
inline SubBuilder &rawRequest(const Value &r)¶
Store raw pvRequest blob.
-
inline SubBuilder &syncCancel(bool b)¶
Controls whether Operation::cancel() and Subscription::cancel() synchronize.
When true (the default) explicit or implicit cancel blocks until any in progress callback has completed. This makes safe some use of references in callbacks.
- Since
0.2.0
-
inline SubBuilder &field(const std::string &fld)¶
Syntax¶
The parser behind pvxs::client::detail::CommonBuilder::pvRequest()
understands the following grammar.
pvRequest ::= | entry | pvRequest entry entry ::= field | record | field_name field ::= "field" "(" field_list ")" record ::= "record" "[" option_list "]" field_list ::= | field_name | field_list "," field_name option_list ::= | option | option_list option option ::= key "=" value
For examples:
“field()”
“field(value)”
“value”
“field(value,alarm)”
“field(value)field(alarm)”
“record[wait=true]”
“field()record[wait=true]”
“field(value)record[wait=true]”
Misc¶
-
struct Config¶
Public Functions
-
Config &applyDefs(const defs_t &defs)¶
update with definitions as with EPICS_PVA* environment variables Process environment is not changed.
-
void updateDefs(defs_t &defs) const¶
extract definitions with environment variable names as keys. Process environment is not changed.
-
void expand()¶
Apply rules to translate current requested configuration into one which can actually be loaded based on current host network configuration.
Explicit use of expand() is optional as the Context ctor expands any Config given. expand() is provided as a aid to help understand how Context::effective() is arrived at.
- Post:
autoAddrList==false
Public Members
-
std::vector<std::string> addressList¶
List of unicast, multicast, and broadcast addresses to which search requests will be sent.
Entries may take the forms:
<ipaddr>[:<port#>]
<ipmultiaddr>[:<port>][,<ttl>][@<ifaceaddr>]
-
std::vector<std::string> interfaces¶
List of local interface addresses on which beacons may be received. Also constrains autoAddrList to only consider broadcast addresses of listed interfaces. Empty implies wildcard 0.0.0.0
-
std::vector<std::string> nameServers¶
List of TCP name servers. Client context will maintain connections, and send search requests, to these servers.
- Since
0.2.0
-
unsigned short udp_port = 5076¶
UDP port to bind. Default is 5076. May be zero, cf. Server::config() to find allocated port.
-
unsigned short tcp_port = 5075¶
Default TCP port for name servers
- Since
0.2.0
-
bool autoAddrList = true¶
Whether to extend the addressList with local interface broadcast addresses. (recommended)
-
double tcpTimeout = 40.0¶
Inactivity timeout interval for TCP connections. (seconds)
- Since
0.2.0
-
Config &applyDefs(const defs_t &defs)¶
-
struct Connected : public std::runtime_error¶
For monitor only. Subscription has (re)connected.
-
struct Disconnect : public std::runtime_error¶
Operation failed because of connection to server was lost.
Subclassed by pvxs::client::Finished
Public Members
-
const epicsTime time¶
When loss of connection was noticed (when timeout expires).
-
const epicsTime time¶
-
struct Finished : public pvxs::client::Disconnect¶
For monitor only. Subscription has completed normally and no more events will ever be received.
-
struct RemoteError : public std::runtime_error¶
Error condition signaled by server.