format NTTable as CSV

This commit is contained in:
Michael Davidsaver
2018-10-07 12:16:13 -07:00
parent 6171cd6867
commit 90cffa60d6
2 changed files with 100 additions and 21 deletions

View File

@@ -170,6 +170,21 @@ bool printEnumT(std::ostream& strm, const PVStructure& top)
return true;
}
void csvEscape(std::string& S)
{
// concise, not particularly efficient...
std::string temp(escape(S).style(escape::CSV).str());
if(S.find_first_of(" ,\\")!=S.npos) {// only quote if necessary (stupid Excel)
std::string temp2;
temp2.reserve(temp.size()+2);
temp2.push_back('\"');
temp2 += temp;
temp2.push_back('\"');
temp2.swap(temp);
}
S = temp;
}
bool printTable(std::ostream& strm, const PVStructure& top)
{
PVStructure::const_shared_pointer cf(top.getSubField<PVStructure>("value"));
@@ -184,6 +199,7 @@ bool printTable(std::ostream& strm, const PVStructure& top)
}
}
// maybe output a line with meta-data
{
bool havets = !!top.getSubField("timeStamp");
bool haveal = !!top.getSubField("alarm");
@@ -210,7 +226,7 @@ bool printTable(std::ostream& strm, const PVStructure& top)
}
const PVFieldPtrArray& columns = cf->getPVFields();
std::vector<shared_vector<const std::string> > coldat(columns.size());
std::vector<shared_vector<std::string> > coldat(columns.size());
std::vector<size_t> widths(columns.size());
labels.reserve(columns.size());
@@ -220,42 +236,42 @@ bool printTable(std::ostream& strm, const PVStructure& top)
for(size_t i=0, N=columns.size(); i<N; i++) {
if(i>=labels.size()) {
labels.push_back(cf->getStructure()->getFieldName(i));
} else {
csvEscape(labels[i]);
}
widths[i] = labels[i].size();
static_cast<const PVScalarArray*>(columns[i].get())->getAs(coldat[i]);
{
shared_vector<const std::string> ro;
static_cast<const PVScalarArray*>(columns[i].get())->getAs(ro);
coldat[i] = thaw(ro);
}
// truncate if some columns are longer than others
nrows = std::min(nrows, coldat[i].size());
for(size_t j=0, M=coldat[i].size(); j<M; j++) {
csvEscape(coldat[i][j]);
widths[i] = std::max(widths[i], coldat[i][j].size());
}
}
size_t sep = 0;
// output header line
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<<", ";
}
}
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<<", ";
}
strm<<'\n';
}

View File

@@ -95,17 +95,24 @@ testDiff(const std::string& expect, const std::string& actual, const std::string
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);
Lp = L+search[n].L;
Rp = R+search[n].R;
if(lhs[Lp]==rhs[Rp])
if(Lp<lhs.size() && Rp<rhs.size() && lhs[Lp]==rhs[Rp])
break;
}
if(Lp>=lhs.size() || Rp>=rhs.size()) {
// reached end without match
Lp = lhs.size();
Rp = rhs.size();
}
for(size_t l=L; l<Lp; l++)
ret<<"- "<<pvd::escape(lhs[l])<<'\n';
for(size_t r=R; r<Rp; r++)
ret<<"+ "<<pvd::escape(rhs[r])<<'\n';
assert(Lp>L); // must make progress
assert(Rp>R);
L = Lp;
R = Rp;
// loop around and print matching line
@@ -166,6 +173,54 @@ void showNTScalarString()
testDiff("<undefined> bar MINOR DEVICE FOO \n", print(input->stream()));
}
static const pvd::StructureConstPtr table(pvd::getFieldCreate()->createFieldBuilder()
->setId("epics:nt/NTTable:1.0")
->addArray("labels", pvd::pvString)
->addNestedStructure("value")
->addArray("colA", pvd::pvInt)
->addArray("colB", pvd::pvString)
->endNested()
->add("alarm", pvd::getStandardField()->alarm())
->add("timeStamp", pvd::getStandardField()->timeStamp())
->createStructure());
void showNTTable()
{
testDiag("%s", CURRENT_FUNCTION);
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(table));
testDiff("<undefined> \n"
"colA, colB\n"
, print(input->stream()),
"empty table");
pvd::PVStringArray::svector sarr;
sarr.push_back("labelA");
sarr.push_back("label B");
input->getSubFieldT<pvd::PVStringArray>("labels")->replace(pvd::freeze(sarr));
pvd::PVIntArray::svector iarr;
iarr.push_back(1);
iarr.push_back(2);
iarr.push_back(3);
iarr.push_back(42); // will not be shown
input->getSubFieldT<pvd::PVIntArray>("value.colA")->replace(pvd::freeze(iarr));
sarr.push_back("one\x7f");
sarr.push_back("two words");
sarr.push_back("A '\"'");
input->getSubFieldT<pvd::PVStringArray>("value.colB")->replace(pvd::freeze(sarr));
testDiff("<undefined> \n"
"labelA, \"label B\"\n"
" 1, one\\x7F\n"
" 2, \"two words\"\n"
" 3, \"A \\'\"\"\\'\"\n"
, print(input->stream()),
"with data");
}
static const pvd::StructureConstPtr everything(pvd::getFieldCreate()->createFieldBuilder()
->setId("omg")
->add("scalar", pvd::pvString)
@@ -194,11 +249,18 @@ void testRaw()
testDiag("%s", CURRENT_FUNCTION);
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(everything));
{
pvd::PVStringArray::svector temp;
temp.push_back("hello");
temp.push_back("world\x7f");
input->getSubFieldT<pvd::PVStringArray>("scalarArray")->replace(pvd::freeze(temp));
}
testDiff("omg \n"
" string scalar \n"
" string[] scalarArray []\n"
" string scalar \n" // bit 1
" string[] scalarArray [\"hello\", \"world\\x7F\"]\n"
" structure below\n"
" int A 0\n"
" int A 0\n" // bit 4
" union select\n"
" (none)\n"
" union[] arrselect\n"
@@ -216,7 +278,7 @@ void testRaw()
testDiff("omg \n"
"\033[1m string scalar \n"
"\033[0m\033[1m string[] scalarArray []\n"
"\033[0m\033[1m string[] scalarArray [\"hello\", \"world\\x7F\"]\n"
"\033[0m structure below\n"
"\033[1m int A 0\n"
"\033[0m union select\n"
@@ -250,9 +312,10 @@ void testEscape()
MAIN(testprinter)
{
testPlan(14);
testPlan(16);
showNTScalarNumeric();
showNTScalarString();
showNTTable();
testRaw();
testEscape();
return testDone();