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 diff --git a/pvtoolsSrc/eget.cpp b/pvtoolsSrc/eget.cpp index c94ed84..a4cd903 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) @@ -886,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; @@ -894,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) @@ -1045,7 +1032,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 +1043,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 +1461,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 +1528,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 +1590,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", @@ -2102,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 cd9659f..b75fd0e 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); } @@ -69,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) @@ -95,7 +111,11 @@ 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; + { + //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; + } } @@ -255,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) @@ -293,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); @@ -339,7 +375,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 +433,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 74e0eee..1ab4e84 100644 --- a/pvtoolsSrc/pvput.cpp +++ b/pvtoolsSrc/pvput.cpp @@ -28,6 +28,10 @@ using namespace std::tr1; using namespace epics::pvData; using namespace epics::pvAccess; +//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) { int processed = 0; @@ -98,68 +102,164 @@ 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 pv = pvUnion->select(selector); + if (!pv) + throw std::runtime_error("invalid union selector value '" + selector + "'"); + + size_t processed = fromString(pv, from, fromStartIndex); + return processed + 1; +} + +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(); 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(); - // TODO union/unionArray support - 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++; - } - else if(type==structureArray) { - PVStructureArrayPtr 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()); - } - } - 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 @@ -185,10 +285,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) @@ -204,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; } @@ -416,7 +542,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 +594,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", @@ -535,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..aa43535 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,59 @@ std::ostream& terse(std::ostream& o, PVField::shared_pointer const & pv) } } +std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure 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& 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 + 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 +173,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..184c662 100644 --- a/pvtoolsSrc/pvutils.h +++ b/pvtoolsSrc/pvutils.h @@ -16,6 +16,15 @@ 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 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); /* Converts a hex character to its integer value */ char from_hex(char ch); @@ -103,3 +112,193 @@ 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&); + + 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&)); + + // 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) << 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) << func; + return out; +} + +template <> +inline pvutil_ostream& +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) +{ + 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& o, const epics::pvData::PVStructureArray::shared_pointer & value) +{ + 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); +} + diff --git a/src/pva/pvaVersion.h b/src/pva/pvaVersion.h index 5352ff7..1a2fa3a 100644 --- a/src/pva/pvaVersion.h +++ b/src/pva/pvaVersion.h @@ -25,7 +25,7 @@ // module version // TODO to be generated, etc. #define EPICS_PVA_MAJOR_VERSION 4 -#define EPICS_PVA_MINOR_VERSION 1 +#define EPICS_PVA_MINOR_VERSION 0 #define EPICS_PVA_MAINTENANCE_VERSION 0 #define EPICS_PVA_DEVELOPMENT_FLAG 1 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.).