This is Unofficial EPICS BASE Doxygen Site
pvget.cpp
Go to the documentation of this file.
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 #include <iostream>
6 #include <vector>
7 #include <set>
8 #include <deque>
9 #include <string>
10 #include <istream>
11 #include <fstream>
12 #include <sstream>
13 
14 #include <stdio.h>
15 
16 #include <epicsStdlib.h>
17 #include <epicsGetopt.h>
18 #include <epicsExit.h>
19 #include <epicsGuard.h>
20 
21 #include <pv/pvData.h>
22 #include <pv/logger.h>
23 #include <pv/lock.h>
24 #include <pv/event.h>
25 #include <pv/thread.h>
26 #include <pv/reftrack.h>
27 
28 #include <pv/caProvider.h>
29 #include <pv/logger.h>
30 #include <pva/client.h>
31 
32 #include "pvutils.h"
33 
34 #ifndef EXECNAME
35 # define EXECNAME "pvget"
36 #endif
37 
38 namespace {
39 
40 size_t pvnamewidth;
41 
42 int haderror;
43 
44 void usage (void)
45 {
46  fprintf (stderr, "\nUsage: " EXECNAME " [options] <PV name>...\n"
47  "\n"
49  " deprecated options:\n"
50  " -q, -t, -i, -n, -F: ignored\n"
51  " -f <input file>: errors\n"
52  " Output details:\n"
53  " -m -v: Monitor in Raw mode. Print only fields marked as changed.\n"
54  " -m -vv: Monitor in Raw mode. Highlight fields marked as changed, show all valid fields.\n"
55  " -m -vvv: Monitor in Raw mode. Highlight fields marked as changed, show all fields.\n"
56  " -vv: Get in Raw mode. Highlight valid fields, show all fields.\n"
57  "\n"
58  "example: " EXECNAME " double01\n\n"
59  , request.c_str(), timeout, defaultProvider.c_str());
60 }
61 
62 struct Getter : public pvac::ClientChannel::GetCallback, public Tracker
63 {
64  POINTER_DEFINITIONS(Getter);
65 
67 
68  Getter(pvac::ClientChannel& channel, const pvd::PVStructurePtr& pvRequest)
69  {
70  op = channel.get(this, pvRequest);
71  }
72  virtual ~Getter() {}
73 
74  virtual void getDone(const pvac::GetEvent& event) OVERRIDE FINAL
75  {
76  std::cout<<std::setw(pvnamewidth)<<std::left<<op.name()<<' ';
77  switch(event.event) {
79  std::cerr<<"Error "<<event.message<<"\n";
80  haderror = 1;
81  break;
83  break;
85  pvd::PVStructure::Formatter fmt(event.value->stream()
86  .format(outmode));
87 
88  if(verbosity>=2)
89  fmt.highlight(*event.valid); // show all, highlight valid
90  else
91  fmt.show(*event.valid); // only show valid, highlight none
92 
93  std::cout<<fmt;
94  }
95  break;
96  }
97  std::cout.flush();
98  done();
99  }
100 };
101 
102 
103 
104 struct Worker {
105  virtual ~Worker() {}
106  virtual void process(const pvac::MonitorEvent& event) =0;
107 };
108 
109 // simple work queue with thread.
110 // moves monitor queue handling off of PVA thread(s)
111 struct WorkQueue : public epicsThreadRunable {
113  typedef std::tr1::shared_ptr<Worker> value_type;
114  typedef std::tr1::weak_ptr<Worker> weak_type;
115  // work queue holds only weak_ptr
116  // so jobs must be kept alive seperately
117  typedef std::deque<std::pair<weak_type, pvac::MonitorEvent> > queue_t;
118  queue_t queue;
119  epicsEvent event;
120  bool running;
121  pvd::Thread worker;
122 
123  WorkQueue()
124  :running(true)
125  ,worker(pvd::Thread::Config()
126  .name("Monitor handler")
127  .autostart(true)
128  .run(this))
129  {}
130  ~WorkQueue() {close();}
131 
132  void close()
133  {
134  {
135  Guard G(mutex);
136  running = false;
137  }
138  event.signal();
139  worker.exitWait();
140  }
141 
142  void push(const weak_type& cb, const pvac::MonitorEvent& evt)
143  {
144  bool wake;
145  {
146  Guard G(mutex);
147  if(!running) return; // silently refuse to queue during/after close()
148  wake = queue.empty();
149  queue.push_back(std::make_pair(cb, evt));
150  }
151  if(wake)
152  event.signal();
153  }
154 
155  virtual void run() OVERRIDE FINAL
156  {
157  Guard G(mutex);
158 
159  while(running) {
160  if(queue.empty()) {
161  UnGuard U(G);
162  event.wait();
163  } else {
164  queue_t::value_type ent(queue.front());
165  value_type cb(ent.first.lock());
166  queue.pop_front();
167  if(!cb) continue;
168 
169  try {
170  UnGuard U(G);
171  cb->process(ent.second);
172  }catch(std::exception& e){
173  std::cout<<"Error in monitor handler : "<<e.what()<<"\n";
174  }
175  }
176  }
177  }
178 };
179 
180 
181 struct MonTracker : public pvac::ClientChannel::MonitorCallback,
182  public Worker,
183  public Tracker,
184  public std::tr1::enable_shared_from_this<MonTracker>
185 {
186  POINTER_DEFINITIONS(MonTracker);
187 
188  MonTracker(WorkQueue& monwork, pvac::ClientChannel& channel, const pvd::PVStructurePtr& pvRequest)
189  :monwork(monwork)
190  ,mon(channel.monitor(this, pvRequest))
191  {}
192  virtual ~MonTracker() {mon.cancel();}
193 
194  WorkQueue& monwork;
195 
196  pvd::BitSet valid; // only access for process()
197 
198  pvac::Monitor mon; // must be last data member
199 
200  virtual void monitorEvent(const pvac::MonitorEvent& evt) OVERRIDE FINAL
201  {
202  // shared_from_this() will fail as Cancel is delivered in our dtor.
203  if(evt.event==pvac::MonitorEvent::Cancel) return;
204 
205  // running on internal provider worker thread
206  // minimize work here.
207  monwork.push(shared_from_this(), evt);
208  }
209 
210  virtual void process(const pvac::MonitorEvent& evt) OVERRIDE FINAL
211  {
212  // running on our worker thread
213  switch(evt.event) {
215  std::cerr<<std::setw(pvnamewidth)<<std::left<<mon.name()<<" Error "<<evt.message<<"\n";
216  haderror = 1;
217  done();
218  break;
220  break;
222  std::cout<<std::setw(pvnamewidth)<<std::left<<mon.name()<<" <Disconnect>\n";
223  valid.clear();
224  break;
226  {
227  unsigned n;
228  for(n=0; n<2 && mon.poll(); n++) {
229  valid |= mon.changed;
230 
231  pvd::PVStructure::Formatter fmt(mon.root->stream()
232  .format(outmode));
233 
234  if(verbosity>=3)
235  fmt.highlight(mon.changed); // show all
236  else if(verbosity>=2)
237  fmt.highlight(mon.changed).show(valid);
238  else
239  fmt.show(mon.changed); // highlight none
240 
241  std::cout<<std::setw(pvnamewidth)<<std::left<<mon.name()<<' '<<fmt;
242  }
243  if(n==2) {
244  // too many updates, re-queue to balance with others
245  monwork.push(shared_from_this(), evt);
246  } else if(n==0) {
247  LOG(pva::logLevelDebug, "%s Spurious Data event on channel", mon.name().c_str());
248  } else {
249  if(mon.complete())
250  done();
251  }
252  }
253  break;
254  }
255  std::cout.flush();
256  }
257 };
258 
259 } // namespace
260 
261 #ifndef MAIN
262 # define MAIN main
263 #endif
264 
265 int MAIN (int argc, char *argv[])
266 {
267  try {
268  int opt; /* getopt() current option */
269 #ifdef PVMONITOR
270  bool monitor = true;
271 #else
272  bool monitor = false;
273 #endif
274 
275  epics::RefMonitor refmon;
276 
277  // ================ Parse Arguments
278 
279  while ((opt = getopt(argc, argv, ":hvVRM:r:w:tmp:qdcF:f:ni")) != -1) {
280  switch (opt) {
281  case 'h': /* Print usage */
282  usage();
283  return 0;
284  case 'v':
285  verbosity++;
286  break;
287  case 'V': /* Print version */
288  {
289  fprintf(stdout, "pvAccess %u.%u.%u%s\n",
290  EPICS_PVA_MAJOR_VERSION,
291  EPICS_PVA_MINOR_VERSION,
292  EPICS_PVA_MAINTENANCE_VERSION,
293  (EPICS_PVA_DEVELOPMENT_FLAG)?"-SNAPSHOT":"");
294  fprintf(stdout, "pvData %u.%u.%u%s\n",
295  EPICS_PVD_MAJOR_VERSION,
296  EPICS_PVD_MINOR_VERSION,
297  EPICS_PVD_MAINTENANCE_VERSION,
298  (EPICS_PVD_DEVELOPMENT_FLAG)?"-SNAPSHOT":"");
299  fprintf(stdout, "Base %s\n", EPICS_VERSION_FULL);
300  return 0;
301  }
302  case 'R':
303  refmon.start(5.0);
304  break;
305  case 'M':
306  if(strcmp(optarg, "raw")==0) {
307  outmode = pvd::PVStructure::Formatter::Raw;
308  } else if(strcmp(optarg, "nt")==0) {
309  outmode = pvd::PVStructure::Formatter::NT;
310  } else if(strcmp(optarg, "json")==0) {
311  outmode = pvd::PVStructure::Formatter::JSON;
312  } else {
313  fprintf(stderr, "Unknown output mode '%s'\n", optarg);
314  outmode = pvd::PVStructure::Formatter::Raw;
315  }
316  break;
317  case 'w': /* Set PVA timeout value */
318  {
319  double temp;
320  if((epicsScanDouble(optarg, &temp)) != 1)
321  {
322  fprintf(stderr, "'%s' is not a valid timeout value "
323  "- ignored. ('" EXECNAME " -h' for help.)\n", optarg);
324  } else {
325  timeout = temp;
326  }
327  }
328  break;
329  case 'r': /* Set PVA timeout value */
330  request = optarg;
331  break;
332  case 't': /* Terse mode */
333  case 'i': /* T-types format mode */
334  case 'F': /* Store this for output formatting */
335  case 'n':
336  case 'q': /* Quiet mode */
337  // deprecate
338  break;
339  case 'f': /* Use input stream as input */
340  fprintf(stderr, "Unsupported option -f\n");
341  return 1;
342  case 'm': /* Monitor mode */
343  monitor = true;
344  break;
345  case 'p': /* Set default provider */
347  break;
348  case 'd': /* Debug log level */
349  debugFlag = true;
350  break;
351  case 'c': /* Clean-up and report used instance count */
352  break;
353  case '?':
354  fprintf(stderr,
355  "Unrecognized option: '-%c'. ('" EXECNAME " -h' for help.)\n",
356  optopt);
357  return 1;
358  case ':':
359  fprintf(stderr,
360  "Option '-%c' requires an argument. ('" EXECNAME " -h' for help.)\n",
361  optopt);
362  return 1;
363  default :
364  usage();
365  return 1;
366  }
367  }
368 
369  if(monitor)
370  timeout = -1;
371 
372  if(verbosity>0 && outmode==pvd::PVStructure::Formatter::NT)
373  outmode = pvd::PVStructure::Formatter::Raw;
374 
375  pvd::PVStructure::shared_pointer pvRequest;
376  try {
377  pvRequest = pvd::createRequest(request);
378  } catch(std::exception& e){
379  fprintf(stderr, "failed to parse request string: %s\n", e.what());
380  return 1;
381  }
382 
383  for(int i = optind; i < argc; i++) {
384  pvnamewidth = std::max(pvnamewidth, strlen(argv[i]));
385  }
386 
388 
390 
391  {
393 
394  std::vector<std::tr1::shared_ptr<Tracker> > tracked;
395 
396  epics::auto_ptr<WorkQueue> Q;
397  if(monitor)
398  Q.reset(new WorkQueue);
399 
400  for(int i = optind; i < argc; i++) {
401  pvac::ClientChannel chan(provider.connect(argv[i]));
402 
403  if(monitor) {
404  std::tr1::shared_ptr<MonTracker> mon(new MonTracker(*Q, chan, pvRequest));
405 
406  tracked.push_back(mon);
407 
408  } else { // Get
409  std::tr1::shared_ptr<Getter> get(new Getter(chan, pvRequest));
410 
411  tracked.push_back(get);
412  }
413  }
414 
415  // ========================== Wait for operations to complete, or timeout
416 
417  Tracker::prepare(); // install signal handler
418 
419  if(debugFlag)
420  std::cerr<<"Waiting...\n";
421 
422  {
424  while(Tracker::inprog.size() && !Tracker::abort) {
425  UnGuard U(G);
426  if(timeout<=0)
427  Tracker::doneEvt.wait();
428  else if(!Tracker::doneEvt.wait(timeout)) {
429  haderror = 1;
430  std::cerr<<"Timeout\n";
431  break;
432  }
433  }
434  }
435  }
436 
437  if(refmon.running()) {
438  refmon.stop();
439  // show final counts
440  refmon.current();
441  }
442 
443  // ========================== All done now
444 
445  if(debugFlag)
446  std::cerr<<"Done\n";
447 
448  return haderror ? 1 : 0;
449  } catch(std::exception& e) {
450  std::cerr<<"Error: "<<e.what()<<"\n";
451  return 1;
452  }
453 }
double timeout
Definition: pvutils.cpp:25
std::string message
set for event=Fail
Definition: client.h:192
int getopt(int nargc, char *const *nargv, const char *ostr)
Definition: epicsGetopt.c:65
#define max(x, y)
Definition: flexdef.h:81
virtual void getDone(const GetEvent &evt)=0
get or rpc operation is complete
std::string request
static epicsMutex doneLock
Definition: pvutils.h:47
BitSet & clear(uint32 bitIndex)
Definition: bitSet.cpp:112
#define EXECNAME
Definition: pvget.cpp:35
int i
Definition: scan.c:967
Information on get/rpc completion.
Definition: client.h:99
int optind
Definition: epicsGetopt.c:50
std::string name() const
Channel name.
Definition: client.cpp:131
static void prepare()
Definition: pvutils.cpp:49
#define SET_LOG_LEVEL(level)
Definition: logger.h:50
static epicsEvent doneEvt
Definition: pvutils.h:48
subscription interrupted due to loss of communication
Definition: client.h:189
A vector of bits.
Definition: bitSet.h:56
Operation get(GetCallback *cb, epics::pvData::PVStructure::const_shared_pointer pvRequest=epics::pvData::PVStructure::const_shared_pointer())
Definition: clientGet.cpp:151
Handle for monitor subscription.
Definition: client.h:117
callback for get() and rpc()
Definition: client.h:319
#define epicsScanDouble(str, to)
Definition: epicsStdlib.h:78
#define OVERRIDE
Definition: pvAccess.h:55
PVStructure::shared_pointer createRequest(std::string const &request)
Handle for in-progress get/put/rpc operation.
Definition: client.h:50
subscription ends in cancellation
Definition: client.h:188
int optopt
Definition: epicsGetopt.c:50
epics::pvData::PVStructure::const_shared_pointer root
Definition: client.h:160
epics::pvData::PVStructure::const_shared_pointer value
New data. NULL unless event==Success.
Definition: client.h:102
#define LOG(level, format,...)
Definition: logger.h:48
epics::pvData::BitSet::const_shared_pointer valid
Definition: client.h:105
#define POINTER_DEFINITIONS(clazz)
Definition: sharedPtr.h:198
static inprog_t inprog
Definition: pvutils.h:50
epicsMutex mutex
Definition: pvAccess.cpp:71
enum pvac::PutEvent::event_t event
Extended replacement for the Posix exit and atexit routines.
int verbosity
Definition: pvutils.cpp:29
#define stdout
Definition: epicsStdio.h:30
Central client context.
Definition: client.h:517
request cancelled before completion
Definition: client.h:92
#define MAIN
Definition: pvget.cpp:262
#define COMMON_OPTIONS
Definition: pvutils.h:25
std::tr1::shared_ptr< PVStructure > PVStructurePtr
Definition: pvData.h:87
std::string name() const
Channel name.
Information on monitor subscription/queue change.
Definition: client.h:184
ClientChannel connect(const std::string &name, const ClientChannel::Options &conf=ClientChannel::Options())
Definition: client.cpp:295
request ends in failure. Check message
Definition: client.h:91
enum pvac::MonitorEvent::event_t event
void done(int k)
Definition: antelope.c:77
void done()
Definition: pvutils.h:62
C++ wrapper for epicsThread from EPICS base.
Definition: thread.h:65
#define stderr
Definition: epicsStdio.h:32
ChannelPut::shared_pointer op
Definition: pvAccess.cpp:132
Data queue not empty. Call Monitor::poll()
Definition: client.h:190
bool debugFlag
Definition: pvutils.cpp:26
Monitor event notification.
Definition: client.h:403
epics::pvData::BitSet changed
Definition: client.h:161
static void start()
start provider ca
Definition: caProvider.cpp:201
std::string defaultProvider
char * optarg
Definition: epicsGetopt.c:55
static bool abort
Definition: pvutils.h:51
void usage(void)
Definition: cainfo.c:36
pvd::PVStructure::Formatter::format_t outmode
Definition: pvutils.cpp:28
It worked!
Definition: client.h:93
#define FINAL
Definition: pvAccess.h:48
bool complete() const
subscription ends in an error
Definition: client.h:187
Monitor monitor(MonitorCallback *cb, epics::pvData::PVStructure::const_shared_pointer pvRequest=epics::pvData::PVStructure::const_shared_pointer())