From 38f47b483268c4627b4730262a71b6f1c384397d Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Tue, 14 Oct 2014 14:09:50 +0200 Subject: [PATCH 1/5] Added tag 4.0.0 for changeset 91b7272415af --- .hgtags | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hgtags b/.hgtags index 2a2a76e..1d77148 100644 --- a/.hgtags +++ b/.hgtags @@ -18,3 +18,5 @@ d720da04d44ae6ed4a9d548717f453a0fbaa9e56 3.0.4 ceca448e7c62c23388a0c866c905c7080633a875 4.0.0 ceca448e7c62c23388a0c866c905c7080633a875 4.0.0 cf6fc9696904fd1735523a70a4f59b5ad6a3f2d5 4.0.0 +cf6fc9696904fd1735523a70a4f59b5ad6a3f2d5 4.0.0 +91b7272415af8fdb5b81c98cc6c374558d2ab805 4.0.0 From b3e9ceca1070936fb7a56666226111ec7f644fff Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 15 Oct 2014 09:38:45 +0200 Subject: [PATCH 2/5] pvput enum and union[Array] support --- pvtoolsSrc/pvput.cpp | 182 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 176 insertions(+), 6 deletions(-) diff --git a/pvtoolsSrc/pvput.cpp b/pvtoolsSrc/pvput.cpp index 74e0eee..72d80dd 100644 --- a/pvtoolsSrc/pvput.cpp +++ b/pvtoolsSrc/pvput.cpp @@ -28,6 +28,9 @@ using namespace std::tr1; using namespace epics::pvData; using namespace epics::pvAccess; +enum EnumMode { AutoEnum, NumberEnum, StringEnum }; +EnumMode enumMode = AutoEnum; + size_t fromString(PVScalarArrayPtr const &pv, StringArray const & from, size_t fromStartIndex = 0) { int processed = 0; @@ -98,8 +101,155 @@ size_t fromString(PVStructureArrayPtr const &pv, StringArray const & from, size_ return processed; } +size_t fromString(PVUnionArrayPtr const & pvUnionArray, StringArray const & from, size_t fromStartIndex); + +size_t fromString(PVUnionPtr const & pvUnion, StringArray const & from, size_t fromStartIndex = 0) +{ + if (pvUnion->getUnion()->isVariant()) + throw std::runtime_error("cannot handle variant unions"); + + size_t fromValueCount = from.size(); + + if (fromStartIndex >= fromValueCount) + throw std::runtime_error("not enough of values"); + + string selector = from[fromStartIndex++]; + PVFieldPtr fieldField = pvUnion->select(selector); + if (!fieldField) + throw std::runtime_error("invalid union selector value '" + selector + "'"); + + size_t processed = 1; + + try + { + Type type = fieldField->getField()->getType(); + if(type==structure) { + PVStructurePtr pv = static_pointer_cast(fieldField); + size_t count = fromString(pv, from, fromStartIndex); + processed += count; + } + else if(type==scalarArray) { + PVScalarArrayPtr pv = static_pointer_cast(fieldField); + size_t count = fromString(pv, from, fromStartIndex); + processed += count; + } + else if(type==scalar) { + + if (fromStartIndex >= fromValueCount) + throw std::runtime_error("not enough of values"); + + PVScalarPtr pv = static_pointer_cast(fieldField); + getConvert()->fromString(pv, from[fromStartIndex]); + processed++; + } + else if(type==structureArray) { + PVStructureArrayPtr pv = static_pointer_cast(fieldField); + size_t count = fromString(pv, from, fromStartIndex); + processed += count; + } + else if(type==union_) { + PVUnionPtr pv = static_pointer_cast(fieldField); + size_t count = fromString(pv, from, fromStartIndex); + processed += count; + } + else if(type==unionArray) { + PVUnionArrayPtr pv = static_pointer_cast(fieldField); + size_t count = fromString(pv, from, fromStartIndex); + processed += count; + } + else { + std::ostringstream oss; + oss << "fromString unsupported fieldType " << type; + throw std::logic_error(oss.str()); + } + } + catch (std::exception &ex) + { + std::ostringstream os; + os << "failed to parse '" << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << "'"; + os << ": " << ex.what(); + throw std::runtime_error(os.str()); + } + + return processed; +} + +size_t fromString(PVUnionArrayPtr const &pv, StringArray const & from, size_t fromStartIndex = 0) +{ + int processed = 0; + size_t fromValueCount = from.size(); + + // first get count + if (fromStartIndex >= fromValueCount) + throw std::runtime_error("not enough of values"); + + size_t numberOfUnions; + istringstream iss(from[fromStartIndex]); + iss >> numberOfUnions; + // not fail and entire value is parsed (e.g. to detect 1.2 parsing to 1) + if (iss.fail() || !iss.eof()) + throw runtime_error("failed to parse element count value (uint) of field '" + pv->getFieldName() + "' from string value '" + from[fromStartIndex] + "'"); + fromStartIndex++; + processed++; + + PVUnionArray::svector pvUnions; + pvUnions.reserve(numberOfUnions); + + PVDataCreatePtr pvDataCreate = getPVDataCreate(); + for (size_t i = 0; i < numberOfUnions; ++i) + { + PVUnionPtr pvUnion = pvDataCreate->createPVUnion(pv->getUnionArray()->getUnion()); + size_t count = fromString(pvUnion, from, fromStartIndex); + processed += count; + fromStartIndex += count; + pvUnions.push_back(pvUnion); + } + + pv->replace(freeze(pvUnions)); + + return processed; +} + size_t fromString(PVStructurePtr const & pvStructure, StringArray const & from, size_t fromStartIndex = 0) { + // handle enum in a special way + if (pvStructure->getStructure()->getID() == "enum_t") + { + int32 index = -1; + PVInt::shared_pointer pvIndex = pvStructure->getSubField("index"); + if (!pvIndex) + throw std::runtime_error("enum_t structure does not have 'int index' field"); + + PVStringArray::shared_pointer pvChoices = pvStructure->getSubField("choices"); + if (!pvChoices) + throw std::runtime_error("enum_t structure does not have 'string choices[]' field"); + PVStringArray::const_svector choices(pvChoices->view()); + + if (enumMode == AutoEnum || enumMode == StringEnum) + { + shared_vector::const_iterator it = std::find(choices.begin(), choices.end(), from[fromStartIndex]); + if (it != choices.end()) + index = static_cast(it - choices.begin()); + else if (enumMode == StringEnum) + throw runtime_error("enum string value '" + from[fromStartIndex] + "' invalid"); + } + + if ((enumMode == AutoEnum && index == -1) || enumMode == NumberEnum) + { + istringstream iss(from[fromStartIndex]); + iss >> index; + // not fail and entire value is parsed (e.g. to detect 1.2 parsing to 1) + if (iss.fail() || !iss.eof()) + throw runtime_error("enum value '" + from[fromStartIndex] + "' invalid"); + + if (index < 0 || index >= static_cast(choices.size())) + throw runtime_error("index '" + from[fromStartIndex] + "' out of bounds"); + } + + pvIndex->put(index); + return 1; + } + size_t processed = 0; size_t fromValueCount = from.size(); @@ -112,7 +262,6 @@ size_t fromString(PVStructurePtr const & pvStructure, StringArray const & from, try { Type type = fieldField->getField()->getType(); - // TODO union/unionArray support if(type==structure) { PVStructurePtr pv = static_pointer_cast(fieldField); size_t count = fromString(pv, from, fromStartIndex); @@ -131,8 +280,9 @@ size_t fromString(PVStructurePtr const & pvStructure, StringArray const & from, throw std::runtime_error("not enough of values"); PVScalarPtr pv = static_pointer_cast(fieldField); - getConvert()->fromString(pv, from[fromStartIndex++]); + getConvert()->fromString(pv, from[fromStartIndex]); processed++; + fromStartIndex++; } else if(type==structureArray) { PVStructureArrayPtr pv = static_pointer_cast(fieldField); @@ -140,8 +290,19 @@ size_t fromString(PVStructurePtr const & pvStructure, StringArray const & from, processed += count; fromStartIndex += count; } + else if(type==union_) { + PVUnionPtr pv = static_pointer_cast(fieldField); + size_t count = fromString(pv, from, fromStartIndex); + processed += count; + fromStartIndex += count; + } + else if(type==unionArray) { + PVUnionArrayPtr pv = static_pointer_cast(fieldField); + size_t count = fromString(pv, from, fromStartIndex); + processed += count; + fromStartIndex += count; + } else { - // union/unionArray not supported std::ostringstream oss; oss << "fromString unsupported fieldType " << type; throw std::logic_error(oss.str()); @@ -160,8 +321,6 @@ size_t fromString(PVStructurePtr const & pvStructure, StringArray const & from, return processed; } - - #define DEFAULT_TIMEOUT 3.0 #define DEFAULT_REQUEST "field(value)" @@ -185,10 +344,15 @@ void usage (void) " -d: Enable debug output\n" " -F : Use as an alternate output field separator\n" " -f : Use as an input that provides a list PV name(s) to be read, use '-' for stdin\n" + " enum format:\n" + " default: Auto - try value as enum string, then as index number\n" + " -n: Force enum interpretation of values as numbers\n" + " -s: Force enum interpretation of values as strings\n" "\nexample: pvput double01 1.234\n\n" , DEFAULT_REQUEST, DEFAULT_TIMEOUT); } + void printValue(std::string const & channelName, PVStructure::shared_pointer const & pv) { if (mode == ValueOnlyMode) @@ -416,7 +580,7 @@ int main (int argc, char *argv[]) setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ putenv(const_cast("POSIXLY_CORRECT=")); /* Behave correct on GNU getopt systems; e.g. handle negative numbers */ - while ((opt = getopt(argc, argv, ":hr:w:tqdF:f:")) != -1) { + while ((opt = getopt(argc, argv, ":hr:w:tqdF:f:ns")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); @@ -468,6 +632,12 @@ int main (int argc, char *argv[]) fromStream = true; break; } + case 'n': + enumMode = NumberEnum; + break; + case 's': + enumMode = StringEnum; + break; case '?': fprintf(stderr, "Unrecognized option: '-%c'. ('pvput -h' for help.)\n", From 1e2f335c6140c117cb531fd1016a91989e0b209f Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 15 Oct 2014 12:01:43 +0200 Subject: [PATCH 3/5] enum_t/time_t print in terse mode --- pvtoolsSrc/eget.cpp | 35 ++------ pvtoolsSrc/pvget.cpp | 12 ++- pvtoolsSrc/pvlist.cpp | 2 +- pvtoolsSrc/pvput.cpp | 197 +++++++++++++++++------------------------ pvtoolsSrc/pvutils.cpp | 77 ++++++++++++++++ pvtoolsSrc/pvutils.h | 73 +++++++++++++++ 6 files changed, 251 insertions(+), 145 deletions(-) diff --git a/pvtoolsSrc/eget.cpp b/pvtoolsSrc/eget.cpp index c94ed84..ce7597d 100644 --- a/pvtoolsSrc/eget.cpp +++ b/pvtoolsSrc/eget.cpp @@ -157,29 +157,7 @@ void formatNTEnum(std::ostream& o, PVStructurePtr const & pvStruct) return; } - PVIntPtr index = dynamic_pointer_cast(enumt->getSubField("index")); - if (index.get() == 0) - { - std::cerr << "no int 'value.index' field in NTEnum" << std::endl; - return; - } - - PVStringArrayPtr choices = dynamic_pointer_cast(enumt->getSubField("choices")); - if (choices.get() == 0) - { - std::cerr << "no string[] 'value.choices' field in NTEnum" << std::endl; - return; - } - - int32 ix = index->get(); - if (ix < 0 || ix > static_cast(choices->getLength())) - { - o << ix; - } - else - { - choices->dumpValue(o, ix); - } + printEnumT(o, enumt); } size_t getLongestString(shared_vector const & array) @@ -1045,7 +1023,7 @@ void usage (void) " -r : Get request string, specifies what fields to return and options, default is '%s'\n" " -w : Wait time, specifies timeout, default is %f second(s)\n" " -z: Pure pvAccess RPC based service (send NTURI.query as request argument)\n" - " -n: Do not format NT types, dump structure instead\n" + " -N: Do not format NT types, dump structure instead\n" " -t: Terse mode\n" " -T: Transpose vector, table, matrix\n" " -m: Monitor mode\n" @@ -1056,6 +1034,8 @@ void usage (void) " -F : Use as an alternate output field separator\n" " -f : Use as an input that provides a list PV name(s) to be read, use '-' for stdin\n" " -c: Wait for clean shutdown and report used instance count (for expert users)" + " enum format:\n" + " -n: Force enum interpretation of values as numbers (default is enum string)\n" "\n\nexamples:\n\n" "#! Get the value of the PV corr:li32:53:bdes\n" "> eget corr:li32:53:bdes\n" @@ -1472,7 +1452,7 @@ int main (int argc, char *argv[]) setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ - while ((opt = getopt(argc, argv, ":hr:s:a:w:zntTmxp:qdcF:f:")) != -1) { + while ((opt = getopt(argc, argv, ":hr:s:a:w:zNtTmxp:qdcF:f:n")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); @@ -1539,7 +1519,7 @@ int main (int argc, char *argv[]) case 'z': /* pvAccess RPC mode */ onlyQuery = true; break; - case 'n': /* Do not format NT types */ + case 'N': /* Do not format NT types */ dumpStructure = true; break; case 't': /* Terse mode */ @@ -1601,6 +1581,9 @@ int main (int argc, char *argv[]) fromStream = true; break; } + case 'n': + setEnumPrintMode(NumberEnum); + break; case '?': fprintf(stderr, "Unrecognized option: '-%c'. ('eget -h' for help.)\n", diff --git a/pvtoolsSrc/pvget.cpp b/pvtoolsSrc/pvget.cpp index cd9659f..0410f33 100644 --- a/pvtoolsSrc/pvget.cpp +++ b/pvtoolsSrc/pvget.cpp @@ -57,6 +57,8 @@ void usage (void) " -F : Use as an alternate output field separator\n" " -f : Use as an input that provides a list PV name(s) to be read, use '-' for stdin\n" " -c: Wait for clean shutdown and report used instance count (for expert users)\n" + " enum format:\n" + " -n: Force enum interpretation of values as numbers (default is enum string)\n" "\nexample: pvget double01\n\n" , DEFAULT_REQUEST, DEFAULT_TIMEOUT); } @@ -95,7 +97,12 @@ void printValue(std::string const & channelName, PVStructure::shared_pointer con else if (mode == TerseMode) terseStructure(std::cout, pv) << std::endl; else + { std::cout << channelName << std::endl << *(pv.get()) << std::endl << std::endl; + + //pvutil_ostream myos(std::cout.rdbuf()); + //myos << channelName << std::endl << *(pv.get()) << std::endl << std::endl; + } } @@ -339,7 +346,7 @@ int main (int argc, char *argv[]) setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ - while ((opt = getopt(argc, argv, ":hr:w:tmqdcF:f:")) != -1) { + while ((opt = getopt(argc, argv, ":hr:w:tmqdcF:f:n")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); @@ -397,6 +404,9 @@ int main (int argc, char *argv[]) fromStream = true; break; } + case 'n': + setEnumPrintMode(NumberEnum); + break; case '?': fprintf(stderr, "Unrecognized option: '-%c'. ('pvget -h' for help.)\n", diff --git a/pvtoolsSrc/pvlist.cpp b/pvtoolsSrc/pvlist.cpp index 672a26f..d263a55 100644 --- a/pvtoolsSrc/pvlist.cpp +++ b/pvtoolsSrc/pvlist.cpp @@ -604,7 +604,7 @@ int main (int argc, char *argv[]) if (quiet) cmd += 'q'; if (printInfo) - cmd += 'n'; + cmd += 'N'; cmd += "s pva://" + serverAddress + "/server?op="; if (printInfo) cmd += "info"; diff --git a/pvtoolsSrc/pvput.cpp b/pvtoolsSrc/pvput.cpp index 72d80dd..1ab4e84 100644 --- a/pvtoolsSrc/pvput.cpp +++ b/pvtoolsSrc/pvput.cpp @@ -28,8 +28,9 @@ using namespace std::tr1; using namespace epics::pvData; using namespace epics::pvAccess; -enum EnumMode { AutoEnum, NumberEnum, StringEnum }; -EnumMode enumMode = AutoEnum; +//EnumMode enumMode = AutoEnum; + +size_t fromString(PVFieldPtr const & pv, StringArray const & from, size_t fromStartIndex); size_t fromString(PVScalarArrayPtr const &pv, StringArray const & from, size_t fromStartIndex = 0) { @@ -114,64 +115,12 @@ size_t fromString(PVUnionPtr const & pvUnion, StringArray const & from, size_t f throw std::runtime_error("not enough of values"); string selector = from[fromStartIndex++]; - PVFieldPtr fieldField = pvUnion->select(selector); - if (!fieldField) + PVFieldPtr pv = pvUnion->select(selector); + if (!pv) throw std::runtime_error("invalid union selector value '" + selector + "'"); - size_t processed = 1; - - try - { - Type type = fieldField->getField()->getType(); - if(type==structure) { - PVStructurePtr pv = static_pointer_cast(fieldField); - size_t count = fromString(pv, from, fromStartIndex); - processed += count; - } - else if(type==scalarArray) { - PVScalarArrayPtr pv = static_pointer_cast(fieldField); - size_t count = fromString(pv, from, fromStartIndex); - processed += count; - } - else if(type==scalar) { - - if (fromStartIndex >= fromValueCount) - throw std::runtime_error("not enough of values"); - - PVScalarPtr pv = static_pointer_cast(fieldField); - getConvert()->fromString(pv, from[fromStartIndex]); - processed++; - } - else if(type==structureArray) { - PVStructureArrayPtr pv = static_pointer_cast(fieldField); - size_t count = fromString(pv, from, fromStartIndex); - processed += count; - } - else if(type==union_) { - PVUnionPtr pv = static_pointer_cast(fieldField); - size_t count = fromString(pv, from, fromStartIndex); - processed += count; - } - else if(type==unionArray) { - PVUnionArrayPtr pv = static_pointer_cast(fieldField); - size_t count = fromString(pv, from, fromStartIndex); - processed += count; - } - else { - std::ostringstream oss; - oss << "fromString unsupported fieldType " << type; - throw std::logic_error(oss.str()); - } - } - catch (std::exception &ex) - { - std::ostringstream os; - os << "failed to parse '" << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << "'"; - os << ": " << ex.what(); - throw std::runtime_error(os.str()); - } - - return processed; + size_t processed = fromString(pv, from, fromStartIndex); + return processed + 1; } size_t fromString(PVUnionArrayPtr const &pv, StringArray const & from, size_t fromStartIndex = 0) @@ -251,76 +200,68 @@ size_t fromString(PVStructurePtr const & pvStructure, StringArray const & from, } size_t processed = 0; - size_t fromValueCount = from.size(); PVFieldPtrArray const & fieldsData = pvStructure->getPVFields(); if (fieldsData.size() != 0) { size_t length = pvStructure->getStructure()->getNumberFields(); for(size_t i = 0; i < length; i++) { - PVFieldPtr fieldField = fieldsData[i]; - - try - { - Type type = fieldField->getField()->getType(); - if(type==structure) { - PVStructurePtr pv = static_pointer_cast(fieldField); - size_t count = fromString(pv, from, fromStartIndex); - processed += count; - fromStartIndex += count; - } - else if(type==scalarArray) { - PVScalarArrayPtr pv = static_pointer_cast(fieldField); - size_t count = fromString(pv, from, fromStartIndex); - processed += count; - fromStartIndex += count; - } - else if(type==scalar) { - - if (fromStartIndex >= fromValueCount) - throw std::runtime_error("not enough of values"); - - PVScalarPtr pv = static_pointer_cast(fieldField); - getConvert()->fromString(pv, from[fromStartIndex]); - processed++; - fromStartIndex++; - } - else if(type==structureArray) { - PVStructureArrayPtr pv = static_pointer_cast(fieldField); - size_t count = fromString(pv, from, fromStartIndex); - processed += count; - fromStartIndex += count; - } - else if(type==union_) { - PVUnionPtr pv = static_pointer_cast(fieldField); - size_t count = fromString(pv, from, fromStartIndex); - processed += count; - fromStartIndex += count; - } - else if(type==unionArray) { - PVUnionArrayPtr pv = static_pointer_cast(fieldField); - size_t count = fromString(pv, from, fromStartIndex); - processed += count; - fromStartIndex += count; - } - else { - std::ostringstream oss; - oss << "fromString unsupported fieldType " << type; - throw std::logic_error(oss.str()); - } - } - catch (std::exception &ex) - { - std::ostringstream os; - os << "failed to parse '" << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << "'"; - os << ": " << ex.what(); - throw std::runtime_error(os.str()); - } + size_t count = fromString(fieldsData[i], from, fromStartIndex); + processed += count; + fromStartIndex += count; } } return processed; } +size_t fromString(PVFieldPtr const & fieldField, StringArray const & from, size_t fromStartIndex) +{ + try + { + switch (fieldField->getField()->getType()) + { + case scalar: + { + if (fromStartIndex >= from.size()) + throw std::runtime_error("not enough of values"); + + PVScalarPtr pv = static_pointer_cast(fieldField); + getConvert()->fromString(pv, from[fromStartIndex]); + return 1; + } + + case scalarArray: + return fromString(static_pointer_cast(fieldField), from, fromStartIndex); + + case structure: + return fromString(static_pointer_cast(fieldField), from, fromStartIndex); + + case structureArray: + return fromString(static_pointer_cast(fieldField), from, fromStartIndex); + + case union_: + return fromString(static_pointer_cast(fieldField), from, fromStartIndex); + + case unionArray: + return fromString(static_pointer_cast(fieldField), from, fromStartIndex); + + default: + std::ostringstream oss; + oss << "fromString unsupported fieldType " << fieldField->getField()->getType(); + throw std::logic_error(oss.str()); + } + } + catch (std::exception &ex) + { + std::ostringstream os; + os << "failed to parse '" << fieldField->getField()->getID() << ' ' + << fieldField->getFieldName() << "'"; + os << ": " << ex.what(); + throw std::runtime_error(os.str()); + } +} + + #define DEFAULT_TIMEOUT 3.0 #define DEFAULT_REQUEST "field(value)" @@ -368,6 +309,27 @@ void printValue(std::string const & channelName, PVStructure::shared_pointer con Type valueType = value->getField()->getType(); if (valueType != scalar && valueType != scalarArray) { + // special case for enum + if (valueType == structure) + { + PVStructurePtr pvStructure = static_pointer_cast(value); + if (pvStructure->getStructure()->getID() == "enum_t") + { + if (fieldSeparator == ' ') + std::cout << std::setw(30) << std::left << channelName; + else + std::cout << channelName; + + std::cout << fieldSeparator; + + printEnumT(std::cout, pvStructure); + + std::cout << std::endl; + + return; + } + } + // switch to structure mode std::cout << channelName << std::endl << *(pv.get()) << std::endl << std::endl; } @@ -705,6 +667,7 @@ int main (int argc, char *argv[]) std::cout << std::boolalpha; terseSeparator(fieldSeparator); + setEnumPrintMode(enumMode); ClientFactory::start(); ChannelProvider::shared_pointer provider = getChannelProviderRegistry()->getProvider("pva"); diff --git a/pvtoolsSrc/pvutils.cpp b/pvtoolsSrc/pvutils.cpp index b3688b2..9512556 100644 --- a/pvtoolsSrc/pvutils.cpp +++ b/pvtoolsSrc/pvutils.cpp @@ -10,6 +10,7 @@ #include #include +#include using namespace std; using namespace std::tr1; @@ -68,6 +69,18 @@ void terseArrayCount(bool flag) arrayCountFlag = flag; } +EnumMode enumMode = AutoEnum; +void setEnumPrintMode(EnumMode mode) +{ + enumMode = mode; +} + +bool formatTTypesFlag = true; +void formatTTypes(bool flag) +{ + formatTTypesFlag = flag; +} + std::ostream& terse(std::ostream& o, PVField::shared_pointer const & pv) { @@ -99,6 +112,54 @@ std::ostream& terse(std::ostream& o, PVField::shared_pointer const & pv) } } +std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvEnumT) +{ + PVInt::shared_pointer pvIndex = pvEnumT->getSubField("index"); + if (!pvIndex) + throw std::runtime_error("enum_t structure does not have 'int index' field"); + + PVStringArray::shared_pointer pvChoices = pvEnumT->getSubField("choices"); + if (!pvChoices) + throw std::runtime_error("enum_t structure does not have 'string choices[]' field"); + + if (enumMode == AutoEnum || enumMode == StringEnum) + { + int32 ix = pvIndex->get(); + if (ix < 0 || ix > static_cast(pvChoices->getLength())) + o << ix; + else + pvChoices->dumpValue(o, ix); + } + else + o << pvIndex->get(); + + return o; +} + +std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvTimeT) +{ + #define TIMETEXTLEN 32 + char timeText[TIMETEXTLEN]; + epicsTimeStamp epicsTS; + + PVTimeStamp pvTimeStamp; + if (pvTimeStamp.attach(pvTimeT)) + { + TimeStamp ts; + pvTimeStamp.get(ts); + + epicsTS.secPastEpoch = ts.getEpicsSecondsPastEpoch(); + epicsTS.nsec = ts.getNanoseconds(); + } + else + throw std::runtime_error("invalid time_t structure"); + + epicsTimeToStrftime(timeText, TIMETEXTLEN, "%Y-%m-%dT%H:%M:%S.%03f", &epicsTS); + o << timeText; + + return o; +} + std::ostream& terseStructure(std::ostream& o, PVStructure::shared_pointer const & pvStructure) { if (!pvStructure) @@ -107,6 +168,22 @@ std::ostream& terseStructure(std::ostream& o, PVStructure::shared_pointer const return o; } + // special t-types support (enum_t and time_t, etc.) + if (formatTTypesFlag) + { + string id = pvStructure->getStructure()->getID(); + if (id == "enum_t") + { + printEnumT(o, pvStructure); + return o; + } + else if (id == "time_t") + { + printTimeT(o, pvStructure); + return o; + } + } + PVFieldPtrArray fieldsData = pvStructure->getPVFields(); size_t length = pvStructure->getStructure()->getNumberFields(); bool first = true; diff --git a/pvtoolsSrc/pvutils.h b/pvtoolsSrc/pvutils.h index 2f5041c..45d8ce4 100644 --- a/pvtoolsSrc/pvutils.h +++ b/pvtoolsSrc/pvutils.h @@ -16,6 +16,13 @@ std::ostream& terseScalarArray(std::ostream& o, epics::pvData::PVScalarArray::sh std::ostream& terseStructureArray(std::ostream& o, epics::pvData::PVStructureArray::shared_pointer const & pvArray); std::ostream& terseUnionArray(std::ostream& o, epics::pvData::PVUnionArray::shared_pointer const & pvArray); +enum EnumMode { AutoEnum, NumberEnum, StringEnum }; +void setEnumPrintMode(EnumMode mode); + +void formatTTypes(bool flag); + +std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvEnumT); +std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvTimeT); /* Converts a hex character to its integer value */ char from_hex(char ch); @@ -103,3 +110,69 @@ struct dump_stack_only_on_debug }; std::ostream& operator<<(std::ostream& os, const dump_stack_only_on_debug& d); + + + +/* +#include +#include + +// usage: pvutil_ostream myos(std::cout.rdbuf()); + +class pvutil_ostream : private std::ostream +{ +public: + pvutil_ostream(std::streambuf* sb) + : std::ostream(sb) + {} + + template + friend pvutil_ostream& operator<<(pvutil_ostream&, const T&); + + // Additional overload to handle ostream specific io manipulators + friend pvutil_ostream& operator<<(pvutil_ostream&, std::ostream& (*)(std::ostream&)); + + // Accessor function to get a reference to the ostream + std::ostream& get_ostream() { return *this; } +}; + + +template +inline pvutil_ostream& +operator<<(pvutil_ostream& out, const T& value) +{ + static_cast(out) << '.'; + static_cast(out) << value; + return out; +} + +// overload for std::ostream specific io manipulators +inline pvutil_ostream& +operator<<(pvutil_ostream& out, std::ostream& (*func)(std::ostream&)) +{ + static_cast(out) << '#'; + static_cast(out) << func; + return out; +} + +// overload for PVField +template <> +inline pvutil_ostream& +operator<<(pvutil_ostream& out, const epics::pvData::PVField& value) +{ + static_cast(out) << '?'; +// static_cast(out) << value; + value.dumpValue(out); + return out; +} + +template <> +inline pvutil_ostream& +operator<<(pvutil_ostream& out, const epics::pvData::PVStructure& value) +{ + static_cast(out) << '!'; +// static_cast(out) << value; + value.dumpValue(out); + return out; +} +*/ From 66f2995a71e9490418872926dedc58727de0851c Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 15 Oct 2014 14:13:55 +0200 Subject: [PATCH 4/5] pvlist for servers w/ multiple providers --- src/server/responseHandlers.cpp | 56 +++++++++++++++++++++++---------- src/server/serverContext.cpp | 2 +- src/server/serverContext.h | 2 +- 3 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/server/responseHandlers.cpp b/src/server/responseHandlers.cpp index 86dd22b..ec7a892 100644 --- a/src/server/responseHandlers.cpp +++ b/src/server/responseHandlers.cpp @@ -451,6 +451,10 @@ public: return _waitEvent.wait(timeoutSec); } + void resetEvent() { + _waitEvent.tryWait(); + } + private: epics::pvData::Mutex _waitMutex; epics::pvData::Event _waitEvent; @@ -504,24 +508,44 @@ public: string op = opField->get(); if (op == "channels") { - ChannelListRequesterImpl::shared_pointer listListener(new ChannelListRequesterImpl()); - m_serverContext->getChannelProviders()[0]->channelList(listListener); // TODO multiple channel providers !!!! - if (!listListener->waitForCompletion(TIMEOUT_SEC)) - throw RPCRequestException(Status::STATUSTYPE_ERROR, "failed to fetch channel list due to timeout"); - - Status& status = listListener->status; - if (!status.isSuccess()) - { - string errorMessage = "failed to fetch channel list: " + status.getMessage(); - if (!status.getStackDump().empty()) - errorMessage += "\n" + status.getStackDump(); - throw RPCRequestException(Status::STATUSTYPE_ERROR, errorMessage); - } - PVStructure::shared_pointer result = getPVDataCreate()->createPVStructure(channelListStructure); - PVStringArray::shared_pointer pvArray = result->getSubField("value"); - pvArray->replace(listListener->channelNames); + PVStringArray::shared_pointer allChannelNames = result->getSubField("value"); + + ChannelListRequesterImpl::shared_pointer listListener(new ChannelListRequesterImpl()); + std::vector providers = m_serverContext->getChannelProviders(); + + size_t providerCount = providers.size(); + for (size_t i = 0; i < providerCount; i++) + { + providers[i]->channelList(listListener); + if (!listListener->waitForCompletion(TIMEOUT_SEC)) + throw RPCRequestException(Status::STATUSTYPE_ERROR, "failed to fetch channel list due to timeout"); + + Status& status = listListener->status; + if (!status.isSuccess()) + { + string errorMessage = "failed to fetch channel list: " + status.getMessage(); + if (!status.getStackDump().empty()) + errorMessage += "\n" + status.getStackDump(); + throw RPCRequestException(Status::STATUSTYPE_ERROR, errorMessage); + } + + // optimization + if (providerCount == 1) + { + allChannelNames->replace(listListener->channelNames); + } + else + { + PVStringArray::svector list(allChannelNames->reuse()); + std::copy(listListener->channelNames.begin(), listListener->channelNames.end(), + back_inserter(list)); + allChannelNames->replace(freeze(list)); + } + + listListener->resetEvent(); + } return result; } diff --git a/src/server/serverContext.cpp b/src/server/serverContext.cpp index fc3b453..460bcae 100644 --- a/src/server/serverContext.cpp +++ b/src/server/serverContext.cpp @@ -645,7 +645,7 @@ void ServerContextImpl::setChannelProviderName(std::string channelProviderName) _channelProviderNames = channelProviderName; } -std::vector ServerContextImpl::getChannelProviders() +std::vector& ServerContextImpl::getChannelProviders() { return _channelProviders; } diff --git a/src/server/serverContext.h b/src/server/serverContext.h index 7336bdb..61fb5e2 100644 --- a/src/server/serverContext.h +++ b/src/server/serverContext.h @@ -291,7 +291,7 @@ public: * Get channel providers. * @return channel providers. */ - std::vector getChannelProviders(); + std::vector& getChannelProviders(); /** * Return true if channel provider name is provided by configuration (e.g. system env. var.). From cfbbe577db3ece0ebc38f0b83aa818f34d876f74 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 15 Oct 2014 16:10:41 +0200 Subject: [PATCH 5/5] enhanced printout, enum_t/time_t support --- pvtoolsSrc/eget.cpp | 21 ++++-- pvtoolsSrc/pvget.cpp | 57 +++++++++++---- pvtoolsSrc/pvutils.cpp | 11 ++- pvtoolsSrc/pvutils.h | 156 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 209 insertions(+), 36 deletions(-) diff --git a/pvtoolsSrc/eget.cpp b/pvtoolsSrc/eget.cpp index ce7597d..a4cd903 100644 --- a/pvtoolsSrc/eget.cpp +++ b/pvtoolsSrc/eget.cpp @@ -864,7 +864,9 @@ void formatNT(std::ostream& o, PVFieldPtr const & pv) else { std::cerr << "non-normative type" << std::endl; - o << *(pv.get()) << std::endl; + //o << *(pv.get()) << std::endl; + pvutil_ostream myos(std::cout.rdbuf()); + myos << *(pv.get()) << std::endl; } return; @@ -872,14 +874,21 @@ void formatNT(std::ostream& o, PVFieldPtr const & pv) } // no ID, just dump - o << *(pv.get()) << std::endl; + pvutil_ostream myos(std::cout.rdbuf()); + myos << *(pv.get()) << std::endl; } void dumpValue(std::string const & channelName, PVField::shared_pointer const & pv) { if (!channelName.empty()) std::cout << channelName << std::endl; - std::cout << *(pv.get()) << std::endl << std::endl; + //std::cout << *(pv.get()) << std::endl << std::endl; + + pvutil_ostream myos(std::cout.rdbuf()); + if (pv->getField()->getType() == structure) + myos << *(static_pointer_cast(pv).get()) << std::endl << std::endl; + else + myos << *(pv.get()) << std::endl << std::endl; } void printValue(std::string const & channelName, PVStructure::shared_pointer const & pv, bool forceTerseWithName = false) @@ -2085,7 +2094,11 @@ int main (int argc, char *argv[]) if (rpcRequesterImpl->getLastResponse().get() == 0) std::cout << "(null)" << std::endl; else - std::cout << *(rpcRequesterImpl->getLastResponse().get()) << std::endl; + { + //std::cout << *(rpcRequesterImpl->getLastResponse().get()) << std::endl; + pvutil_ostream myos(std::cout.rdbuf()); + myos << *(rpcRequesterImpl->getLastResponse().get()) << std::endl; + } } else formatNT(std::cout, rpcRequesterImpl->getLastResponse()); diff --git a/pvtoolsSrc/pvget.cpp b/pvtoolsSrc/pvget.cpp index 0410f33..b75fd0e 100644 --- a/pvtoolsSrc/pvget.cpp +++ b/pvtoolsSrc/pvget.cpp @@ -71,16 +71,30 @@ void printValue(std::string const & channelName, PVStructure::shared_pointer con if (value.get() == 0) { std::cerr << "no 'value' field" << std::endl; - std::cout << channelName << std::endl << *(pv.get()) << std::endl << std::endl; + //std::cout << channelName << std::endl << *(pv.get()) << std::endl << std::endl; + pvutil_ostream myos(std::cout.rdbuf()); + myos << channelName << std::endl << *(pv.get()) << std::endl << std::endl; } else { Type valueType = value->getField()->getType(); if (valueType != scalar && valueType != scalarArray) { - // switch to structure mode - std::cout << channelName << std::endl << *(pv.get()) << std::endl << std::endl; - } + // switch to structure mode, unless it's NTEnum + if (value->getField()->getID() == "enum_t") + { + std::cout << std::setw(30) << std::left << channelName; + std::cout << fieldSeparator; + printEnumT(std::cout, static_pointer_cast(value)); + std::cout << std::endl; + } + else + { + //std::cout << channelName << std::endl << *(pv.get()) << std::endl << std::endl; + pvutil_ostream myos(std::cout.rdbuf()); + myos << channelName << std::endl << *(pv.get()) << std::endl << std::endl; + } + } else { if (fieldSeparator == ' ' && value->getField()->getType() == scalar) @@ -98,10 +112,9 @@ void printValue(std::string const & channelName, PVStructure::shared_pointer con terseStructure(std::cout, pv) << std::endl; else { - std::cout << channelName << std::endl << *(pv.get()) << std::endl << std::endl; - - //pvutil_ostream myos(std::cout.rdbuf()); - //myos << channelName << std::endl << *(pv.get()) << std::endl << std::endl; + //std::cout << channelName << std::endl << *(pv.get()) << std::endl << std::endl; + pvutil_ostream myos(std::cout.rdbuf()); + myos << channelName << std::endl << *(pv.get()) << std::endl << std::endl; } } @@ -262,17 +275,31 @@ class MonitorRequesterImpl : public MonitorRequester { std::cerr << "no 'value' field" << std::endl; std::cout << m_channelName << std::endl; - std::cout << *(element->pvStructurePtr.get()) << std::endl << std::endl; + //std::cout << *(element->pvStructurePtr.get()) << std::endl << std::endl; + pvutil_ostream myos(std::cout.rdbuf()); + myos << *(element->pvStructurePtr.get()) << std::endl << std::endl; } else { Type valueType = value->getField()->getType(); if (valueType != scalar && valueType != scalarArray) { - // switch to structure mode - std::cout << m_channelName << std::endl; - std::cout << *(element->pvStructurePtr.get()) << std::endl << std::endl; - } + // switch to structure mode, unless it's NTEnum + if (value->getField()->getID() == "enum_t") + { + std::cout << std::setw(30) << std::left << m_channelName; + std::cout << fieldSeparator; + printEnumT(std::cout, static_pointer_cast(value)); + std::cout << std::endl; + } + else + { + std::cout << m_channelName << std::endl; + //std::cout << *(element->pvStructurePtr.get()) << std::endl << std::endl; + pvutil_ostream myos(std::cout.rdbuf()); + myos << *(element->pvStructurePtr.get()) << std::endl << std::endl; + } + } else { if (fieldSeparator == ' ' && value->getField()->getType() == scalar) @@ -300,7 +327,9 @@ class MonitorRequesterImpl : public MonitorRequester else { std::cout << m_channelName << std::endl; - std::cout << *(element->pvStructurePtr.get()) << std::endl << std::endl; + //std::cout << *(element->pvStructurePtr.get()) << std::endl << std::endl; + pvutil_ostream myos(std::cout.rdbuf()); + myos << *(element->pvStructurePtr.get()) << std::endl << std::endl; } monitor->release(element); diff --git a/pvtoolsSrc/pvutils.cpp b/pvtoolsSrc/pvutils.cpp index 9512556..aa43535 100644 --- a/pvtoolsSrc/pvutils.cpp +++ b/pvtoolsSrc/pvutils.cpp @@ -112,13 +112,13 @@ std::ostream& terse(std::ostream& o, PVField::shared_pointer const & pv) } } -std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvEnumT) +std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure const & pvEnumT) { - PVInt::shared_pointer pvIndex = pvEnumT->getSubField("index"); + PVInt::shared_pointer pvIndex = pvEnumT.getSubField("index"); if (!pvIndex) throw std::runtime_error("enum_t structure does not have 'int index' field"); - PVStringArray::shared_pointer pvChoices = pvEnumT->getSubField("choices"); + PVStringArray::shared_pointer pvChoices = pvEnumT.getSubField("choices"); if (!pvChoices) throw std::runtime_error("enum_t structure does not have 'string choices[]' field"); @@ -136,6 +136,11 @@ std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::shared_poi return o; } +std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvEnumT) +{ + return printEnumT(o, *pvEnumT); +} + std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvTimeT) { #define TIMETEXTLEN 32 diff --git a/pvtoolsSrc/pvutils.h b/pvtoolsSrc/pvutils.h index 45d8ce4..184c662 100644 --- a/pvtoolsSrc/pvutils.h +++ b/pvtoolsSrc/pvutils.h @@ -21,6 +21,8 @@ void setEnumPrintMode(EnumMode mode); void formatTTypes(bool flag); +std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure const & pvEnumT); +//std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure const & pvTimeT); std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvEnumT); std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::shared_pointer const & pvTimeT); @@ -113,7 +115,7 @@ std::ostream& operator<<(std::ostream& os, const dump_stack_only_on_debug& d); -/* + #include #include @@ -129,6 +131,8 @@ public: template friend pvutil_ostream& operator<<(pvutil_ostream&, const T&); + friend pvutil_ostream& dumpPVStructure(pvutil_ostream&, const epics::pvData::PVStructure &, bool); + // Additional overload to handle ostream specific io manipulators friend pvutil_ostream& operator<<(pvutil_ostream&, std::ostream& (*)(std::ostream&)); @@ -141,7 +145,6 @@ template inline pvutil_ostream& operator<<(pvutil_ostream& out, const T& value) { - static_cast(out) << '.'; static_cast(out) << value; return out; } @@ -150,29 +153,152 @@ operator<<(pvutil_ostream& out, const T& value) inline pvutil_ostream& operator<<(pvutil_ostream& out, std::ostream& (*func)(std::ostream&)) { - static_cast(out) << '#'; static_cast(out) << func; return out; } -// overload for PVField template <> inline pvutil_ostream& -operator<<(pvutil_ostream& out, const epics::pvData::PVField& value) +operator<<(pvutil_ostream& o, const epics::pvData::PVField::shared_pointer & fieldField); + +template <> +inline pvutil_ostream& +operator<<(pvutil_ostream& o, const epics::pvData::PVStructure & value); + +template <> +inline pvutil_ostream& +operator<<(pvutil_ostream& o, const epics::pvData::PVUnion::shared_pointer & value); + +template <> +inline pvutil_ostream& +operator<<(pvutil_ostream& o, const epics::pvData::PVStructure::shared_pointer & value) { - static_cast(out) << '?'; -// static_cast(out) << value; - value.dumpValue(out); - return out; + std::string id = value->getStructure()->getID(); + if (id == "enum_t") + { + o << epics::pvData::format::indent() << id << ' ' << value->getFieldName() << " # "; + printEnumT(o, value); + o << std::endl; + dumpPVStructure(o, *value, false); + return o; + } + else if (id == "time_t") + { + o << epics::pvData::format::indent() << id << ' ' << value->getFieldName() << " # "; + printTimeT(o, value); + o << std::endl; + dumpPVStructure(o, *value, false); + return o; + } + + return o << *value; } template <> inline pvutil_ostream& -operator<<(pvutil_ostream& out, const epics::pvData::PVStructure& value) +operator<<(pvutil_ostream& o, const epics::pvData::PVStructureArray::shared_pointer & value) { - static_cast(out) << '!'; -// static_cast(out) << value; - value.dumpValue(out); - return out; + o << epics::pvData::format::indent() << value->getStructureArray()->getID() + << ' ' << value->getFieldName() << std::endl; + size_t length = value->getLength(); + if (length > 0) + { + epics::pvData::format::indent_scope s(o); + + epics::pvData::PVStructureArray::const_svector data(value->view()); + for (size_t i = 0; i < length; i++) + o << data[i]; + } + + return o; } -*/ + +template <> +inline pvutil_ostream& +operator<<(pvutil_ostream& o, const epics::pvData::PVUnionArray::shared_pointer & value) +{ + o << epics::pvData::format::indent() << value->getUnionArray()->getID() + << ' ' << value->getFieldName() << std::endl; + size_t length = value->getLength(); + if (length > 0) + { + epics::pvData::format::indent_scope s(o); + + epics::pvData::PVUnionArray::const_svector data(value->view()); + for (size_t i = 0; i < length; i++) + o << data[i]; + } + + return o; +} + +template <> +inline pvutil_ostream& +operator<<(pvutil_ostream& o, const epics::pvData::PVUnion::shared_pointer & value) +{ + o << epics::pvData::format::indent() << value->getUnion()->getID() + << ' ' << value->getFieldName() << std::endl; + { + epics::pvData::format::indent_scope s(o); + + epics::pvData::PVFieldPtr fieldField = value->get(); + if (fieldField.get() == NULL) + o << epics::pvData::format::indent() << "(none)" << std::endl; + else + o << fieldField; + } + return o; +} + +template <> +inline pvutil_ostream& +operator<<(pvutil_ostream& o, const epics::pvData::PVField::shared_pointer & fieldField) +{ + epics::pvData::Type type = fieldField->getField()->getType(); + if (type == epics::pvData::scalar || type == epics::pvData::scalarArray) + o << epics::pvData::format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl; + else if (type == epics::pvData::structure) + o << std::tr1::static_pointer_cast(fieldField); + else if (type == epics::pvData::structureArray) + o << std::tr1::static_pointer_cast(fieldField); + else if (type == epics::pvData::union_) + o << std::tr1::static_pointer_cast(fieldField); + else if (type == epics::pvData::unionArray) + o << std::tr1::static_pointer_cast(fieldField); + else + throw std::runtime_error("unsupported type"); + + return o; +} + +pvutil_ostream& +dumpPVStructure(pvutil_ostream& o, const epics::pvData::PVStructure & value, bool showHeader) +{ + if (showHeader) + { + std::string id = value.getStructure()->getID(); + o << epics::pvData::format::indent() << id << ' ' << value.getFieldName(); + o << std::endl; + } + + { + epics::pvData::format::indent_scope s(o); + + epics::pvData::PVFieldPtrArray const & fieldsData = value.getPVFields(); + if (fieldsData.size() != 0) { + size_t length = value.getStructure()->getNumberFields(); + for(size_t i=0; i +inline pvutil_ostream& +operator<<(pvutil_ostream& o, const epics::pvData::PVStructure& value) +{ + return dumpPVStructure(o, value, true); +} +