pvxslibs 1.5.0__cp310-cp310-manylinux2014_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. pvxslibs/__init__.py +0 -0
  2. pvxslibs/dbd/pvxsIoc.dbd +8 -0
  3. pvxslibs/include/pvxs/client.h +1094 -0
  4. pvxslibs/include/pvxs/data.h +948 -0
  5. pvxslibs/include/pvxs/iochooks.h +170 -0
  6. pvxslibs/include/pvxs/log.h +148 -0
  7. pvxslibs/include/pvxs/netcommon.h +82 -0
  8. pvxslibs/include/pvxs/nt.h +208 -0
  9. pvxslibs/include/pvxs/server.h +238 -0
  10. pvxslibs/include/pvxs/sharedArray.h +748 -0
  11. pvxslibs/include/pvxs/sharedpv.h +121 -0
  12. pvxslibs/include/pvxs/source.h +290 -0
  13. pvxslibs/include/pvxs/srvcommon.h +148 -0
  14. pvxslibs/include/pvxs/unittest.h +327 -0
  15. pvxslibs/include/pvxs/util.h +354 -0
  16. pvxslibs/include/pvxs/version.h +97 -0
  17. pvxslibs/include/pvxs/versionNum.h +6 -0
  18. pvxslibs/ioc.py +10 -0
  19. pvxslibs/lib/__init__.py +0 -0
  20. pvxslibs/lib/event_core_dsoinfo.py +14 -0
  21. pvxslibs/lib/event_pthread_dsoinfo.py +14 -0
  22. pvxslibs/lib/libevent_core.so +0 -0
  23. pvxslibs/lib/libevent_core.so.2.2.0 +0 -0
  24. pvxslibs/lib/libevent_pthread.so +0 -0
  25. pvxslibs/lib/libevent_pthread.so.2.2.0 +0 -0
  26. pvxslibs/lib/libpvxs.so +0 -0
  27. pvxslibs/lib/libpvxs.so.1.5 +0 -0
  28. pvxslibs/lib/libpvxsIoc.so +0 -0
  29. pvxslibs/lib/libpvxsIoc.so.1.5 +0 -0
  30. pvxslibs/lib/pvxsIoc_dsoinfo.py +14 -0
  31. pvxslibs/lib/pvxs_dsoinfo.py +14 -0
  32. pvxslibs/path.py +12 -0
  33. pvxslibs/test/__init__.py +0 -0
  34. pvxslibs/test/test_load.py +30 -0
  35. pvxslibs/version.py +32 -0
  36. pvxslibs-1.5.0.dist-info/METADATA +44 -0
  37. pvxslibs-1.5.0.dist-info/RECORD +40 -0
  38. pvxslibs-1.5.0.dist-info/WHEEL +5 -0
  39. pvxslibs-1.5.0.dist-info/licenses/LICENSE +26 -0
  40. pvxslibs-1.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1094 @@
1
+ /**
2
+ * Copyright - See the COPYRIGHT that is included with this distribution.
3
+ * pvxs is distributed subject to a Software License Agreement found
4
+ * in file LICENSE that is included with this distribution.
5
+ */
6
+ #ifndef PVXS_CLIENT_H
7
+ #define PVXS_CLIENT_H
8
+
9
+ #include <stdexcept>
10
+ #include <string>
11
+ #include <map>
12
+ #include <vector>
13
+ #include <memory>
14
+ #include <functional>
15
+ #include <iosfwd>
16
+ #include <typeinfo>
17
+
18
+ #include <epicsTime.h>
19
+ #include <epicsEndian.h>
20
+
21
+ #include <pvxs/version.h>
22
+ #include <pvxs/data.h>
23
+ #include <pvxs/netcommon.h>
24
+ #include <pvxs/util.h>
25
+
26
+ namespace pvxs {
27
+ namespace client {
28
+
29
+ class Context;
30
+ struct Config;
31
+
32
+ //! Operation failed because of connection to server was lost
33
+ struct PVXS_API Disconnect : public std::runtime_error
34
+ {
35
+ Disconnect();
36
+ virtual ~Disconnect();
37
+
38
+ //! When loss of connection was noticed (when timeout expires).
39
+ const epicsTime time;
40
+ };
41
+
42
+ //! Error condition signaled by server
43
+ struct PVXS_API RemoteError : public std::runtime_error
44
+ {
45
+ RemoteError(const std::string& msg);
46
+ virtual ~RemoteError();
47
+ };
48
+
49
+ //! For monitor only. Subscription has completed normally
50
+ //! and no more events will ever be received.
51
+ struct PVXS_API Finished : public Disconnect
52
+ {
53
+ Finished() = default;
54
+ virtual ~Finished();
55
+ };
56
+
57
+ //! For monitor only. Subscription has (re)connected.
58
+ struct PVXS_API Connected : public std::runtime_error
59
+ {
60
+ Connected(const std::string& peerName);
61
+ virtual ~Connected();
62
+
63
+ const std::string peerName;
64
+ const epicsTime time;
65
+ };
66
+
67
+ struct PVXS_API Interrupted : public std::runtime_error
68
+ {
69
+ Interrupted();
70
+ virtual ~Interrupted();
71
+ };
72
+
73
+ struct PVXS_API Timeout : public std::runtime_error
74
+ {
75
+ Timeout();
76
+ virtual ~Timeout();
77
+ };
78
+
79
+ //! Holder for a Value or an exception
80
+ class Result {
81
+ Value _result;
82
+ std::exception_ptr _error;
83
+ std::string _peerName;
84
+ public:
85
+ Result() = default;
86
+ Result(Value&& val, const std::string& peerName) :_result(std::move(val)), _peerName(peerName) {}
87
+ explicit Result(const std::exception_ptr& err) :_error(err) {}
88
+
89
+ //! Access to the Value, or rethrow the exception
90
+ Value& operator()() {
91
+ if(_error)
92
+ std::rethrow_exception(_error);
93
+ return _result;
94
+ }
95
+
96
+ const std::string peerName() const { return _peerName; }
97
+
98
+ bool error() const { return !!_error; }
99
+ explicit operator bool() const { return _result || _error; }
100
+ };
101
+
102
+ //! Handle for in-progress operation
103
+ struct PVXS_API Operation {
104
+ //! Operation type
105
+ const enum operation_t {
106
+ Info = 17, // CMD_GET_FIELD
107
+ Get = 10, // CMD_GET
108
+ Put = 11, // CMD_PUT
109
+ RPC = 20, // CMD_RPC
110
+ Monitor = 13, // CMD_MONITOR
111
+ Discover = 3, // CMD_SEARCH
112
+ } op;
113
+
114
+ explicit constexpr Operation(operation_t op) :op(op) {}
115
+ Operation(const Operation&) = delete;
116
+ Operation& operator=(const Operation&) = delete;
117
+ virtual ~Operation() =0;
118
+
119
+ /** PV name
120
+ *
121
+ * Returned reference is valid for the lifetime of the Operation
122
+ */
123
+ virtual const std::string& name() =0;
124
+
125
+ //! Explicitly cancel a pending operation.
126
+ //! Blocks until an in-progress callback has completed.
127
+ //! @returns true if the operation was canceled, or false if already complete.
128
+ virtual bool cancel() =0;
129
+
130
+ /** @brief Block until Operation completion
131
+ *
132
+ * As an alternative to a .result() callback, wait for operation completion,
133
+ * timeout, or interruption (via. interrupt() ).
134
+ *
135
+ * @param timeout Time to wait prior to throwing TimeoutError. cf. epicsEvent::wait(double)
136
+ * @return result Value. Always empty/invalid for put()
137
+ * @throws Timeout Timeout exceeded
138
+ * @throws Interrupted interrupt() called
139
+ */
140
+ virtual Value wait(double timeout) =0;
141
+
142
+ //! wait(double) without a timeout
143
+ Value wait() {
144
+ return wait(99999999.0);
145
+ }
146
+
147
+ //! Queue an interruption of a wait() or wait(double) call.
148
+ virtual void interrupt() =0;
149
+
150
+ protected:
151
+ virtual void _reExecGet(std::function<void(client::Result&&)>&& resultcb) =0;
152
+ virtual void _reExecPut(const Value& arg, std::function<void(client::Result&&)>&& resultcb) =0;
153
+ public:
154
+ #ifdef PVXS_EXPERT_API_ENABLED
155
+ // usable when Builder::autoExec(false)
156
+ // For GET/PUT, (re)issue request for current value
157
+ inline void reExecGet(std::function<void(client::Result&&)>&& resultcb) { this->_reExecGet(std::move(resultcb)); }
158
+ // For PUT (re)issue request to set current value
159
+ inline void reExecPut(const Value& arg, std::function<void(client::Result&&)>&& resultcb) { this->_reExecPut(arg, std::move(resultcb)); }
160
+ #endif
161
+ };
162
+
163
+ //! Information about the state of a Subscription
164
+ struct SubscriptionStat {
165
+ //! Number of events in the queue
166
+ size_t nQueue=0;
167
+ //! Number of Value updates where the server reported at least
168
+ //! one update dropped/squashed.
169
+ size_t nSrvSquash=0;
170
+ //! Number of Value updates dropped/squashed due to client queue overflow
171
+ size_t nCliSquash=0;
172
+ //! Max queue size so far
173
+ size_t maxQueue=0;
174
+ //! Limit on queue size
175
+ size_t limitQueue=0;
176
+ };
177
+
178
+ //! Handle for monitor subscription
179
+ struct PVXS_API Subscription {
180
+ Subscription() = default;
181
+ Subscription(const Subscription&) = delete;
182
+ Subscription& operator=(const Subscription&) = delete;
183
+ virtual ~Subscription() =0;
184
+
185
+ protected:
186
+ virtual const std::string& _name() =0;
187
+ public:
188
+ //! PV name
189
+ inline const std::string& name() { return _name(); }
190
+
191
+ //! Explicitly cancel a active subscription.
192
+ //! Blocks until any in-progress callback has completed.
193
+ virtual bool cancel() =0;
194
+
195
+ //! Ask a server to stop (true) or re-start (false), sending updates to this Subscription
196
+ virtual void pause(bool p=true) =0;
197
+ //! Shorthand for @code pause(false) @endcode
198
+ inline void resume() { pause(false); }
199
+
200
+ /** De-queue update from subscription event queue.
201
+ *
202
+ * If the queue is empty, return an empty/invalid Value (Value::valid()==false).
203
+ * A data update is returned as a Value.
204
+ * An error or special event is thrown.
205
+ *
206
+ * @returns A valid Value until the queue is empty
207
+ * @throws Connected (depending on MonitorBuilder::maskConnected())
208
+ * @throws Disconnect (depending on MonitorBuilder::maskDisconnect())
209
+ * @throws Finished (depending on MonitorBuilder::maskDisconnect())
210
+ * @throws RemoteError For server signaled errors
211
+ * @throws std::exception For client side failures.
212
+ *
213
+ * @code
214
+ * std::shared_ptr<Subscription> sub(...);
215
+ * try {
216
+ * while(auto update = sub.pop()) {
217
+ * // have data update
218
+ * ...
219
+ * }
220
+ * // queue empty
221
+ * } catch(Connected& con) { // if MonitorBuilder::maskConnected(false)
222
+ * } catch(Finished& con) { // if MonitorBuilder::maskDisconnected(false)
223
+ * } catch(Disconnect& con) { // if MonitorBuilder::maskDisconnected(false)
224
+ * } catch(RemoteError& con) { // error message from server
225
+ * } catch(std::exception& con) { // client side error
226
+ * }
227
+ * @endcode
228
+ */
229
+ virtual Value pop() =0;
230
+
231
+ protected:
232
+ virtual bool doPop(std::vector<Value>& out, size_t limit=0u) =0;
233
+ public:
234
+
235
+ #ifdef PVXS_EXPERT_API_ENABLED
236
+ /** De-queue a batch of updates from subscription event queue.
237
+ *
238
+ * @param out Updated with any Values dequeued. Will always be clear()d
239
+ * @param limit When non-zero, an upper limit on the number of Values which will be dequeued.
240
+ * @return true if the queue was not emptied, and pop() should be called again.
241
+ * false if the queue was emptied, and a further onEvent() callback may be awaited.
242
+ * @throws the same exceptions as non-batch pop()
243
+ *
244
+ * @since 1.1.0 Added
245
+ */
246
+ inline bool pop(std::vector<Value>& out, size_t limit=0u)
247
+ { return doPop(out, limit); }
248
+ #endif
249
+
250
+ //! Poll statistics
251
+ //! @since 1.1.0
252
+ virtual void stats(SubscriptionStat&, bool reset = false) =0;
253
+
254
+ protected:
255
+ virtual void _onEvent(std::function<void(Subscription&)>&&) =0;
256
+ public:
257
+ #ifdef PVXS_EXPERT_API_ENABLED
258
+ // replace handler stored with MonitorBuilder::event()
259
+ inline void onEvent(std::function<void(Subscription&)>&& fn) { this->_onEvent(std::move(fn)); }
260
+ #endif
261
+
262
+ //! Return strong internal reference which will not prevent
263
+ //! implicit cancellation when the last reference returned
264
+ //! by exec() is released.
265
+ //! @since 0.2.0
266
+ virtual std::shared_ptr<Subscription> shared_from_this() const =0;
267
+ };
268
+
269
+ //! Handle for entry in Channel cache
270
+ struct PVXS_API Connect {
271
+ virtual ~Connect() =0;
272
+
273
+ //! Name passed to Context::connect()
274
+ virtual const std::string& name() const =0;
275
+ //! Poll (momentary) connection status
276
+ virtual bool connected() const =0;
277
+ };
278
+
279
+ class GetBuilder;
280
+ class PutBuilder;
281
+ class RPCBuilder;
282
+ class MonitorBuilder;
283
+ class RequestBuilder;
284
+ class ConnectBuilder;
285
+ struct Discovered;
286
+ class DiscoverBuilder;
287
+
288
+ /** An independent PVA protocol client instance
289
+ *
290
+ * Typically created with Config::build()
291
+ *
292
+ * @code
293
+ * Context ctxt(Config::from_env().build());
294
+ * @endcode
295
+ */
296
+ class PVXS_API Context {
297
+ public:
298
+ struct Pvt;
299
+
300
+ //! An empty/dummy Context
301
+ constexpr Context() = default;
302
+ //! Create/allocate a new client with the provided config.
303
+ //! Config::build() is a convenient shorthand.
304
+ explicit Context(const Config &);
305
+ ~Context();
306
+
307
+ /** Create new client context based on configuration from $EPICS_PVA* environment variables.
308
+ *
309
+ * Shorthand for @code Config::fromEnv().build() @endcode.
310
+ * @since 0.2.1
311
+ */
312
+ static
313
+ Context fromEnv();
314
+
315
+ //! effective config of running client
316
+ const Config& config() const;
317
+
318
+ /** Force close the client.
319
+ *
320
+ * ~Context() will close() automatically. So an explicit call is optional.
321
+ *
322
+ * Aborts/interrupts all in progress network operations.
323
+ * Blocks until any in-progress callbacks have completed.
324
+ *
325
+ * @since 1.1.0
326
+ */
327
+ void close();
328
+
329
+ /** Request the present value of a PV
330
+ *
331
+ * Simple blocking
332
+ *
333
+ * @code
334
+ * Context ctxt(...);
335
+ * auto result = ctxt.get("pv:name")
336
+ * .exec()
337
+ * ->wait();
338
+ * @endcode
339
+ *
340
+ * With completion callback
341
+ *
342
+ * @code
343
+ * Context ctxt(...);
344
+ * auto op = ctxt.get("pv:name")
345
+ * .result([](Result&& prototype){
346
+ * std::cout<<prototype();
347
+ * })
348
+ * .exec();
349
+ * // store op until completion
350
+ * @endcode
351
+ * See GetBuilder and <a href="#get-info">Get/Info</a> for details.
352
+ */
353
+ inline
354
+ GetBuilder get(const std::string& pvname);
355
+
356
+ /** Request type information from PV.
357
+ * Results in a Value with no marked fields.
358
+ *
359
+ * Simple blocking
360
+ *
361
+ * @code
362
+ * Context ctxt(...);
363
+ * auto result = ctxt.info("pv:name")
364
+ * .exec()
365
+ * ->wait();
366
+ * @endcode
367
+ *
368
+ * With completion callback
369
+ *
370
+ * @code
371
+ * Context ctxt(...);
372
+ * auto op = ctxt.info("pv:name")
373
+ * .result([](Result&& prototype){
374
+ * std::cout<<prototype();
375
+ * })
376
+ * .exec();
377
+ * // store op until completion
378
+ * @endcode
379
+ *
380
+ * See GetBuilder and <a href="#get-info">Get/Info</a> for details.
381
+ */
382
+ inline
383
+ GetBuilder info(const std::string& pvname);
384
+
385
+ /** Request change/update of PV.
386
+ *
387
+ * Assign certain values to certain fields and block for completion.
388
+ *
389
+ * @code
390
+ * Context ctxt(...);
391
+ * auto result = ctxt.put("pv:name")
392
+ * .set("value", 42)
393
+ * .exec()
394
+ * ->wait();
395
+ * @endcode
396
+ *
397
+ * Alternately, and more generally, using a .build() callback
398
+ * and use .result() callback for completion notification.
399
+ *
400
+ * @code
401
+ * Context ctxt(...);
402
+ * auto op = ctxt.put("pv:name")
403
+ * .build([](Value&& prototype) -> Value {
404
+ * auto putval = prototype.cloneEmpty();
405
+ * putval["value"] = 42;
406
+ * return putval;
407
+ * })
408
+ * .result([](Result&& prototype){
409
+ * try {
410
+ * // always returns empty Value on success
411
+ * prototype();
412
+ * std::cout<<"Success";
413
+ * }catch(std::exception& e){
414
+ * std::cout<<"Error: "<<e.what();
415
+ * }
416
+ * })
417
+ * .exec();
418
+ * // store op until completion
419
+ * @endcode
420
+ *
421
+ * See PutBuilder and <a href="#put">Put</a> for details.
422
+ */
423
+ inline
424
+ PutBuilder put(const std::string& pvname);
425
+
426
+ inline
427
+ RPCBuilder rpc(const std::string& pvname);
428
+
429
+ /** Execute "stateless" remote procedure call operation.
430
+ *
431
+ * Simple blocking
432
+ *
433
+ * @code
434
+ * Value arg = ...;
435
+ * Context ctxt(...);
436
+ * auto result = ctxt.rpc("pv:name", arg)
437
+ * .arg("blah", 5)
438
+ * .arg("other", "example")
439
+ * .exec()
440
+ * ->wait();
441
+ * @endcode
442
+ *
443
+ * With completion callback
444
+ *
445
+ * @code
446
+ * Value arg = ...;
447
+ * Context ctxt(...);
448
+ * auto op = ctxt.rpc("pv:name", arg)
449
+ * .result([](Result&& prototype){
450
+ * std::cout<<prototype();
451
+ * })
452
+ * .exec();
453
+ * // store op until completion
454
+ * @endcode
455
+ *
456
+ * See RPCBuilder and <a href="#rpc">RPC</a> for details.
457
+ */
458
+ inline
459
+ RPCBuilder rpc(const std::string& pvname, const Value& arg);
460
+
461
+ /** Create a new subscription for changes to a PV.
462
+ *
463
+ * @code
464
+ * MPMCFIFO<std::shared_ptr<Subscription>> workqueue(42u);
465
+ *
466
+ * auto sub = ctxt.monitor("pv:name")
467
+ * .event([&workqueue](Subscription& sub) {
468
+ * // Subscription queue becomes not empty.
469
+ * // Avoid I/O on PVXS worker thread,
470
+ * // delegate to application thread
471
+ * workqueue.push(sub.shared_from_this());
472
+ * })
473
+ * .exec();
474
+ *
475
+ * while(auto sub = workqueue.pop()) { // could workqueue.push(nullptr) to break
476
+ * try {
477
+ * Value update = sub.pop();
478
+ * if(!update)
479
+ * continue; // Subscription queue empty, wait for another event callback
480
+ * std::cout<<update<<"\n";
481
+ * } catch(std::exception& e) {
482
+ * // may be Connected(), Disconnect(), Finished(), or RemoteError()
483
+ * std::cerr<<"Error "<<e.what()<<"\n";
484
+ * }
485
+ * // queue not empty, reschedule
486
+ * workqueue.push(sub);
487
+ * }
488
+ * // store op until completion
489
+ * @endcode
490
+ *
491
+ * See MonitorBuilder and <a href="#monitor">Monitor</a> for details.
492
+ */
493
+ inline
494
+ MonitorBuilder monitor(const std::string& pvname);
495
+
496
+ /** Manually add, and maintain, an entry in the Channel cache.
497
+ *
498
+ * This optional method may be used when it is known that a given PV
499
+ * will be needed in future.
500
+ * ConnectBuilder::onConnect() and ConnectBuilder::onDisconnect()
501
+ * may be used to get asynchronous notification, or
502
+ * the returned Connect object may be used to poll Channel (dis)connect state.
503
+ *
504
+ * @since 0.2.0
505
+ */
506
+ inline
507
+ ConnectBuilder connect(const std::string& pvname);
508
+
509
+ /** Compose a pvRequest independently of a network operation.
510
+ *
511
+ * This is not a network operation.
512
+ *
513
+ * Use of request() is optional. pvRequests can be composed
514
+ * with individual network operation Builders.
515
+ *
516
+ * @code
517
+ * Value pvReq = Context::request()
518
+ * .pvRequest("field(value)field(blah)")
519
+ * .record("pipeline", true)
520
+ * .build();
521
+ * @endcode
522
+ */
523
+ static inline
524
+ RequestBuilder request();
525
+
526
+ /** Discover the presence or absence of Servers.
527
+ *
528
+ * Combines information from periodic Server Beacon messages, and optionally
529
+ * Discover pings, to provide notice when PVA servers appear or disappear
530
+ * from attached networks.
531
+ *
532
+ * Note that a discover() Operation will never complete with a Value,
533
+ * and so can only end with a timeout or cancellation.
534
+ *
535
+ * @code
536
+ * Context ctxt(...);
537
+ * auto op = ctxt.discover([](const Discovered& evt) {
538
+ * std::cout<<evt<<std::endl;
539
+ * })
540
+ * .pingAll(false) // implied default
541
+ * .exec();
542
+ * op->wait(10.0); // wait 10 seconds, will always timeout.
543
+ * @endcode
544
+ *
545
+ * @since 0.3.0
546
+ */
547
+ inline
548
+ DiscoverBuilder discover(std::function<void(const Discovered &)> && fn);
549
+
550
+ /** Request prompt search of any disconnected channels.
551
+ *
552
+ * This method is recommended for use when executing a batch of operations.
553
+ *
554
+ * @code
555
+ * Context ctxt = ...;
556
+ * std::vector<std::string> pvnames = ...;
557
+ * std::vector<Operation> ops(pvnames.size());
558
+ *
559
+ * // Initiate all operations
560
+ * for(size_t i=0; i<pvname.size(); i++)
561
+ * ops[i] = ctxt.get(pvnames[i]).exec();
562
+ *
563
+ * ctxt.hurryUp(); // indicate end of batch
564
+ *
565
+ * for(size_t i=0; i<pvname.size(); i++)
566
+ * ... = ops[i].wait(); // wait for results
567
+ * @endcode
568
+ *
569
+ * Optional. Equivalent to detection of a new server.
570
+ * This method has no effect if called more often than once per 30 seconds.
571
+ */
572
+ void hurryUp();
573
+
574
+ #ifdef PVXS_EXPERT_API_ENABLED
575
+ //! Actions of cacheClear()
576
+ //! @since 0.2.0
577
+ enum cacheAction {
578
+ Clean, //!< Remove channel(s) if unused. Optional for user code.
579
+ Drop, //!< Remove channel(s) unconditionally. Prevents reuse of open channel(s).
580
+ Disconnect, //!< Remove channels(s) unconditionally, and cancel any in-progress operations.
581
+ };
582
+
583
+ /** Channel cache maintenance.
584
+ *
585
+ * @param action cf. cacheAction
586
+ *
587
+ * @since 0.2.0 'name' and 'action' arguments. Defaults to previous behavior.
588
+ */
589
+ void cacheClear(const std::string& name = std::string(), cacheAction action = Clean);
590
+
591
+ //! Ignore any search replies with these GUIDs
592
+ //! @since 0.2.0
593
+ void ignoreServerGUIDs(const std::vector<ServerGUID>& guids);
594
+
595
+ //! Compile report about peers and channels
596
+ //! @since 0.2.0
597
+ Report report(bool zero=true) const;
598
+ #endif
599
+
600
+ explicit operator bool() const { return pvt.operator bool(); }
601
+ size_t use_count() const { return pvt.use_count(); }
602
+ private:
603
+ std::shared_ptr<Pvt> pvt;
604
+ };
605
+
606
+ namespace detail {
607
+ struct PVRParser;
608
+
609
+ class PVXS_API CommonBase {
610
+ protected:
611
+ std::shared_ptr<Context::Pvt> ctx;
612
+ std::string _name;
613
+ std::string _server;
614
+ struct Req;
615
+ std::shared_ptr<Req> req;
616
+ unsigned _prio = 0u;
617
+ bool _autoexec = true;
618
+ bool _syncCancel = true;
619
+
620
+ CommonBase() {}
621
+ CommonBase(const std::shared_ptr<Context::Pvt>& ctx, const std::string& name) : ctx(ctx), _name(name) {}
622
+ ~CommonBase();
623
+
624
+ void _rawRequest(const Value&);
625
+ void _field(const std::string& s);
626
+ void _record(const std::string& key, const void* value, StoreType vtype);
627
+ void _parse(const std::string& req);
628
+ Value _buildReq() const;
629
+
630
+ friend struct PVRParser;
631
+ };
632
+
633
+ class PVXS_API PRBase : public CommonBase {
634
+ protected:
635
+ struct Args;
636
+ std::shared_ptr<Args> _args;
637
+
638
+ PRBase() {}
639
+ PRBase(const std::shared_ptr<Context::Pvt>& ctx, const std::string& name) : CommonBase(ctx, name) {}
640
+ ~PRBase();
641
+
642
+ void _set(const std::string& name, const void *ptr, StoreType type, bool required);
643
+ Value _builder(Value&& prototype) const;
644
+ Value _uriArgs() const;
645
+ };
646
+
647
+ //! Options common to all operations
648
+ template<typename SubBuilder, typename Base>
649
+ class CommonBuilder : public Base {
650
+ protected:
651
+ CommonBuilder() {}
652
+ constexpr CommonBuilder(const std::shared_ptr<Context::Pvt>& ctx, const std::string& name) : Base(ctx, name) {}
653
+ inline SubBuilder& _sb() { return static_cast<SubBuilder&>(*this); }
654
+ public:
655
+ //! Add field to pvRequest blob.
656
+ //! A more efficient alternative to @code pvRequest("field(name)") @endcode
657
+ SubBuilder& field(const std::string& fld) { this->_field(fld); return _sb(); }
658
+
659
+ /** Add a key/value option to the request.
660
+ *
661
+ * Well known options include:
662
+ *
663
+ * - queueSize : positive integer
664
+ * - block : bool
665
+ * - process : bool or string "true", "false", or "passive"
666
+ * - pipeline : bool
667
+ *
668
+ * A more efficient alternative to @code pvRequest("record[key=value]") @endcode
669
+ */
670
+ template<typename T>
671
+ SubBuilder& record(const std::string& name, const T& val) {
672
+ const typename impl::StoreAs<T>::store_t& norm(impl::StoreTransform<T>::in(val));
673
+ this->_record(name, &norm, impl::StoreAs<T>::code);
674
+ return _sb();
675
+ }
676
+
677
+ /** Parse pvRequest string.
678
+ *
679
+ * Supported syntax is a list of zero or more entities
680
+ * separated by zero or more spaces.
681
+ *
682
+ * - ``field(<fld.name>)``
683
+ * - ``record[<key>=\<value>]``
684
+ */
685
+ SubBuilder& pvRequest(const std::string& expr) { this->_parse(expr); return _sb(); }
686
+
687
+ //! Store raw pvRequest blob.
688
+ SubBuilder& rawRequest(const Value& r) { this->_rawRequest(r); return _sb(); }
689
+
690
+ SubBuilder& priority(int p) { this->_prio = p; return _sb(); }
691
+ SubBuilder& server(const std::string& s) { this->_server = s; return _sb(); }
692
+
693
+ #ifdef PVXS_EXPERT_API_ENABLED
694
+ // for GET/PUT control whether operations automatically proceed from INIT to EXEC
695
+ // cf. Operation::reExec()
696
+ SubBuilder& autoExec(bool b) { this->_autoexec = b; return _sb(); }
697
+ #endif
698
+
699
+ /** Controls whether Operation::cancel() and Subscription::cancel() synchronize.
700
+ *
701
+ * When true (the default) explicit or implicit cancel blocks until any
702
+ * in progress callback has completed. This makes safe some use of
703
+ * references in callbacks.
704
+ * @since 0.2.0
705
+ */
706
+ SubBuilder& syncCancel(bool b) { this->_syncCancel = b; return _sb(); }
707
+ };
708
+
709
+ } // namespace detail
710
+
711
+ //! Prepare a remote GET or GET_FIELD (info) operation.
712
+ //! See Context::get()
713
+ class GetBuilder : public detail::CommonBuilder<GetBuilder, detail::CommonBase> {
714
+ std::function<void (const Value&)> _onInit;
715
+ std::function<void(Result&&)> _result;
716
+ bool _get = false;
717
+ PVXS_API
718
+ std::shared_ptr<Operation> _exec_info();
719
+ PVXS_API
720
+ std::shared_ptr<Operation> _exec_get();
721
+ public:
722
+ GetBuilder() {}
723
+ GetBuilder(const std::shared_ptr<Context::Pvt>& ctx, const std::string& name, bool get) :CommonBuilder{ctx,name}, _get(get) {}
724
+ //! Callback through which result Value or an error will be delivered.
725
+ //! The functor is stored in the Operation returned by exec().
726
+ GetBuilder& result(std::function<void(Result&&)>&& cb) { _result = std::move(cb); return *this; }
727
+
728
+ #ifdef PVXS_EXPERT_API_ENABLED
729
+ // called during operation INIT phase for Get/Put/Monitor when remote type
730
+ // description is available.
731
+ GetBuilder& onInit(std::function<void (const Value&)>&& cb) { this->_onInit = std::move(cb); return *this; }
732
+ #endif
733
+
734
+ /** Execute the network operation.
735
+ * The caller must keep returned Operation pointer until completion
736
+ * or the operation will be implicitly canceled.
737
+ */
738
+ inline std::shared_ptr<Operation> exec() {
739
+ return _get ? _exec_get() : _exec_info();
740
+ }
741
+
742
+ friend struct Context::Pvt;
743
+ friend class Context;
744
+ };
745
+ GetBuilder Context::info(const std::string& name) { return GetBuilder{pvt, name, false}; }
746
+ GetBuilder Context::get(const std::string& name) { return GetBuilder{pvt, name, true}; }
747
+
748
+ //! Prepare a remote PUT operation
749
+ //! See Context::put()
750
+ class PutBuilder : public detail::CommonBuilder<PutBuilder, detail::PRBase> {
751
+ std::function<void (const Value&)> _onInit;
752
+ std::function<Value(Value&&)> _builder;
753
+ std::function<void(Result&&)> _result;
754
+ bool _doGet = true;
755
+ public:
756
+ PutBuilder() {}
757
+ PutBuilder(const std::shared_ptr<Context::Pvt>& ctx, const std::string& name) :CommonBuilder{ctx,name} {}
758
+
759
+ /** If fetchPresent is true (the default). Then the Value passed to
760
+ * the build() callback will be initialized with a previous value for this PV.
761
+ *
762
+ * This will be necessary for situation like NTEnum to fetch the choices list.
763
+ * But may be undesirable when writing to array fields to avoid
764
+ * the expense of fetching a copy of the array to be overwritten.
765
+ */
766
+ PutBuilder& fetchPresent(bool f) { _doGet = f; return *this; }
767
+
768
+ PutBuilder& set(const std::string& name, const void *ptr, StoreType type, bool required) {
769
+ _set(name, ptr, type, required);
770
+ return *this;
771
+ }
772
+
773
+ /** Utilize default .build() to assign a value to the named field.
774
+ *
775
+ * @param name The field name to attempt to assign.
776
+ * @param val The value to assign. cf. Value::from()
777
+ * @param required Whether to fail if this value can not be assigned to this field.
778
+ */
779
+ template<typename T>
780
+ PutBuilder& set(const std::string& name, const T& val, bool required=true)
781
+ {
782
+ const typename impl::StoreAs<T>::store_t& norm(impl::StoreTransform<T>::in(val));
783
+ return set(name, &norm, impl::StoreAs<T>::code, required);
784
+ }
785
+
786
+ /** Provide the builder callback.
787
+ *
788
+ * Once the PV type information is received from the server,
789
+ * this function will be responsible for populating a Value
790
+ * which will actually be sent.
791
+ *
792
+ * The functor is stored in the Operation returned by exec().
793
+ */
794
+ PutBuilder& build(std::function<Value(Value&&)>&& cb) { _builder = std::move(cb); return *this; }
795
+
796
+ /** Provide the operation result callback.
797
+ * This callback will be passed a Result which is either an empty Value (success)
798
+ * or an exception on error.
799
+ *
800
+ * The functor is stored in the Operation returned by exec().
801
+ */
802
+ PutBuilder& result(std::function<void(Result&&)>&& cb) { _result = std::move(cb); return *this; }
803
+
804
+ #ifdef PVXS_EXPERT_API_ENABLED
805
+ // called during operation INIT phase for Get/Put/Monitor when remote type
806
+ // description is available.
807
+ PutBuilder& onInit(std::function<void (const Value&)>&& cb) { this->_onInit = std::move(cb); return *this; }
808
+ #endif
809
+
810
+ /** Execute the network operation.
811
+ * The caller must keep returned Operation pointer until completion
812
+ * or the operation will be implicitly canceled.
813
+ */
814
+ PVXS_API
815
+ std::shared_ptr<Operation> exec();
816
+
817
+ friend struct Context::Pvt;
818
+ friend class Context;
819
+ };
820
+ PutBuilder Context::put(const std::string& name) { return PutBuilder{pvt, name}; }
821
+
822
+ //! Prepare a remote RPC operation.
823
+ //! See Context::rpc()
824
+ class RPCBuilder : public detail::CommonBuilder<RPCBuilder, detail::PRBase> {
825
+ Value _argument;
826
+ std::function<void(Result&&)> _result;
827
+ public:
828
+ RPCBuilder() {}
829
+ RPCBuilder(const std::shared_ptr<Context::Pvt>& ctx, const std::string& name) :CommonBuilder{ctx,name} {}
830
+ //! Callback through which result Value or an error will be delivered.
831
+ //! The functor is stored in the Operation returned by exec().
832
+ RPCBuilder& result(std::function<void(Result&&)>&& cb) { _result = std::move(cb); return *this; }
833
+
834
+ RPCBuilder& arg(const std::string& name, const void *ptr, StoreType type) {
835
+ _set(name, ptr, type, true);
836
+ return *this;
837
+ }
838
+
839
+ /** Provide argument value.
840
+ *
841
+ * @param name Argument name
842
+ * @param val The value to assign. cf. Value::from()
843
+ */
844
+ template<typename T>
845
+ RPCBuilder& arg(const std::string& name, const T& val)
846
+ {
847
+ const typename impl::StoreAs<T>::store_t& norm(impl::StoreTransform<T>::in(val));
848
+ _set(name, &norm, impl::StoreAs<T>::code, true);
849
+ return *this;
850
+ }
851
+
852
+ /** Execute the network operation.
853
+ * The caller must keep returned Operation pointer until completion
854
+ * or the operation will be implicitly canceled.
855
+ */
856
+ PVXS_API
857
+ std::shared_ptr<Operation> exec();
858
+
859
+ friend struct Context::Pvt;
860
+ friend class Context;
861
+ };
862
+ RPCBuilder Context::rpc(const std::string& name) { return RPCBuilder{pvt, name}; }
863
+ RPCBuilder Context::rpc(const std::string& name, const Value &arg) {
864
+ RPCBuilder ret{pvt, name};
865
+ ret._argument = arg;
866
+ return ret;
867
+ }
868
+
869
+ //! Prepare a remote subscription
870
+ //! See Context::monitor()
871
+ class MonitorBuilder : public detail::CommonBuilder<MonitorBuilder, detail::CommonBase> {
872
+ std::function<void(Subscription&, const Value&)> _onInit;
873
+ std::function<void(Subscription&)> _event;
874
+ bool _maskConn = true;
875
+ bool _maskDisconn = false;
876
+ public:
877
+ MonitorBuilder() {}
878
+ MonitorBuilder(const std::shared_ptr<Context::Pvt>& ctx, const std::string& name) :CommonBuilder{ctx,name} {}
879
+ /** Install FIFO not-empty event callback.
880
+ *
881
+ * This functor will be called each time the Subscription event queue becomes
882
+ * not empty. A Subscription becomes empty when Subscription::pop() returns
883
+ * an empty/invalid Value.
884
+ *
885
+ * The functor is stored in the Subscription returned by exec().
886
+ */
887
+ MonitorBuilder& event(std::function<void(Subscription&)>&& cb) { _event = std::move(cb); return *this; }
888
+ //! Include Connected exceptions in queue (default false).
889
+ MonitorBuilder& maskConnected(bool m = true) { _maskConn = m; return *this; }
890
+ //! Include Disconnected exceptions in queue (default true).
891
+ MonitorBuilder& maskDisconnected(bool m = true) { _maskDisconn = m; return *this; }
892
+
893
+ #ifdef PVXS_EXPERT_API_ENABLED
894
+ // called during operation INIT phase for Get/Put/Monitor when remote type
895
+ // description is available.
896
+ MonitorBuilder& onInit(std::function<void (Subscription&, const Value&)>&& cb) { this->_onInit = std::move(cb); return *this; }
897
+ #endif
898
+
899
+ //! Submit request to subscribe
900
+ PVXS_API
901
+ std::shared_ptr<Subscription> exec();
902
+
903
+ friend struct Context::Pvt;
904
+ friend class Context;
905
+ };
906
+ MonitorBuilder Context::monitor(const std::string& name) { return MonitorBuilder{pvt, name}; }
907
+
908
+ class RequestBuilder : public detail::CommonBuilder<RequestBuilder, detail::CommonBase>
909
+ {
910
+ public:
911
+ RequestBuilder() {}
912
+ //! Return composed pvRequest
913
+ Value build() const {
914
+ return _buildReq();
915
+ }
916
+ };
917
+ RequestBuilder Context::request() { return RequestBuilder{}; }
918
+
919
+ //! cf. Context::connect()
920
+ //! @since 0.2.0
921
+ class ConnectBuilder
922
+ {
923
+ std::shared_ptr<Context::Pvt> ctx;
924
+ std::string _pvname;
925
+ std::string _server;
926
+ std::function<void()> _onConn;
927
+ std::function<void()> _onDis;
928
+ bool _syncCancel = true;
929
+ public:
930
+ ConnectBuilder() {}
931
+ ConnectBuilder(const std::shared_ptr<Context::Pvt>& ctx, const std::string& pvname)
932
+ :ctx(ctx)
933
+ ,_pvname(pvname)
934
+ {}
935
+
936
+ //! Handler to be invoked when channel becomes connected.
937
+ ConnectBuilder& onConnect(std::function<void()>&& cb) { _onConn = std::move(cb); return *this; }
938
+ //! Handler to be invoked when channel becomes disconnected.
939
+ ConnectBuilder& onDisconnect(std::function<void()>&& cb) { _onDis = std::move(cb); return *this; }
940
+
941
+ /** Controls whether Connect::~Connect() synchronizes.
942
+ *
943
+ * When true (the default) explicit or implicit cancel blocks until any
944
+ * in progress callback has completed. This makes safe some use of
945
+ * references in callbacks.
946
+ * @since 0.2.0
947
+ */
948
+ ConnectBuilder& syncCancel(bool b) { this->_syncCancel = b; return *this; }
949
+
950
+ ConnectBuilder& server(const std::string& s) { this->_server = s; return *this; }
951
+
952
+ //! Submit request to connect
953
+ PVXS_API
954
+ std::shared_ptr<Connect> exec();
955
+ };
956
+ ConnectBuilder Context::connect(const std::string& pvname) { return ConnectBuilder{pvt, pvname}; }
957
+
958
+ //! Change of state event associated with a Context::discover()
959
+ struct Discovered {
960
+ //! What sort of event is this?
961
+ enum event_t : uint8_t {
962
+ Online=1, //!< Beacon from new server GUID
963
+ Timeout=2, //!< Beacon timeout for previous server
964
+ } event;
965
+ uint8_t peerVersion; //!< Last reported peer PVA protocol version.
966
+ std::string peer; //!< source of Beacon
967
+ std::string proto; //!< Advertised protocol. eg. "tcp"
968
+ std::string server;//!< Server protocol endpoint.
969
+ ServerGUID guid; //!< Server provided ID
970
+ epicsTime time; //!< Local system time of event
971
+ };
972
+ PVXS_API
973
+ std::ostream& operator<<(std::ostream& strm, const Discovered& evt);
974
+ //! Prepare a Context::discover() operation
975
+ //! @since 0.3.0
976
+ class DiscoverBuilder
977
+ {
978
+ std::shared_ptr<Context::Pvt> ctx;
979
+ std::function<void(const Discovered &)> _fn;
980
+ bool _syncCancel = true;
981
+ bool _ping = false;
982
+ public:
983
+ DiscoverBuilder(const std::shared_ptr<Context::Pvt>& ctx, std::function<void(const Discovered &)>&& fn)
984
+ :ctx(ctx)
985
+ ,_fn(fn)
986
+ {}
987
+
988
+ /** Controls whether client will actively seek to immediately discover all servers.
989
+ *
990
+ * If false, then client will only wait for servers to periodically announce themselves.
991
+ */
992
+ DiscoverBuilder& pingAll(bool b) { this->_ping = b; return *this; }
993
+
994
+ /** Controls whether Operation::cancel() synchronizes.
995
+ *
996
+ * When true (the default) explicit or implicit cancel blocks until any
997
+ * in progress callback has completed. This makes safe some use of
998
+ * references in callbacks.
999
+ */
1000
+ DiscoverBuilder& syncCancel(bool b) { this->_syncCancel = b; return *this; }
1001
+
1002
+ //! Execute. The returned Operation will never complete.
1003
+ PVXS_API
1004
+ std::shared_ptr<Operation> exec();
1005
+ };
1006
+ DiscoverBuilder Context::discover(std::function<void (const Discovered &)> && fn) { return DiscoverBuilder(pvt, std::move(fn)); }
1007
+
1008
+ struct PVXS_API Config {
1009
+ /** List of unicast, multicast, and broadcast addresses to which search requests will be sent.
1010
+ *
1011
+ * Entries may take the forms:
1012
+ * - ``<ipaddr>[:<port#>]``
1013
+ * - ``<ipmultiaddr>[:<port>][,<ttl>][@<ifaceaddr>]``
1014
+ */
1015
+ std::vector<std::string> addressList;
1016
+
1017
+ //! List of local interface addresses on which beacons may be received.
1018
+ //! Also constrains autoAddrList to only consider broadcast addresses of listed interfaces.
1019
+ //! Empty implies wildcard 0.0.0.0
1020
+ std::vector<std::string> interfaces;
1021
+
1022
+ //! List of TCP name servers.
1023
+ //! Client context will maintain connections, and send search requests, to these servers.
1024
+ //! @since 0.2.0
1025
+ std::vector<std::string> nameServers;
1026
+
1027
+ //! UDP port to bind. Default is 5076. May be zero, cf. Server::config() to find allocated port.
1028
+ unsigned short udp_port = 5076;
1029
+ //! Default TCP port for name servers
1030
+ //! @since 0.2.0
1031
+ unsigned short tcp_port = 5075;
1032
+
1033
+ //! Whether to extend the addressList with local interface broadcast addresses. (recommended)
1034
+ bool autoAddrList = true;
1035
+
1036
+ //! Inactivity timeout interval for TCP connections. (seconds)
1037
+ //! @since 0.2.0
1038
+ double tcpTimeout = 40.0;
1039
+
1040
+ private:
1041
+ bool BE = EPICS_BYTE_ORDER==EPICS_ENDIAN_BIG;
1042
+ bool UDP = true;
1043
+ public:
1044
+
1045
+ // compat
1046
+ static inline Config from_env() { return Config{}.applyEnv(); }
1047
+
1048
+ //! Default configuration using process environment
1049
+ static inline Config fromEnv() { return Config{}.applyEnv(); }
1050
+
1051
+ //! update using defined EPICS_PVA* environment variables
1052
+ Config& applyEnv();
1053
+
1054
+ typedef std::map<std::string, std::string> defs_t;
1055
+ //! update with definitions as with EPICS_PVA* environment variables
1056
+ //! Process environment is not changed.
1057
+ Config& applyDefs(const defs_t& defs);
1058
+
1059
+ //! extract definitions with environment variable names as keys.
1060
+ //! Process environment is not changed.
1061
+ void updateDefs(defs_t& defs) const;
1062
+
1063
+ /** Apply rules to translate current requested configuration
1064
+ * into one which can actually be loaded based on current host network configuration.
1065
+ *
1066
+ * Explicit use of expand() is optional as the Context ctor expands any Config given.
1067
+ * expand() is provided as a aid to help understand how Context::effective() is arrived at.
1068
+ *
1069
+ * @post autoAddrList==false
1070
+ */
1071
+ void expand();
1072
+
1073
+ //! Create a new client Context using the current configuration.
1074
+ inline
1075
+ Context build() const {
1076
+ return Context(*this);
1077
+ }
1078
+
1079
+ #ifdef PVXS_EXPERT_API_ENABLED
1080
+ // for protocol compatibility testing
1081
+ inline Config& overrideSendBE(bool be) { BE = be; return *this; }
1082
+ inline bool sendBE() const { return BE; }
1083
+ inline Config& overrideShareUDP(bool share) { UDP = share; return *this; }
1084
+ inline bool shareUDP() const { return UDP; }
1085
+ #endif
1086
+ };
1087
+
1088
+ PVXS_API
1089
+ std::ostream& operator<<(std::ostream& strm, const Config& conf);
1090
+
1091
+ } // namespace client
1092
+ } // namespace pvxs
1093
+
1094
+ #endif // PVXS_CLIENT_H