pvxslibs 1.4.0a1__cp313-cp313-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.

Potentially problematic release.


This version of pvxslibs might be problematic. Click here for more details.

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