pvAccessCPP  7.1.5
client.h
1 /*
2  * Copyright information and license terms for this software can be
3  * found in the file LICENSE that is included with the distribution
4  */
5 #ifndef PVATESTCLIENT_H
6 #define PVATESTCLIENT_H
7 
8 #include <ostream>
9 #include <stdexcept>
10 #include <list>
11 
12 #include <epicsMutex.h>
13 
14 #include <pv/pvData.h>
15 #include <pv/bitSet.h>
16 
17 class epicsEvent;
18 
19 namespace epics {namespace pvAccess {
20 class ChannelProvider;
21 class Channel;
22 class Monitor;
23 class Configuration;
24 }}//namespace epics::pvAccess
25 
26 //! See @ref pvac API
27 namespace pvac {
28 
29 /** @defgroup pvac Client API
30  *
31  * PVAccess network client (or other epics::pvAccess::ChannelProvider)
32  *
33  * Usage:
34  *
35  * 1. Construct a ClientProvider
36  * 2. Use the ClientProvider to obtain a ClientChannel
37  * 3. Use the ClientChannel to begin an get, put, rpc, or monitor operation
38  *
39  * Code examples
40  *
41  * - @ref examples_getme
42  * - @ref examples_putme
43  * - @ref examples_monitorme
44  * @{
45  */
46 
47 class ClientProvider;
48 
49 //! Handle for in-progress get/put/rpc operation
50 struct epicsShareClass Operation
51 {
52  struct Impl
53  {
54  virtual ~Impl() {}
55  virtual std::string name() const =0;
56  virtual void cancel() =0;
57  virtual void show(std::ostream&) const =0;
58  };
59 
60  Operation() {}
61  Operation(const std::tr1::shared_ptr<Impl>&);
62  ~Operation();
63  //! Channel name
64  std::string name() const;
65  //! Immediate cancellation.
66  //! Does not wait for remote confirmation.
67  void cancel();
68 
69  bool valid() const { return !!impl; }
70 
71 #if __cplusplus>=201103L
72  explicit operator bool() const { return valid(); }
73 #else
74 private:
75  typedef bool (Operation::*bool_type)() const;
76 public:
77  operator bool_type() const { return valid() ? &Operation::valid : 0; }
78 #endif
79 
80  void reset() { impl.reset(); }
81 
82 protected:
83  friend epicsShareFunc ::std::ostream& operator<<(::std::ostream& strm, const Operation& op);
84  std::tr1::shared_ptr<Impl> impl;
85 };
86 
87 //! Information on put completion
88 struct epicsShareClass PutEvent
89 {
90  enum event_t {
91  Fail, //!< request ends in failure. Check message
92  Cancel, //!< request cancelled before completion
93  Success, //!< It worked!
94  } event;
95  std::string message; //!< Check when event==Fail
96 };
97 
98 //! Information on get/rpc completion
99 struct epicsShareClass GetEvent : public PutEvent
100 {
101  //! New data. NULL unless event==Success
102  epics::pvData::PVStructure::const_shared_pointer value;
103  //! Mask of fields in value which have been initialized by the server
104  //! @since 6.1.0
105  epics::pvData::BitSet::const_shared_pointer valid;
106 };
107 
108 struct epicsShareClass InfoEvent : public PutEvent
109 {
110  //! Type description resulting from getField operation. NULL unless event==Success
111  epics::pvData::FieldConstPtr type;
112 };
113 
114 struct MonitorSync;
115 
116 //! Handle for monitor subscription
117 struct epicsShareClass Monitor
118 {
119  struct Impl;
120  Monitor() {}
121  Monitor(const std::tr1::shared_ptr<Impl>&);
122  ~Monitor();
123 
124  //! Channel name
125  std::string name() const;
126  //! Immediate cancellation.
127  /** Does not wait for remote confirmation.
128  *
129  @since Up to 7.0.0 also cleared root, changed, and overrun.
130  After 7.0.0, these data members are left unchanged.
131  **/
132  void cancel();
133  /** updates root, changed, overrun
134  *
135  * @return true if a new update was extracted from the queue.
136  * @note This method does not block.
137  * @note MonitorEvent::Data will not be repeated until poll()==false.
138  * @post root!=NULL (after version 6.0.0)
139  * @post root!=NULL iff poll()==true (In version 6.0.0)
140  */
141  bool poll();
142  //! true if all events received.
143  //! Check after poll()==false
144  bool complete() const;
145  /** Monitor update data.
146  *
147  * After version 6.0.0
148  *
149  * Initially NULL, becomes !NULL the first time poll()==true.
150  * The PVStructure pointed to be root will presist until
151  * Monitor reconnect w/ type change. This can be detected
152  * by comparing `root.get()`. references to root may be cached
153  * subject to this test.
154  *
155  * In version 6.0.0
156  *
157  * NULL except after poll()==true. poll()==false sets root=NULL.
158  * references to root should not be stored between calls to poll().
159  */
160  epics::pvData::PVStructure::const_shared_pointer root;
161  epics::pvData::BitSet changed,
162  overrun;
163 
164  bool valid() const { return !!impl; }
165 
166 #if __cplusplus>=201103L
167  explicit operator bool() const { return valid(); }
168 #else
169 private:
170  typedef bool (Monitor::*bool_type)() const;
171 public:
172  operator bool_type() const { return valid() ? &Monitor::valid : 0; }
173 #endif
174 
175  void reset() { impl.reset(); }
176 
177 private:
178  std::tr1::shared_ptr<Impl> impl;
179  friend epicsShareFunc ::std::ostream& operator<<(::std::ostream& strm, const Monitor& op);
180  friend struct MonitorSync;
181 };
182 
183 //! Information on monitor subscription/queue change
185 {
186  enum event_t {
187  Fail=1, //!< subscription ends in an error
188  Cancel=2, //!< subscription ends in cancellation
189  Disconnect=4,//!< subscription interrupted due to loss of communication
190  Data=8, //!< Data queue not empty. Call Monitor::poll()
191  } event;
192  std::string message; //!< set for event=Fail
193 };
194 
195 /** Subscription usable w/o callbacks
196  *
197  * Basic usage is to call wait() or test().
198  * If true is returned, then the 'event', 'root', 'changed', and 'overrun'
199  * members have been updated with a new event.
200  * Test 'event.event' first to find out which kind of event has occured.
201  *
202  * Note that wait()/test() methods are distinct from base class poll().
203  * wait()/test() check for the arrival of MonitorEvent
204  * while poll() checks for the availability of data (eg. following a Data event).
205  */
206 struct epicsShareClass MonitorSync : public Monitor
207 {
208  struct SImpl;
209  MonitorSync() {}
210  MonitorSync(const Monitor&, const std::tr1::shared_ptr<SImpl>&);
211  ~MonitorSync();
212 
213  //! wait for new event
214  //! @returns true when a new event was received.
215  //! false if wake() was called.
216  bool wait();
217  //! wait for new event
218  //! @return false on timeout
219  bool wait(double timeout);
220  //! check if new event is immediately available.
221  //! Does not block.
222  bool test();
223 
224  //! Abort one call to wait(), either concurrent or future.
225  //! Calls are queued.
226  //! wait() will return with MonitorEvent::Fail.
227  void wake();
228 
229  //! most recent event
230  //! updated only during wait() or poll()
231  MonitorEvent event;
232 private:
233  std::tr1::shared_ptr<SImpl> simpl;
234 };
235 
236 //! information on connect/disconnect
238 {
239  //! Is this a connection, or disconnection, event.
240  bool connected;
241  //! For connection events. This is the name provided by the peer (cf. epics::pvAccess::Channel::getRemoteAddress() ).
242  //! @since >6.1.0
243  std::string peerName;
244 };
245 
246 //! Thrown by blocking methods of ClientChannel on operation timeout
247 struct Timeout : public std::runtime_error
248 {
249  Timeout();
250 };
251 
252 namespace detail {
253 class PutBuilder;
254 void registerRefTrack();
255 }
256 
257 /** Represents a single channel
258  *
259  * This class has two sets of methods, those which block for completion, and
260  * those which use callbacks to signal completion.
261  *
262  * Those which block accept a 'timeout' argument (in seconds).
263  *
264  * Those which use callbacks accept a 'cb' argument and return an Operation or Monitor handle object.
265  */
266 class epicsShareClass ClientChannel
267 {
268 #if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ < 305)
269 public:
270  // Impl is public only as a workaround on older GCC
271 #endif
272  struct Impl;
273 private:
274  std::tr1::shared_ptr<Impl> impl;
275  friend class ClientProvider;
276  friend void detail::registerRefTrack();
277  friend epicsShareFunc ::std::ostream& operator<<(::std::ostream& strm, const ClientChannel& op);
278 
279  ClientChannel(const std::tr1::shared_ptr<Impl>& i) :impl(i) {}
280 public:
281  //! Channel creation options
282  struct epicsShareClass Options {
283  short priority;
284  std::string address;
285  Options();
286  bool operator<(const Options&) const;
287  };
288 
289  //! Construct a null channel. All methods throw. May later be assigned from a valid ClientChannel
290  ClientChannel() {}
291  /** Construct a ClientChannel using epics::pvAccess::ChannelProvider::createChannel()
292  *
293  * Does not block.
294  * @throw std::logic_error if the provider is NULL or name is an empty string
295  * @throw std::runtime_error if the ChannelProvider can't provide
296  */
297  ClientChannel(const std::tr1::shared_ptr<epics::pvAccess::ChannelProvider>& provider,
298  const std::string& name,
299  const Options& opt = Options());
300  ~ClientChannel();
301 
302  //! Channel name or an empty string
303  std::string name() const;
304 
305  bool valid() const { return !!impl; }
306 
307 #if __cplusplus>=201103L
308  explicit operator bool() const { return valid(); }
309 #else
310 private:
311  typedef bool (ClientChannel::*bool_type)() const;
312 public:
313  operator bool_type() const { return valid() ? &ClientChannel::valid : 0; }
314 #endif
315 
316  void reset() { impl.reset(); }
317 
318  //! callback for get() and rpc()
319  struct GetCallback {
320  virtual ~GetCallback() {}
321  //! get or rpc operation is complete
322  virtual void getDone(const GetEvent& evt)=0;
323  };
324 
325  //! Issue request to retrieve current PV value
326  //! @param cb Completion notification callback. Must outlive Operation (call Operation::cancel() to force release)
327  //! @param pvRequest if NULL defaults to "field()".
328  Operation get(GetCallback* cb,
329  epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
330 
331  //! Block and retrieve current PV value
332  //! @param timeout in seconds
333  //! @param pvRequest if NULL defaults to "field()".
334  //! @throws Timeout or std::runtime_error
335  epics::pvData::PVStructure::const_shared_pointer
336  get(double timeout = 3.0,
337  epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
338 
339 
340  //! Start an RPC call
341  //! @param cb Completion notification callback. Must outlive Operation (call Operation::cancel() to force release)
342  //! @param arguments encoded call arguments
343  //! @param pvRequest if NULL defaults to "field()".
344  Operation rpc(GetCallback* cb,
345  const epics::pvData::PVStructure::const_shared_pointer& arguments,
346  epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
347 
348  //! Block and execute remote call
349  //! @param timeout in seconds
350  //! @param arguments encoded call arguments
351  //! @param pvRequest if NULL defaults to "field()".
352  epics::pvData::PVStructure::const_shared_pointer
353  rpc(double timeout,
354  const epics::pvData::PVStructure::const_shared_pointer& arguments,
355  epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
356 
357  //! callbacks for put()
358  struct PutCallback {
359  virtual ~PutCallback() {}
360  struct Args {
361  Args(epics::pvData::BitSet& tosend, epics::pvData::BitSet& previousmask) :tosend(tosend), previousmask(previousmask) {}
362  //! Callee must fill this in with an instance of the Structure passed as the 'build' argument.
363  epics::pvData::PVStructure::const_shared_pointer root;
364  //! Callee must set bits corresponding to the fields of 'root' which will actually be sent.
365  epics::pvData::BitSet& tosend;
366  //! A previous value of the PV being "put" when put(..., getprevious=true). eg. use to find enumeration value.
367  //! Otherwise NULL.
368  //! @note The value of the PV may change between the point where "previous" is fetched,
369  //! and when this Put operation completes.
370  //! @since 6.1.0 Added after 6.0.0
371  epics::pvData::PVStructure::const_shared_pointer previous;
372  //! Bit mask indicating those fields of 'previous' which have been set by the server. (others have local defaults)
373  //! Unused if previous==NULL.
374  const epics::pvData::BitSet& previousmask;
375  };
376  /** Server provides expected structure.
377  *
378  * Implementation must instanciate (or re-use) a PVStructure into args.root,
379  * then initialize any necessary fields and set bits in args.tosend as approprate.
380  *
381  * If this method throws, then putDone() is called with PutEvent::Fail
382  */
383  virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) =0;
384  //! Put operation is complete
385  virtual void putDone(const PutEvent& evt)=0;
386  };
387 
388  //! Initiate request to change PV
389  //! @param cb Completion notification callback. Must outlive Operation (call Operation::cancel() to force release)
390  //! @param pvRequest if NULL defaults to "field()".
391  //! @param getprevious If true, fetch a previous value of the PV and make
392  //! this available as PutCallback::Args::previous and previousmask.
393  //! If false, then previous=NULL
394  Operation put(PutCallback* cb,
395  epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer(),
396  bool getprevious = false);
397 
398  //! Synchronious put operation
399  inline
400  detail::PutBuilder put(const epics::pvData::PVStructure::const_shared_pointer &pvRequest = epics::pvData::PVStructure::const_shared_pointer());
401 
402  //! Monitor event notification
403  struct MonitorCallback {
404  virtual ~MonitorCallback() {}
405  /** New monitor event
406  *
407  * - MonitorEvent::Fail - An Error occurred. Check evt.message
408  * - MonitorEvent::Cancel - Monitor::cancel() called
409  * - MonitorEvent::Disconnect - Underlying ClientChannel becomes disconnected
410  * - MonitorEvent::Data - FIFO becomes not empty.Call Monitor::poll()
411  */
412  virtual void monitorEvent(const MonitorEvent& evt)=0;
413  };
414 
415  //! Begin subscription
416  //! @param cb Completion notification callback. Must outlive Monitor (call Monitor::cancel() to force release)
417  Monitor monitor(MonitorCallback *cb,
418  epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
419 
420  /** Begin subscription w/o callbacks
421  *
422  * @param event If not NULL, then subscription events are signaled to this epicsEvent.
423  * Use MonitorSync::test() to see if a subscription has an event waiting.
424  * Otherwise an internal epicsEvent is allocated for use with MonitorSync::wait()
425  *
426  * @note For simple usage with a single MonitorSync, pass event=NULL and call MonitorSync::wait().
427  * If more than one MonitorSync is being created, then pass a custom epicsEvent and use MonitorSync::test() to find
428  * which subscriptions have events pending.
429  */
430  MonitorSync monitor(const epics::pvData::PVStructure::const_shared_pointer& pvRequest = epics::pvData::PVStructure::const_shared_pointer(),
431  epicsEvent *event =0);
432 
433  struct InfoCallback {
434  virtual ~InfoCallback() {}
435  //! getField operation is complete
436  virtual void infoDone(const InfoEvent& evt) =0;
437  };
438 
439  //! Request PV type info.
440  //! @note This type may not be the same as the types used in the get/put/monitor operations.
441  Operation info(InfoCallback *cb, const std::string& subfld = std::string());
442 
443  //! Synchronious getField opreation
444  epics::pvData::FieldConstPtr info(double timeout = 3.0,
445  const std::string& subfld = std::string());
446 
447  //! Connection state change CB
448  struct ConnectCallback {
449  virtual ~ConnectCallback() {}
450  virtual void connectEvent(const ConnectEvent& evt)=0;
451  };
452  //! Append to list of listeners
453  //! @param cb Channel dis/connect notification callback. Must outlive ClientChannel or call to removeConnectListener()
454  void addConnectListener(ConnectCallback*);
455  //! Remove from list of listeners
456  void removeConnectListener(ConnectCallback*);
457 
458  void show(std::ostream& strm) const;
459 private:
460  std::tr1::shared_ptr<epics::pvAccess::Channel> getChannel();
461 };
462 
463 namespace detail {
464 
465 //! Helper to accumulate values to for a Put operation.
466 //! Make sure to call exec() to begin operation.
467 class epicsShareClass PutBuilder {
468  ClientChannel& channel;
469  epics::pvData::PVStructure::const_shared_pointer request;
470 
471  template<typename V>
472  struct triple {
473  std::string name;
474  bool required;
475  V value;
476  triple(const std::string& name, const V& value, bool required =true)
477  :name(name), required(required), value(value)
478  {}
479  };
480 
481  typedef std::list<triple<epics::pvData::AnyScalar> > scalars_t;
482  scalars_t scalars;
483 
484  typedef std::list<triple<epics::pvData::shared_vector<const void> > > arrays_t;
485  arrays_t arrays;
486 
487  struct Exec;
488 
489  friend class pvac::ClientChannel;
490  PutBuilder(ClientChannel& channel, const epics::pvData::PVStructure::const_shared_pointer& request)
491  :channel(channel), request(request)
492  {}
493 public:
494  PutBuilder& set(const std::string& name, const epics::pvData::AnyScalar& value, bool required=true) {
495  scalars.push_back(scalars_t::value_type(name, value, required));
496  return *this;
497  }
498  template<typename T>
499  PutBuilder& set(const std::string& name, T value, bool required=true) {
500  return set(name, epics::pvData::AnyScalar(value), required);
501  }
502  PutBuilder& set(const std::string& name, const epics::pvData::shared_vector<const void>& value, bool required=true) {
503  arrays.push_back(arrays_t::value_type(name, value, required));
504  return *this;
505  }
506  template<typename T>
507  PutBuilder& set(const std::string& name, const epics::pvData::shared_vector<const T>& value, bool required=true) {
508  return set(name, epics::pvData::static_shared_vector_cast<const void>(value), required);
509  }
510  void exec(double timeout=3.0);
511 };
512 
513 
514 }// namespace detail
515 
516 //! Central client context.
517 class epicsShareClass ClientProvider
518 {
519  struct Impl;
520  std::tr1::shared_ptr<Impl> impl;
521  friend void detail::registerRefTrack();
522  friend epicsShareFunc ::std::ostream& operator<<(::std::ostream& strm, const ClientProvider& op);
523 public:
524 
525  //! Construct a null provider. All methods throw. May later be assigned from a valid ClientProvider
526  ClientProvider() {}
527  /** Use named provider.
528  *
529  * @param providerName ChannelProvider name, may be prefixed with "clients:" or "servers:" to query
530  * epics::pvAccess::ChannelProviderRegistry::clients() or
531  * epics::pvAccess::ChannelProviderRegistry::servers().
532  * No prefix implies "clients:".
533  */
534  ClientProvider(const std::string& providerName,
535  const std::tr1::shared_ptr<epics::pvAccess::Configuration>& conf = std::tr1::shared_ptr<epics::pvAccess::Configuration>());
536  explicit ClientProvider(const std::tr1::shared_ptr<epics::pvAccess::ChannelProvider>& provider);
537  ~ClientProvider();
538 
539  std::string name() const;
540 
541  /** Get a new Channel
542  *
543  * Does not block.
544  * Never returns NULL.
545  * Uses internal Channel cache.
546  */
547  ClientChannel connect(const std::string& name,
548  const ClientChannel::Options& conf = ClientChannel::Options());
549 
550  //! Remove from channel cache
551  bool disconnect(const std::string& name,
552  const ClientChannel::Options& conf = ClientChannel::Options());
553 
554  //! Clear channel cache
555  void disconnect();
556 
557  bool valid() const { return !!impl; }
558 
559 #if __cplusplus>=201103L
560  explicit operator bool() const { return valid(); }
561 #else
562 private:
563  typedef bool (ClientProvider::*bool_type)() const;
564 public:
565  operator bool_type() const { return valid() ? &ClientProvider::valid : 0; }
566 #endif
567 
568  void reset() { impl.reset(); }
569 };
570 
571 
572 
573 detail::PutBuilder
574 ClientChannel::put(const epics::pvData::PVStructure::const_shared_pointer& pvRequest)
575 {
576  return detail::PutBuilder(*this, pvRequest);
577 }
578 
579 epicsShareFunc ::std::ostream& operator<<(::std::ostream& strm, const Operation& op);
580 epicsShareFunc ::std::ostream& operator<<(::std::ostream& strm, const Monitor& op);
581 epicsShareFunc ::std::ostream& operator<<(::std::ostream& strm, const ClientChannel& op);
582 epicsShareFunc ::std::ostream& operator<<(::std::ostream& strm, const ClientProvider& op);
583 
584 //! @}
585 
586 }//namespace pvac
587 
588 #endif // PVATESTCLIENT_H
Represents a single channel.
Definition: client.h:266
std::string message
set for event=Fail
Definition: client.h:192
Helper to accumulate values to for a Put operation.
Definition: client.h:467
Information on get/rpc completion.
Definition: client.h:99
Data queue not empty. Call Monitor::poll()
Definition: client.h:190
Information on put completion.
Definition: client.h:88
Information on monitor subscription/queue change.
Definition: client.h:184
std::string peerName
For connection events.
Definition: client.h:243
subscription ends in cancellation
Definition: client.h:188
virtual void authNZMessage(epics::pvData::PVStructure::shared_pointer const &data)=0
Pass data to the active security plug-in session.
Subscription usable w/o callbacks.
Definition: client.h:206
Central client context.
Definition: client.h:517
subscription interrupted due to loss of communication
Definition: client.h:189
subscription ends in an error
Definition: client.h:187
information on connect/disconnect
Definition: client.h:237
Handle for monitor subscription.
Definition: client.h:117
Thrown by blocking methods of ClientChannel on operation timeout.
Definition: client.h:247
bool connected
Is this a connection, or disconnection, event.
Definition: client.h:240
Handle for in-progress get/put/rpc operation.
Definition: client.h:50