This is Unofficial EPICS BASE Doxygen Site
printer.cpp
Go to the documentation of this file.
1 /* printer.cpp */
2 /*
3  * Copyright information and license terms for this software can be
4  * found in the file LICENSE that is included with the distribution
5  */
6 
7 #include <stdio.h>
8 #include <ctype.h>
9 #if defined(__linux__) || defined(__APPLE__)
10 #include <unistd.h>
11 #define HAVE_ISATTY
12 #endif
13 
14 #include <deque>
15 
16 #include <epicsTime.h>
17 #include <epicsString.h>
18 
19 #define epicsExportSharedSymbols
20 #include <pv/bitSet.h>
21 #include <pv/pvData.h>
22 #include <pv/json.h>
23 
24 namespace epics { namespace pvData {
25 
26 namespace format
27 {
28 static int indent_index = std::ios_base::xalloc();
29 
30 long& indent_value(std::ios_base& ios)
31 {
32  return ios.iword(indent_index);
33 }
34 
35 std::ostream& operator<<(std::ostream& os, indent_level const& indent)
36 {
37  indent_value(os) = indent.level;
38  return os;
39 }
40 
41 std::ostream& operator<<(std::ostream& os, indent const&)
42 {
43  long il = indent_value(os);
44  for(long i=0, spaces = il * 4; i<spaces; i++)
45  os.put(' ');
46  return os;
47 }
48 
49 array_at_internal operator<<(std::ostream& str, array_at const& manip)
50 {
51  return array_at_internal(manip.index, str);
52 }
53 }
54 
55 }} //epics::pvData
56 namespace {
57 using namespace epics::pvData;
58 
59 bool useEscapes(std::ostream& strm) {
60  FILE *fp = 0;
61  // TODO: a better way to do this...
62  if(&std::cout==&strm)
63  fp = stdout;
64  if(&std::cerr==&strm)
65  fp = stderr;
66  if(!fp) return false;
67 #ifdef HAVE_ISATTY
68  // check $TERM as well?
69  return isatty(fileno(fp))==1;
70 #else
71  // TODO: in theory windows 10 can be made to understand escapes as well
72  return false;
73 #endif
74 }
75 
76 
77 void printAlarmTx(std::ostream& strm, const PVStructure& sub)
78 {
79  PVScalar::const_shared_pointer pvSeverity(sub.getSubField<PVInt>("severity"));
80  PVScalar::const_shared_pointer pvStatus(sub.getSubField<PVInt>("status"));
81  PVString::const_shared_pointer pvMessage(sub.getSubField<PVString>("message"));
82 
83  switch(pvSeverity ? pvSeverity->getAs<int32>() : 0) {
84  case 0: return; // nothing more to do here...
85  case 1: strm<<"MINOR "; break;
86  case 2: strm<<"MAJOR "; break;
87  case 3: strm<<"INVALID "; break;
88  case 4: strm<<"UNDEFINED "; break;
89  default: strm<<pvSeverity->getAs<int32>()<<' ';
90  }
91 
92  switch(pvStatus ? pvStatus->getAs<int32>() : 0) {
93  case 0: break;
94  case 1: strm<<"DEVICE "; break;
95  case 2: strm<<"DRIVER "; break;
96  case 3: strm<<"RECORD "; break;
97  case 4: strm<<"DB "; break;
98  case 5: strm<<"CONF "; break;
99  case 6: strm<<"UNDEFINED "; break;
100  case 7: strm<<"CLIENT "; break;
101  default: strm<<pvStatus->getAs<int32>()<<' ';
102  }
103 
104  if(pvMessage && !pvMessage->get().empty())
105  strm<<pvMessage->get()<<' ';
106 }
107 
108 
109 void printAlarmT(std::ostream& strm, const PVStructure& top)
110 {
111  PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("alarm"));
112  if(sub)
113  printAlarmTx(strm, *sub);
114 }
115 
116 void printTimeTx(std::ostream& strm, const PVStructure& tsubop)
117 {
118  char timeText[32];
119  epicsTimeStamp epicsTS;
120 
121  PVScalar::const_shared_pointer secf(tsubop.getSubField<PVScalar>("secondsPastEpoch")),
122  nsecf(tsubop.getSubField<PVScalar>("nanoseconds")),
123  tagf(tsubop.getSubField<PVScalar>("userTag"));
124 
125  epicsTS.secPastEpoch = secf ? secf->getAs<int64>() : 0;
126  epicsTS.nsec = nsecf ? nsecf->getAs<int32>() : 0;
127 
130  else
131  epicsTS.secPastEpoch = 0;
132 
133  epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%d %H:%M:%S.%03f", &epicsTS);
134  strm <<std::setw(24) <<std::left <<timeText <<' ';
135  if (tagf) {
136  int64 tagv = tagf->getAs<int64>();
137  if(tagv)
138  strm << tagv << ' ';
139  }
140 }
141 
142 
143 void printTimeT(std::ostream& strm, const PVStructure& top)
144 {
145  PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("timeStamp"));
146  if(sub)
147  printTimeTx(strm, *sub);
148 }
149 
150 bool printEnumT(std::ostream& strm, const PVStructure& top, bool fromtop)
151 {
152  PVStructure::const_shared_pointer value;
153  if(fromtop) {
154  value = top.getSubField<PVStructure>("value");
155  } else {
156  value = std::tr1::static_pointer_cast<const PVStructure>(top.shared_from_this());
157  }
158  PVScalar::const_shared_pointer idx(value->getSubField<PVScalar>("index"));
159  PVStringArray::const_shared_pointer choices(value->getSubField<PVStringArray>("choices"));
160  if(!idx || !choices) return false;
161 
162  if(fromtop) {
163  strm<<format::indent();
164  printTimeT(strm, top);
165  printAlarmT(strm, top);
166  }
167 
168  PVStringArray::const_svector ch(choices->view());
169  uint32 I = idx->getAs<uint32>();
170  strm<<'('<<I<<')';
171  if(I>=ch.size()) {
172  strm<<" <undefined>";
173  } else {
174  strm<<' '<<escape(ch[I]);
175  }
176  return true;
177 }
178 
179 void csvEscape(std::string& S)
180 {
181  // concise, not particularly efficient...
182  std::string temp(escape(S).style(escape::CSV).str());
183  if(S.find_first_of(" ,\\")!=S.npos) {// only quote if necessary (stupid Excel)
184  std::string temp2;
185  temp2.reserve(temp.size()+2);
186  temp2.push_back('\"');
187  temp2 += temp;
188  temp2.push_back('\"');
189  temp2.swap(temp);
190  }
191  S = temp;
192 }
193 
194 bool printTable(std::ostream& strm, const PVStructure& top)
195 {
196  PVStructure::const_shared_pointer cf(top.getSubField<PVStructure>("value"));
197  if(!cf) return false;
198 
199  {
200  const FieldConstPtrArray& fields = cf->getStructure()->getFields();
201  if(fields.empty()) return false;
202  for(size_t i=0, N=fields.size(); i<N; i++) {
203  if(fields[i]->getType()!=scalarArray)
204  return false; // cowardly refusing to handle anything else...
205  }
206  }
207 
208  // maybe output a line with meta-data
209  {
210  bool havets = !!top.getSubField("timeStamp");
211  bool haveal = !!top.getSubField("alarm");
212  if(havets || haveal)
213  strm<<format::indent();
214  if(havets) {
215  printTimeT(strm, top);
216  strm<<' ';
217  }
218  if(haveal) {
219  printAlarmT(strm, top);
220  strm<<' ';
221  }
222  strm<<'\n';
223  }
224 
225  PVStringArray::svector labels;
226  {
227  PVStringArray::const_shared_pointer lf(top.getSubField<PVStringArray>("labels"));
228  if(lf) {
229  PVStringArray::const_svector L(lf->view());
230  labels = thaw(L);
231  }
232  }
233 
234  const PVFieldPtrArray& columns = cf->getPVFields();
235  std::vector<shared_vector<std::string> > coldat(columns.size());
236 
237  std::vector<size_t> widths(columns.size());
238  labels.reserve(columns.size());
239 
240  size_t nrows = size_t(-1);
241 
242  for(size_t i=0, N=columns.size(); i<N; i++) {
243  if(i>=labels.size()) {
244  labels.push_back(cf->getStructure()->getFieldName(i));
245  } else {
246  csvEscape(labels[i]);
247  }
248  widths[i] = labels[i].size();
249 
250  {
252  static_cast<const PVScalarArray*>(columns[i].get())->getAs(ro);
253  coldat[i] = thaw(ro);
254  }
255 
256  // truncate if some columns are longer than others
257  nrows = std::min(nrows, coldat[i].size());
258 
259  for(size_t j=0, M=coldat[i].size(); j<M; j++) {
260  csvEscape(coldat[i][j]);
261  widths[i] = std::max(widths[i], coldat[i][j].size());
262  }
263  }
264 
265  // output header line
266  strm<<format::indent();
267  for(size_t c=0, N=coldat.size(); c<N; c++) {
268  strm<<std::setw(widths[c])<<std::right<<labels[c];
269  if(c+1!=N) {
270  strm<<' ';
271  }
272  }
273  strm<<'\n';
274 
275  for(size_t r=0; r<nrows; r++) {
276  strm<<format::indent();
277  for(size_t c=0, N=coldat.size(); c<N; c++) {
278  strm<<std::setw(widths[c])<<std::right<<coldat[c][r];
279  if(c+1!=N)
280  strm<<' ';
281  }
282  strm<<'\n';
283  }
284 
285  return true;
286 }
287 
288 void expandBS(const PVStructure& top, BitSet& mask, bool parents) {
289  if(mask.get(0)) { // special handling because getSubField(0) not allowed
290  // wildcard
291  for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
292  mask.set(idx);
293  }
294 
295  } else {
296  for(int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
297  PVField::const_shared_pointer fld = top.getSubFieldT(idx);
298 
299  // look forward and mark all children
300  for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
301  mask.set(i);
302 
303  if(parents) {
304  // look back and mark all parents
305  // we've already stepped past all parents so siblings will not be automatically marked
306  for(const PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
307  mask.set(parent->getFieldOffset());
308  }
309  }
310  }
311  }
312 }
313 
314 }
315 namespace epics { namespace pvData {
316 
317 void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur)
318 {
319  typedef std::deque<std::pair<const PVField*, size_t> > todo_t;
320  todo_t todo;
321  BitSet show, highlight;
322 
323  const long orig_indent = format::indent_value(strm);
324 
325  {
326  if(format.xshow)
327  show = *format.xshow;
328  else
329  show.set(0);
330 
331  if(format.xhighlight)
332  highlight = *format.xhighlight;
333 
334  expandBS(format.xtop, show, true);
335  expandBS(format.xtop, highlight, false);
336  highlight &= show; // can't highlight hidden fields (paranoia)
337  }
338 
339  if(!show.get(0)) return; // nothing to do here...
340 
341  todo.push_front(std::make_pair(&format.xtop, orig_indent));
342 
343  while(!todo.empty()) {
344  todo_t::value_type cur(todo.front());
345  todo.pop_front();
346 
347  format::indent_value(strm) = cur.second;
348 
349  bool hl = highlight.get(cur.first->getFieldOffset()) && format.xmode==PVStructure::Formatter::ANSI;
350  if(hl)
351  strm<<"\x1b[1m"; // Bold
352 
353  switch(cur.first->getField()->getType()) {
354  case structure: {
355  const PVStructure* str = static_cast<const PVStructure*>(cur.first);
356 
357  const PVFieldPtrArray& flds = str->getPVFields();
358 
359  for(PVFieldPtrArray::const_reverse_iterator it(flds.rbegin()), end(flds.rend()); it!=end; ++it) {
360  const PVField *fld = (*it).get();
361  if(!show.get(fld->getFieldOffset())) continue;
362 
363  todo.push_front(std::make_pair(fld, cur.second+1));
364  }
365 
366  std::string id(cur.first->getField()->getID());
367 
368  strm<<format::indent()<<id<<' '<<cur.first->getFieldName();
369  if(id=="alarm_t") {
370  strm.put(' ');
371  printAlarmTx(strm, *str);
372  } else if(id=="time_t") {
373  strm.put(' ');
374  printTimeTx(strm, *str);
375  } else if(id=="enum_t") {
376  strm.put(' ');
377  printEnumT(strm, *str, false);
378  }
379  strm.put('\n');
380  }
381  break;
382  case scalar:
383  case scalarArray:
384  strm<<format::indent()<<cur.first->getField()->getID()<<' '<<cur.first->getFieldName()
385  <<' '<<*cur.first
386  <<'\n';
387  break;
388  case structureArray:
389  case union_:
390  case unionArray:
391  strm<<*cur.first;
392  break;
393  }
394 
395  if(hl)
396  strm<<"\x1b[0m"; // reset
397  }
398 
399  format::indent_value(strm) = orig_indent;
400 }
401 
402 std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format)
403 {
404  if(format.xfmt==PVStructure::Formatter::JSON) {
405  JSONPrintOptions opts;
406  opts.multiLine = false;
407  printJSON(strm, format.xtop, format.xshow ? *format.xshow : BitSet().set(0), opts);
408  strm<<'\n';
409  return strm;
410 
411  } else if(format.xfmt==PVStructure::Formatter::NT) {
412  std::string id(format.xtop.getStructure()->getID()),
413  idprefix(id.substr(0, id.find_first_of('.')));
414 
415  // NTTable
416  if(idprefix=="epics:nt/NTTable:1") {
417  if(printTable(strm, format.xtop))
418  return strm;
419  } else {
420  //NTScalar, NTScalarArray, NTEnum, or anything with '.value'
421 
422  PVField::const_shared_pointer value(format.xtop.getSubField("value"));
423  if(value) {
424  switch(value->getField()->getType()) {
425  case scalar:
426  strm<<format::indent();
427  printTimeT(strm, format.xtop);
428  strm<<std::setprecision(6)<<*static_cast<const PVScalar*>(value.get())<<' ';
429  printAlarmT(strm, format.xtop);
430  strm<<'\n';
431  return strm;
432 
433  case scalarArray:
434  strm<<format::indent();
435  printTimeT(strm, format.xtop);
436  printAlarmT(strm, format.xtop);
437  strm<<std::setprecision(6)<<*static_cast<const PVScalarArray*>(value.get())<<'\n';
438  return strm;
439 
440  case structure:
441  if(printEnumT(strm, format.xtop, true)) {
442  strm<<'\n';
443  return strm;
444  }
445  break;
446  default:
447  break;
448  }
449  }
450  }
451  }
452  // fall through unhandled as Raw
453 
454  PVStructure::Formatter format2(format);
455 
456  if(format2.xmode==PVStructure::Formatter::Auto)
457  format2.xmode = useEscapes(strm) ? PVStructure::Formatter::ANSI : PVStructure::Formatter::Plain;
458 
459  printRaw(strm, format2, format.xtop);
460 
461  return strm;
462 }
463 
464 static char hexdigit(char c) {
465  c &= 0xf;
466  if(c<9)
467  return '0'+c;
468  else
469  return 'A'+c-10;
470 }
471 
473 
474 std::string escape::str() const
475 {
476  std::ostringstream strm;
477  strm<<(*this);
478  return strm.str();
479 }
480 
482 std::ostream& operator<<(std::ostream& strm, const escape& Q)
483 {
484  for(size_t pos = 0, len = Q.orig.size(); pos < len; pos++) {
485  const char C = Q.orig[pos];
486  char quote = '\\', next;
487  // compare me with epicsStrnEscapedFromRaw()
488  switch(C) {
489  case '\a': next = 'a'; break;
490  case '\b': next = 'b'; break;
491  case '\f': next = 'f'; break;
492  case '\n': next = 'n'; break;
493  case '\r': next = 'r'; break;
494  case '\t': next = 't'; break;
495  case '\v': next = 'v'; break;
496  case '\\': next = '\\'; break;
497  case '\'': next = '\''; break;
498  case '\"': next = '\"'; if(Q.S==escape::CSV) quote = '"'; break;
499  default:
500  if(!isprint(C)) {
501  // print three charator escape
502  strm<<"\\x"<<hexdigit(C>>4)<<hexdigit(C);
503  } else {
504  // literal
505  strm.put(C);
506  }
507  continue;
508  }
509  // print two charactor escape
510  strm.put(quote);
511  strm.put(next);
512  }
513 
514  return strm;
515 }
516 
517 }} //epics::pvData
FORCE_INLINE std::tr1::shared_ptr< PVField > getSubField(A a)
Definition: pvData.h:744
const std::string & getFieldName() const
Definition: pvData.h:166
std::ostream & operator<<(std::ostream &os, indent_level const &indent)
Definition: printer.cpp:35
const PVFieldPtrArray & getPVFields() const
Definition: pvData.h:736
Definition: link.h:174
#define max(x, y)
Definition: flexdef.h:81
PVScalar is the base class for each scalar field.
Definition: pvData.h:272
bool get(uint32 bitIndex) const
Definition: bitSet.cpp:130
#define quote(v)
int i
Definition: scan.c:967
#define min(x, y)
Definition: flexdef.h:78
const FieldConstPtr & getField() const
Definition: pvData.h:208
shared_ptr< T > static_pointer_cast(shared_ptr< U > const &r) BOOST_NOEXCEPT
Definition: shared_ptr.hpp:788
#define epicsShareFunc
Definition: shareLib.h:209
TODO only here because of the Lockable.
Definition: ntaggregate.cpp:16
void printRaw(std::ostream &strm, const PVStructure::Formatter &format, const PVStructure &cur)
Definition: printer.cpp:317
Options used during printing.
Definition: json.h:42
A vector of bits.
Definition: bitSet.h:56
#define str(v)
PVField is the base class for each PVData field.
Definition: pvData.h:152
std::size_t getFieldOffset() const
Definition: PVField.cpp:44
epicsUInt32 secPastEpoch
seconds since 0000 Jan 1, 1990
Definition: epicsTime.h:34
LIBCOM_API size_t epicsStdCall epicsTimeToStrftime(char *pBuff, size_t bufLength, const char *pFormat, const epicsTimeStamp *pTS)
Convert epicsTimeStamp to string. See epicsTime::strftime()
Definition: epicsTime.cpp:1120
PVString is special case, since it implements SerializableArray.
Definition: pvData.h:521
pvData
Definition: monitor.h:428
void push_back(param_type v)
Definition: sharedVector.h:602
template class for all extensions of PVArray.
Definition: pvData.h:55
Base class for a scalarArray.
Definition: pvData.h:618
int32 nextSetBit(uint32 fromIndex) const
Definition: bitSet.cpp:164
#define POSIX_TIME_AT_EPICS_EPOCH
The EPICS Epoch is 00:00:00 Jan 1, 1990 UTC.
Definition: epicsTime.h:26
std::vector< FieldConstPtr > FieldConstPtrArray
Definition: pvIntrospect.h:146
void printJSON(std::ostream &strm, const PVStructure &val, const BitSet &mask, const JSONPrintOptions &opts)
Definition: print.cpp:211
std::vector< PVFieldPtr > PVFieldPtrArray
Definition: pvData.h:70
Data interface for a structure,.
Definition: pvData.h:712
#define stdout
Definition: epicsStdio.h:30
size_t size() const
Number of elements visible through this vector.
Definition: sharedVector.h:220
int64_t int64
Definition: pvType.h:87
FORCE_INLINE std::tr1::shared_ptr< PVField > getSubFieldT(A a)
Definition: pvData.h:786
std::string str() const
print to string and return. (alloc and copy)
Definition: printer.cpp:474
Class that holds the data for each possible scalar type.
Definition: pvData.h:54
EPICS time stamp, for use from C code.
Definition: epicsTime.h:33
long & indent_value(std::ios_base &ios)
Definition: printer.cpp:30
const StructureConstPtr & getStructure() const
Definition: pvData.h:731
#define stderr
Definition: epicsStdio.h:32
EPICS time-stamps (epicsTimeStamp), epicsTime C++ class and C functions for handling wall-clock times...
BitSet & set(uint32 bitIndex)
Definition: bitSet.cpp:103
PVStructure * getParent()
Definition: pvData.h:213
void reserve(size_t i)
Set array capacity.
Definition: sharedVector.h:428
bool multiLine
include new lines
Definition: json.h:44
int32_t int32
Definition: pvType.h:83
epicsUInt32 nsec
nanoseconds within second
Definition: epicsTime.h:35
uint32_t uint32
Definition: pvType.h:99
std::size_t getNumberFields() const
Definition: PVField.cpp:56