Add PVStructure::stream()
This commit is contained in:
@@ -4,12 +4,22 @@
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
#define HAVE_ISATTY
|
||||
#endif
|
||||
|
||||
#include <deque>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <epicsTime.h>
|
||||
|
||||
using std::string;
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/pvData.h>
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
# include <pv/json.h>
|
||||
#endif
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
@@ -40,6 +50,390 @@ array_at_internal operator<<(std::ostream& str, array_at const& manip)
|
||||
{
|
||||
return array_at_internal(manip.index, str);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}}
|
||||
}} //epics::pvData
|
||||
namespace {
|
||||
using namespace epics::pvData;
|
||||
|
||||
bool useEscapes(std::ostream& strm) {
|
||||
FILE *fp = 0;
|
||||
// TODO: a better way to do this...
|
||||
if(&std::cout==&strm)
|
||||
fp = stdout;
|
||||
if(&std::cerr==&strm)
|
||||
fp = stderr;
|
||||
if(!fp) return false;
|
||||
#ifdef HAVE_ISATTY
|
||||
// check $TERM as well?
|
||||
return isatty(fileno(fp))==1;
|
||||
#else
|
||||
// TODO: in theory windows 10 can be made to understand escapes as well
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void printAlarmTx(std::ostream& strm, const PVStructure& sub)
|
||||
{
|
||||
PVScalar::const_shared_pointer pvSeverity(sub.getSubField<PVInt>("severity"));
|
||||
PVScalar::const_shared_pointer pvStatus(sub.getSubField<PVInt>("status"));
|
||||
PVString::const_shared_pointer pvMessage(sub.getSubField<PVString>("message"));
|
||||
|
||||
switch(pvSeverity ? pvSeverity->getAs<int32>() : 0) {
|
||||
case 0: return; // nothing more to do here...
|
||||
case 1: strm<<"MINOR "; break;
|
||||
case 2: strm<<"MAJOR "; break;
|
||||
case 3: strm<<"INVALID "; break;
|
||||
case 4: strm<<"UNDEFINED "; break;
|
||||
default: strm<<pvSeverity->getAs<int32>()<<' ';
|
||||
}
|
||||
|
||||
switch(pvStatus ? pvStatus->getAs<int32>() : 0) {
|
||||
case 0: break;
|
||||
case 1: strm<<"DEVICE "; break;
|
||||
case 2: strm<<"DRIVER "; break;
|
||||
case 3: strm<<"RECORD "; break;
|
||||
case 4: strm<<"DB "; break;
|
||||
case 5: strm<<"CONF "; break;
|
||||
case 6: strm<<"UNDEFINED "; break;
|
||||
case 7: strm<<"CLIENT "; break;
|
||||
default: strm<<pvStatus->getAs<int32>()<<' ';
|
||||
}
|
||||
|
||||
if(pvMessage && !pvMessage->get().empty())
|
||||
strm<<pvMessage->get()<<' ';
|
||||
}
|
||||
|
||||
|
||||
void printAlarmT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("alarm"));
|
||||
if(sub)
|
||||
printAlarmTx(strm, *sub);
|
||||
}
|
||||
|
||||
void printTimeTx(std::ostream& strm, const PVStructure& tsubop)
|
||||
{
|
||||
char timeText[32];
|
||||
epicsTimeStamp epicsTS;
|
||||
|
||||
PVScalar::const_shared_pointer secf(tsubop.getSubField<PVScalar>("secondsPastEpoch")),
|
||||
nsecf(tsubop.getSubField<PVScalar>("nanoseconds")),
|
||||
tagf(tsubop.getSubField<PVScalar>("userTag"));
|
||||
|
||||
epicsTS.secPastEpoch = secf ? secf->getAs<int64>() : 0;
|
||||
epicsTS.nsec = nsecf ? nsecf->getAs<int32>() : 0;
|
||||
|
||||
if(epicsTS.secPastEpoch > POSIX_TIME_AT_EPICS_EPOCH)
|
||||
epicsTS.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH;
|
||||
else
|
||||
epicsTS.secPastEpoch = 0;
|
||||
|
||||
epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%dT%H:%M:%S.%03f", &epicsTS);
|
||||
strm << timeText << ' ';
|
||||
if (tagf) {
|
||||
int64 tagv = tagf->getAs<int64>();
|
||||
if(tagv)
|
||||
strm << tagv << ' ';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void printTimeT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("timeStamp"));
|
||||
if(sub)
|
||||
printTimeTx(strm, *sub);
|
||||
}
|
||||
|
||||
bool printEnumT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVScalar::const_shared_pointer idx(top.getSubField<PVScalar>("value.index"));
|
||||
PVStringArray::const_shared_pointer choices(top.getSubFieldT<PVStringArray>("value.choices"));
|
||||
if(!idx || !choices) return false;
|
||||
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, top);
|
||||
printAlarmT(strm, top);
|
||||
|
||||
PVStringArray::const_svector ch(choices->view());
|
||||
uint32 I = idx->getAs<uint32>();
|
||||
if(I>=ch.size()) {
|
||||
strm<<'('<<I<<')';
|
||||
} else {
|
||||
strm<<'"'<<ch[I]<<"\" ("<<I<<")";
|
||||
}
|
||||
strm<<'\n';
|
||||
return true;
|
||||
}
|
||||
|
||||
bool printTable(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer cf(top.getSubField<PVStructure>("value"));
|
||||
if(!cf) return false;
|
||||
|
||||
{
|
||||
const FieldConstPtrArray& fields = cf->getStructure()->getFields();
|
||||
if(fields.empty()) return false;
|
||||
for(size_t i=0, N=fields.size(); i<N; i++) {
|
||||
if(fields[i]->getType()!=scalarArray)
|
||||
return false; // cowardly refusing to handle anything else...
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
bool havets = !!top.getSubField("timeStamp");
|
||||
bool haveal = !!top.getSubField("alarm");
|
||||
if(havets || haveal)
|
||||
strm<<format::indent();
|
||||
if(havets) {
|
||||
printTimeT(strm, top);
|
||||
strm<<' ';
|
||||
}
|
||||
if(haveal) {
|
||||
printAlarmT(strm, top);
|
||||
strm<<' ';
|
||||
}
|
||||
strm<<'\n';
|
||||
}
|
||||
|
||||
PVStringArray::svector labels;
|
||||
{
|
||||
PVStringArray::const_shared_pointer lf(top.getSubField<PVStringArray>("labels"));
|
||||
if(lf) {
|
||||
PVStringArray::const_svector L(lf->view());
|
||||
labels = thaw(L);
|
||||
}
|
||||
}
|
||||
|
||||
const PVFieldPtrArray& columns = cf->getPVFields();
|
||||
std::vector<shared_vector<const std::string> > coldat(columns.size());
|
||||
|
||||
std::vector<size_t> widths(columns.size());
|
||||
labels.reserve(columns.size());
|
||||
|
||||
size_t nrows = size_t(-1);
|
||||
|
||||
for(size_t i=0, N=columns.size(); i<N; i++) {
|
||||
if(i>=labels.size()) {
|
||||
labels.push_back(cf->getStructure()->getFieldName(i));
|
||||
}
|
||||
widths[i] = labels[i].size();
|
||||
|
||||
static_cast<const PVScalarArray*>(columns[i].get())->getAs(coldat[i]);
|
||||
|
||||
nrows = std::min(nrows, coldat[i].size());
|
||||
|
||||
for(size_t j=0, M=coldat[i].size(); j<M; j++) {
|
||||
widths[i] = std::max(widths[i], coldat[i][j].size());
|
||||
}
|
||||
}
|
||||
|
||||
size_t sep = 0;
|
||||
|
||||
strm<<format::indent();
|
||||
for(size_t c=0, N=coldat.size(); c<N; c++) {
|
||||
sep += widths[c];
|
||||
strm<<std::setw(widths[c])<<std::right<<labels[c];
|
||||
if(c+1!=N) {
|
||||
strm<<" | ";
|
||||
sep += 3;
|
||||
}
|
||||
}
|
||||
strm<<'\n';
|
||||
|
||||
strm<<format::indent();
|
||||
for(size_t c=0; c<sep; c++)
|
||||
strm.put('=');
|
||||
strm<<'\n';
|
||||
|
||||
for(size_t r=0; r<nrows; r++) {
|
||||
strm<<format::indent();
|
||||
for(size_t c=0, N=coldat.size(); c<N; c++) {
|
||||
strm<<std::setw(widths[c])<<std::right<<coldat[c][r];
|
||||
if(c+1!=N)
|
||||
strm<<" | ";
|
||||
}
|
||||
strm<<'\n';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void expandBS(const PVStructure& top, BitSet& mask, bool parents) {
|
||||
if(mask.get(0)) { // special handling because getSubField(0) not allowed
|
||||
// wildcard
|
||||
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
|
||||
mask.set(idx);
|
||||
}
|
||||
|
||||
} else {
|
||||
for(int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
|
||||
PVField::const_shared_pointer fld = top.getSubFieldT(idx);
|
||||
|
||||
// look forward and mark all children
|
||||
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
|
||||
mask.set(i);
|
||||
|
||||
if(parents) {
|
||||
// look back and mark all parents
|
||||
// we've already stepped past all parents so siblings will not be automatically marked
|
||||
for(const PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
|
||||
mask.set(parent->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur)
|
||||
{
|
||||
typedef std::deque<std::pair<const PVField*, size_t> > todo_t;
|
||||
todo_t todo;
|
||||
BitSet show, highlight;
|
||||
|
||||
const long orig_indent = format::indent_value(strm);
|
||||
|
||||
{
|
||||
if(format.xshow)
|
||||
show = *format.xshow;
|
||||
else
|
||||
show.set(0);
|
||||
|
||||
if(format.xhighlight)
|
||||
highlight = *format.xhighlight;
|
||||
|
||||
expandBS(format.xtop, show, true);
|
||||
expandBS(format.xtop, highlight, false);
|
||||
highlight &= show; // can't highlight hidden fields (paranoia)
|
||||
}
|
||||
|
||||
if(!show.get(0)) return; // nothing to do here...
|
||||
|
||||
todo.push_front(std::make_pair(&format.xtop, orig_indent));
|
||||
|
||||
while(!todo.empty()) {
|
||||
todo_t::value_type cur(todo.front());
|
||||
todo.pop_front();
|
||||
|
||||
format::indent_value(strm) = cur.second;
|
||||
|
||||
bool hl = highlight.get(cur.first->getFieldOffset()) && format.xmode==PVStructure::Formatter::ANSI;
|
||||
if(hl)
|
||||
strm<<"\x1b[1m"; // Bold
|
||||
|
||||
switch(cur.first->getField()->getType()) {
|
||||
case structure: {
|
||||
const PVStructure* str = static_cast<const PVStructure*>(cur.first);
|
||||
|
||||
const PVFieldPtrArray& flds = str->getPVFields();
|
||||
|
||||
for(PVFieldPtrArray::const_reverse_iterator it(flds.rbegin()), end(flds.rend()); it!=end; ++it) {
|
||||
const PVField *fld = (*it).get();
|
||||
if(!show.get(fld->getFieldOffset())) continue;
|
||||
|
||||
todo.push_front(std::make_pair(fld, cur.second+1));
|
||||
}
|
||||
|
||||
std::string id(cur.first->getField()->getID());
|
||||
|
||||
strm<<format::indent()<<id<<' '<<cur.first->getFieldName();
|
||||
if(id=="alarm_t") {
|
||||
strm.put(' ');
|
||||
printAlarmTx(strm, *str);
|
||||
} else if(id=="time_t") {
|
||||
strm.put(' ');
|
||||
printTimeTx(strm, *str);
|
||||
}
|
||||
strm.put('\n');
|
||||
}
|
||||
break;
|
||||
case scalar:
|
||||
case scalarArray:
|
||||
strm<<format::indent()<<cur.first->getField()->getID()<<' '<<cur.first->getFieldName()
|
||||
<<' '<<*cur.first
|
||||
<<'\n';
|
||||
break;
|
||||
case structureArray:
|
||||
case union_:
|
||||
case unionArray:
|
||||
strm<<*cur.first;
|
||||
break;
|
||||
}
|
||||
|
||||
if(hl)
|
||||
strm<<"\x1b[0m"; // reset
|
||||
}
|
||||
|
||||
format::indent_value(strm) = orig_indent;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format)
|
||||
{
|
||||
if(format.xfmt==PVStructure::Formatter::JSON) {
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
JSONPrintOptions opts;
|
||||
opts.multiLine = false;
|
||||
printJSON(strm, format.xtop, format.xshow ? *format.xshow : BitSet().set(0), opts);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
#else
|
||||
// fall through to Raw
|
||||
#endif
|
||||
|
||||
} else if(format.xfmt==PVStructure::Formatter::NT) {
|
||||
std::string id(format.xtop.getStructure()->getID()),
|
||||
idprefix(id.substr(0, id.find_first_of('.')));
|
||||
|
||||
// NTTable
|
||||
if(idprefix=="epics:nt/NTTable:1") {
|
||||
if(printTable(strm, format.xtop))
|
||||
return strm;
|
||||
} else {
|
||||
//NTScalar, NTScalarArray, NTEnum, or anything with '.value'
|
||||
|
||||
PVField::const_shared_pointer value(format.xtop.getSubField("value"));
|
||||
if(value) {
|
||||
switch(value->getField()->getType()) {
|
||||
case scalar:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
strm<<*static_cast<const PVScalar*>(value.get())<<' ';
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
|
||||
case scalarArray:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<*static_cast<const PVScalarArray*>(value.get())<<'\n';
|
||||
return strm;
|
||||
|
||||
case structure:
|
||||
if(printEnumT(strm, format.xtop))
|
||||
return strm;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fall through unhandled as Raw
|
||||
|
||||
PVStructure::Formatter format2(format);
|
||||
|
||||
if(format2.xmode==PVStructure::Formatter::Auto)
|
||||
format2.xmode = useEscapes(strm) ? PVStructure::Formatter::ANSI : PVStructure::Formatter::Plain;
|
||||
|
||||
printRaw(strm, format2, format.xtop);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
}} //epics::pvData
|
||||
|
||||
@@ -886,7 +886,49 @@ public:
|
||||
void copyUnchecked(const PVStructure& from);
|
||||
void copyUnchecked(const PVStructure& from, const BitSet& maskBitSet, bool inverse = false);
|
||||
|
||||
struct Formatter {
|
||||
enum mode_t {
|
||||
Auto,
|
||||
Plain,
|
||||
ANSI,
|
||||
};
|
||||
enum format_t {
|
||||
Raw,
|
||||
NT,
|
||||
JSON,
|
||||
};
|
||||
private:
|
||||
const PVStructure& xtop;
|
||||
const BitSet* xshow;
|
||||
const BitSet* xhighlight;
|
||||
mode_t xmode;
|
||||
format_t xfmt;
|
||||
public:
|
||||
explicit Formatter(const PVStructure& top)
|
||||
:xtop(top)
|
||||
,xshow(0)
|
||||
,xhighlight(0)
|
||||
,xmode(Auto)
|
||||
,xfmt(NT)
|
||||
{}
|
||||
|
||||
// those fields (and their parents) to be printed. non-NT mode.
|
||||
FORCE_INLINE Formatter& show(const BitSet& set) { xshow = &set; return *this; }
|
||||
// those fields (and not their parents) to be specially highlighted. non-NT mode.
|
||||
FORCE_INLINE Formatter& highlight(const BitSet& set) { xhighlight = &set; return *this; }
|
||||
|
||||
FORCE_INLINE Formatter& mode(mode_t m) { xmode = m; return *this; }
|
||||
|
||||
FORCE_INLINE Formatter& format(format_t f) { xfmt = f; return *this; }
|
||||
|
||||
friend epicsShareFunc std::ostream& operator<<(std::ostream& strm, const Formatter& format);
|
||||
friend void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur);
|
||||
};
|
||||
|
||||
FORCE_INLINE Formatter stream() const { return Formatter(*this); }
|
||||
|
||||
private:
|
||||
|
||||
inline PVFieldPtr getSubFieldImpl(const std::string& name, bool throws) const {
|
||||
return getSubFieldImpl(name.c_str(), throws);
|
||||
}
|
||||
@@ -900,6 +942,9 @@ private:
|
||||
EPICS_NOT_COPYABLE(PVStructure)
|
||||
};
|
||||
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format);
|
||||
|
||||
/**
|
||||
* @brief PVUnion has a single subfield.
|
||||
*
|
||||
|
||||
@@ -79,3 +79,7 @@ TESTS += test_reftrack
|
||||
TESTPROD_HOST += testanyscalar
|
||||
testanyscalar_SRCS += testanyscalar.cpp
|
||||
TESTS += testanyscalar
|
||||
|
||||
TESTPROD_HOST += testprinter
|
||||
testprinter_SRCS += testprinter.cpp
|
||||
TESTS += testprinter
|
||||
|
||||
241
testApp/misc/testprinter.cpp
Normal file
241
testApp/misc/testprinter.cpp
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <testMain.h>
|
||||
#include <epicsString.h>
|
||||
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <pv/current_function.h>
|
||||
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/standardField.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
# define USE_JSON
|
||||
#endif
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
typedef std::vector<std::string> lines_t;
|
||||
|
||||
namespace {
|
||||
|
||||
lines_t lines(const std::string& str)
|
||||
{
|
||||
lines_t ret;
|
||||
size_t p = 0;
|
||||
while(true) {
|
||||
size_t next = str.find_first_of('\n', p);
|
||||
ret.push_back(str.substr(p, next-p)); // exclude trailing '\n'
|
||||
if(next==str.npos)
|
||||
break;
|
||||
else
|
||||
p = next+1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string escape(const std::string& inp) {
|
||||
size_t N = epicsStrnEscapedFromRawSize(inp.c_str(), inp.size());
|
||||
std::vector<char> esc(N+1, '\0'); // include space for trailing nil
|
||||
epicsStrnEscapedFromRaw(&esc[0], esc.size(), inp.c_str(), inp.size());
|
||||
return std::string(&esc[0]);
|
||||
}
|
||||
|
||||
std::string print(const pvd::PVStructure::Formatter& fmt)
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm<<fmt;
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
struct point {
|
||||
size_t L, R;
|
||||
};
|
||||
|
||||
// really primative diff
|
||||
// expect -> actual
|
||||
::detail::testPassx
|
||||
testDiff(const std::string& expect, const std::string& actual, const std::string& msg = std::string())
|
||||
{
|
||||
bool match = expect==actual;
|
||||
::detail::testPassx ret(match);
|
||||
ret<<msg<<'\n';
|
||||
|
||||
lines_t lhs(lines(expect)), rhs(lines(actual));
|
||||
|
||||
size_t L=0, R=0;
|
||||
|
||||
while(L<lhs.size() && R<rhs.size()) {
|
||||
if(lhs[L]==rhs[R]) {
|
||||
ret<<" "<<escape(lhs[L])<<'\n';
|
||||
L++;
|
||||
R++;
|
||||
|
||||
} else {
|
||||
// ugly... diagonalization hardcoded...
|
||||
static const point search[] = {
|
||||
{1,0},
|
||||
{0,1},
|
||||
{1,1},
|
||||
{2,1},
|
||||
{1,2},
|
||||
{2,2},
|
||||
};
|
||||
|
||||
|
||||
size_t Lp, Rp;
|
||||
for(size_t n=0, N=sizeof(search)/sizeof(search[0]); n<N; n++) {
|
||||
Lp = std::min(L+search[n].L, lhs.size()-1u);
|
||||
Rp = std::min(R+search[n].R, rhs.size()-1u);
|
||||
|
||||
if(lhs[Lp]==rhs[Rp])
|
||||
break;
|
||||
}
|
||||
|
||||
for(size_t l=L; l<Lp; l++)
|
||||
ret<<"- "<<escape(lhs[l])<<'\n';
|
||||
for(size_t r=R; r<Rp; r++)
|
||||
ret<<"+ "<<escape(rhs[r])<<'\n';
|
||||
L = Lp;
|
||||
R = Rp;
|
||||
// loop around and print matching line
|
||||
}
|
||||
}
|
||||
|
||||
for(; L<lhs.size(); L++)
|
||||
ret<<"- "<<escape(lhs[L])<<'\n';
|
||||
for(; R<rhs.size(); R++)
|
||||
ret<<"+ "<<escape(rhs[R])<<'\n';
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const pvd::StructureConstPtr scalarNumeric(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->setId("epics:nt/NTScalar:1.0")
|
||||
->add("value", pvd::pvInt)
|
||||
->add("alarm", pvd::getStandardField()->alarm())
|
||||
->add("timeStamp", pvd::getStandardField()->timeStamp())
|
||||
->createStructure());
|
||||
|
||||
void showNTScalarNumeric()
|
||||
{
|
||||
testDiag("%s", CURRENT_FUNCTION);
|
||||
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(scalarNumeric));
|
||||
input->getSubFieldT<pvd::PVScalar>("value")->putFrom(-42);
|
||||
|
||||
testDiff("<undefined> -42 \n", print(input->stream()));
|
||||
|
||||
input->getSubFieldT<pvd::PVScalar>("alarm.severity")->putFrom(1);
|
||||
input->getSubFieldT<pvd::PVScalar>("alarm.status")->putFrom(1);
|
||||
input->getSubFieldT<pvd::PVString>("alarm.message")->put("FOO");
|
||||
|
||||
testDiff("<undefined> -42 MINOR DEVICE FOO \n", print(input->stream()));
|
||||
}
|
||||
|
||||
static const pvd::StructureConstPtr scalarString(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->setId("epics:nt/NTScalar:1.0")
|
||||
->add("value", pvd::pvString)
|
||||
->add("alarm", pvd::getStandardField()->alarm())
|
||||
->add("timeStamp", pvd::getStandardField()->timeStamp())
|
||||
->createStructure());
|
||||
|
||||
void showNTScalarString()
|
||||
{
|
||||
testDiag("%s", CURRENT_FUNCTION);
|
||||
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(scalarString));
|
||||
testDiff("<undefined> \n", print(input->stream()));
|
||||
|
||||
input->getSubFieldT<pvd::PVString>("value")->put("bar");
|
||||
|
||||
testDiff("<undefined> bar \n", print(input->stream()));
|
||||
|
||||
input->getSubFieldT<pvd::PVScalar>("alarm.severity")->putFrom(1);
|
||||
input->getSubFieldT<pvd::PVScalar>("alarm.status")->putFrom(1);
|
||||
input->getSubFieldT<pvd::PVString>("alarm.message")->put("FOO");
|
||||
|
||||
testDiff("<undefined> bar MINOR DEVICE FOO \n", print(input->stream()));
|
||||
}
|
||||
|
||||
static const pvd::StructureConstPtr everything(pvd::getFieldCreate()->createFieldBuilder()
|
||||
->setId("omg")
|
||||
->add("scalar", pvd::pvString)
|
||||
->addArray("scalarArray", pvd::pvString)
|
||||
->addNestedStructure("below")
|
||||
->add("A", pvd::pvInt)
|
||||
->addNestedUnion("select")
|
||||
->add("one", pvd::pvInt)
|
||||
->add("two", pvd::pvInt)
|
||||
->endNested()
|
||||
->addNestedUnionArray("arrselect")
|
||||
->add("foo", pvd::pvInt)
|
||||
->add("bar", pvd::pvInt)
|
||||
->endNested()
|
||||
->addNestedStructureArray("astruct")
|
||||
->add("red", pvd::pvInt)
|
||||
->add("blue", pvd::pvInt)
|
||||
->endNested()
|
||||
->endNested()
|
||||
->add("anything", pvd::getFieldCreate()->createVariantUnion())
|
||||
->add("arrayany", pvd::getFieldCreate()->createVariantUnionArray())
|
||||
->createStructure());
|
||||
|
||||
void testRaw()
|
||||
{
|
||||
testDiag("%s", CURRENT_FUNCTION);
|
||||
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(everything));
|
||||
|
||||
testDiff("omg \n"
|
||||
" string scalar \n"
|
||||
" string[] scalarArray []\n"
|
||||
" structure below\n"
|
||||
" int A 0\n"
|
||||
" union select\n"
|
||||
" (none)\n"
|
||||
" union[] arrselect\n"
|
||||
" structure[] astruct\n"
|
||||
" any anything\n"
|
||||
" (none)\n"
|
||||
" any[] arrayany\n"
|
||||
, print(input->stream()));
|
||||
|
||||
testDiff("omg \n"
|
||||
" string scalar \n"
|
||||
" structure below\n"
|
||||
" int A 0\n"
|
||||
, print(input->stream().show(pvd::BitSet().set(1).set(4))));
|
||||
|
||||
testDiff("omg \n"
|
||||
"\033[1m string scalar \n"
|
||||
"\033[0m\033[1m string[] scalarArray []\n"
|
||||
"\033[0m structure below\n"
|
||||
"\033[1m int A 0\n"
|
||||
"\033[0m union select\n"
|
||||
" (none)\n"
|
||||
" union[] arrselect\n"
|
||||
" structure[] astruct\n"
|
||||
" any anything\n"
|
||||
" (none)\n"
|
||||
" any[] arrayany\n"
|
||||
, print(input->stream()
|
||||
.mode(pvd::PVStructure::Formatter::ANSI) // force use of escapes
|
||||
.highlight(pvd::BitSet().set(1).set(2).set(4))
|
||||
));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testprinter)
|
||||
{
|
||||
testPlan(0);
|
||||
showNTScalarNumeric();
|
||||
showNTScalarString();
|
||||
testRaw();
|
||||
return testDone();
|
||||
}
|
||||
Reference in New Issue
Block a user