From 6dea9d90cc64ad81d49b05cb238848380efc2819 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Sep 2018 09:29:49 -0700 Subject: [PATCH] pvutils rework --- pvtoolsSrc/Makefile | 12 + pvtoolsSrc/eget.cpp | 1934 +------------------------------------- pvtoolsSrc/pvcall.cpp | 254 +++++ pvtoolsSrc/pvget.cpp | 791 ++++++---------- pvtoolsSrc/pvinfo.cpp | 184 ++-- pvtoolsSrc/pvlist.cpp | 4 + pvtoolsSrc/pvmonitor.cpp | 3 + pvtoolsSrc/pvput.cpp | 355 ++----- pvtoolsSrc/pvutils.cpp | 559 +++-------- pvtoolsSrc/pvutils.h | 292 +----- 10 files changed, 893 insertions(+), 3495 deletions(-) create mode 100644 pvtoolsSrc/pvcall.cpp create mode 100644 pvtoolsSrc/pvmonitor.cpp diff --git a/pvtoolsSrc/Makefile b/pvtoolsSrc/Makefile index d85a43e..0766217 100644 --- a/pvtoolsSrc/Makefile +++ b/pvtoolsSrc/Makefile @@ -7,18 +7,30 @@ USR_CPPFLAGS += -I$(TOP)/src/remote PROD_HOST += pvget pvget_SRCS += pvget.cpp +pvget_SRCS += pvutils.cpp + +PROD_HOST += pvmonitor +pvmonitor_SRCS += pvmonitor.cpp +pvmonitor_SRCS += pvutils.cpp PROD_HOST += pvput pvput_SRCS += pvput.cpp +pvput_SRCS += pvutils.cpp + +PROD_HOST += pvcall +pvcall_SRCS += pvcall.cpp +pvcall_SRCS += pvutils.cpp PROD_HOST += pvinfo pvinfo_SRCS += pvinfo.cpp +pvinfo_SRCS += pvutils.cpp PROD_HOST += pvlist pvlist_SRCS += pvlist.cpp PROD_HOST += eget eget_SRCS += eget.cpp +eget_SRCS += pvutils.cpp PROD_LIBS += pvAccessCA pvAccess pvData ca Com diff --git a/pvtoolsSrc/eget.cpp b/pvtoolsSrc/eget.cpp index d05fb94..5723c51 100644 --- a/pvtoolsSrc/eget.cpp +++ b/pvtoolsSrc/eget.cpp @@ -1,1026 +1,23 @@ - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "pvutils.cpp" - -using namespace std; -namespace TR1 = std::tr1; -using namespace epics::pvData; -using namespace epics::pvAccess; - -enum PrintMode { ValueOnlyMode, StructureMode, TerseMode }; -PrintMode mode = ValueOnlyMode; - -bool columnMajor = false; - -bool transpose = false; - -bool dumpStructure = false; - -#if defined(_WIN32) && !defined(_MINGW) -FILE *popen(const char *command, const char *mode) { - return _popen(command, mode); -} -int pclose(FILE *stream) { - return _pclose(stream); -} -#endif - -void formatNTAny(std::ostream& o, PVStructurePtr const & pvStruct) -{ - PVFieldPtr value = pvStruct->getSubField("value"); - if (value.get() == 0) - { - std::cerr << "no 'value' field in NTAny" << std::endl; - return; - } - - o << *value; -} - -void formatNTScalar(std::ostream& o, PVStructurePtr const & pvStruct) -{ - PVScalarPtr value = TR1::dynamic_pointer_cast(pvStruct->getSubField("value")); - if (value.get() == 0) - { - std::cerr << "no scalar_t 'value' field in NTScalar" << std::endl; - return; - } - - o << *value; -} - -std::ostream& formatVector(std::ostream& o, - string label, - PVScalarArrayPtr const & pvScalarArray, - bool transpose) -{ - size_t len = pvScalarArray->getLength(); - - if (!transpose) - { - if (!label.empty()) - o << label << std::endl; - - for (size_t i = 0; i < len; i++) - pvScalarArray->dumpValue(o, i) << std::endl; - } - else - { - bool first = true; - if (!label.empty()) - { - o << label; - first = false; - } - - for (size_t i = 0; i < len; i++) { - if (first) - first = false; - else - o << fieldSeparator; - - pvScalarArray->dumpValue(o, i); - } - } - - return o; -} - /* -std::ostream& formatScalarArray(std::ostream& o, PVScalarArrayPtr const & pvScalarArray) + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ + +#include + +#define EXECNAME "eget" + +#define MAIN getMain +#include "pvget.cpp" + +#undef MAIN +#define MAIN callMain +#include "pvcall.cpp" + +static +void egetUsage (void) { - size_t len = pvScalarArray->getLength(); - if (len == 0) - { - // TODO do we really want this - o << "(empty)" << std::endl; - } - else - { - for (size_t i = 0; i < len; i++) - pvScalarArray->dumpValue(o, i) << std::endl; - } - return o; -} -*/ - -void formatNTScalarArray(std::ostream& o, PVStructurePtr const & pvStruct) -{ - PVScalarArrayPtr value = TR1::dynamic_pointer_cast(pvStruct->getSubField("value")); - if (value.get() == 0) - { - std::cerr << "no scalar_t[] 'value' field in NTScalarArray" << std::endl; - return; - } - - //o << *value; - //formatScalarArray(o, value); - formatVector(o, "", value, mode == TerseMode || transpose); -} - -void formatNTEnum(std::ostream& o, PVStructurePtr const & pvStruct) -{ - PVStructurePtr enumt = TR1::dynamic_pointer_cast(pvStruct->getSubField("value")); - if (enumt.get() == 0) - { - std::cerr << "no enum_t 'value' field in NTEnum" << std::endl; - return; - } - - printEnumT(o, enumt); -} - -size_t getLongestString(shared_vector const & array) -{ - size_t max = 0; - size_t len = array.size(); - for (size_t i = 0; i < len; i++) - { - size_t l = array[i].size(); - if (l > max) max = l; - } - return max; -} - -size_t getLongestString(PVScalarArrayPtr const & array) -{ - size_t max = 0; - - string empty; - - ostringstream oss; - size_t len = array->getLength(); - for (size_t i = 0; i < len; i++) - { - oss.str(empty); - array->dumpValue(oss, i); - size_t l = oss.tellp(); - if (l > max) max = l; - } - return max; -} - -// labels are optional -// if provided labels.size() must equals columnData.size() -void formatTable(std::ostream& o, - shared_vector const & labels, - vector const & columnData, - bool showHeader, bool transpose) -{ - // array with maximum number of elements - size_t maxValues = 0; - - // value with longest string form - size_t maxLabelColumnLength = (showHeader && labels.size()) ? getLongestString(labels) : 0; - size_t maxColumnLength = 0; - // - // get maxValue and maxColumnLength - // - size_t numColumns = columnData.size(); - for (size_t i = 0; i < numColumns; i++) - { - PVScalarArrayPtr array = columnData[i]; - if (array.get()) - { - size_t arrayLength = array->getLength(); - if (maxValues < arrayLength) maxValues = arrayLength; - - size_t colLen = getLongestString(array); - if (colLen > maxColumnLength) maxColumnLength = colLen; - } - } - - // add some space - size_t padding = 2; - maxColumnLength += padding; - - if (!transpose) - { - /* non-compact - maxLabelColumnLength += padding; - - // increase maxColumnLength to maxLabelColumnLength - if (maxLabelColumnLength > maxColumnLength) - maxColumnLength = maxLabelColumnLength; - */ - - // - // , , ... - // values values ... - // - - // first print labels - if (showHeader && labels.size()) - { - for (size_t i = 0; i < numColumns; i++) - { - if (fieldSeparator == ' ') - { - int width = std::max(labels[i].size()+padding, maxColumnLength); - o << std::setw(width) << std::right; - // non-compact o << std::setw(maxColumnLength) << std::right; - } - else if (i > 0) - { - o << fieldSeparator; - } - - o << labels[i]; - } - o << std::endl; - } - - // then values - for (size_t r = 0; r < maxValues; r++) - { - for (size_t i = 0; i < numColumns; i++) - { - if (fieldSeparator == ' ' && (showHeader || numColumns > 1)) - { - int width = std::max(labels[i].size()+padding, maxColumnLength); - o << setw(width) << std::right; - // non-compact o << std::setw(maxColumnLength) << std::right; - } - else if (i > 0) - { - o << fieldSeparator; - } - - PVScalarArrayPtr array = columnData[i]; - if (array.get() && r < array->getLength()) - array->dumpValue(o, r); - else - o << ""; - } - o << std::endl; - } - - } - else - { - - // - // values... - // values... - // ... - // - - for (size_t i = 0; i < numColumns; i++) - { - if (showHeader && labels.size()) - { - if (fieldSeparator == ' ') - { - o << std::setw(maxLabelColumnLength) << std::left; - } - o << labels[i]; - } - - for (size_t r = 0; r < maxValues; r++) - { - if (fieldSeparator == ' ' && (showHeader || numColumns > 1)) - { - o << std::setw(maxColumnLength) << std::right; - } - else if (showHeader || r > 0) - o << fieldSeparator; - - PVScalarArrayPtr array = columnData[i]; - if (array.get() && r < array->getLength()) - array->dumpValue(o, r); - else - o << ""; - } - o << std::endl; - } - - } -} - -void formatNTTable(std::ostream& o, PVStructurePtr const & pvStruct) -{ - PVStringArrayPtr labels = pvStruct->getSubField("labels"); - if (labels.get() == 0) - { - std::cerr << "no string[] 'labels' field in NTTable" << std::endl; - return; - } - - PVStructurePtr value = pvStruct->getSubField("value"); - if (value.get() == 0) - { - std::cerr << "no 'value' structure in NTTable" << std::endl; - return; - } - - vector columnData; - PVFieldPtrArray fields = value->getPVFields(); - size_t numColumns = fields.size(); - - if (labels->getLength() != numColumns) - { - std::cerr << "malformed NTTable, length of 'labels' array does not equal to a number of 'value' structure subfields" << std::endl; - return; - } - - for (size_t i = 0; i < numColumns; i++) - { - PVScalarArrayPtr array = TR1::dynamic_pointer_cast(fields[i]); - if (array.get() == 0) - { - std::cerr << "malformed NTTable, " << (i+1) << ". field is not scalar_t[]" << std::endl; - return; - } - - columnData.push_back(array); - } - - bool showHeader = (mode != TerseMode); - formatTable(o, labels->view(), columnData, showHeader, transpose); -} - - -void formatNTMatrix(std::ostream& o, PVStructurePtr const & pvStruct) -{ - PVDoubleArrayPtr value = pvStruct->getSubField("value"); - if (value.get() == 0) - { - std::cerr << "no double[] 'value' field in NTMatrix" << std::endl; - return; - } - - int32 rows, cols; - - PVIntArrayPtr dim = pvStruct->getSubField("dim"); - if (dim.get() != 0) - { - // dim[] = { rows, columns } - size_t dims = dim->getLength(); - if (dims != 1 && dims != 2) - { - std::cerr << "malformed NTMatrix, dim[] must contain 1 or 2 elements instead of " << dims << std::endl; - return; - } - - PVIntArray::const_svector data = dim->view(); - rows = data[0]; - cols = (dims == 2) ? data[1] : 1; - - if (rows <= 0 || cols <= 0) - { - std::cerr << "malformed NTMatrix, dim[] must contain elements > 0" << std::endl; - return; - } - } - else - { - // column vector - rows = value->getLength(); - cols = 1; - } - - o << std::left; - - size_t len = static_cast(rows*cols); - if (len != value->getLength()) - { - std::cerr << "malformed NTMatrix, values[] must contain " << len << " elements instead of " << value->getLength() << std::endl; - return; - } - - // add some space - size_t padding = 2; - size_t maxColumnLength = getLongestString(value) + padding; - - if (!transpose) - { - - // - // el1 el2 el3 - // el4 el5 el6 - // - - size_t ix = 0; - for (int32 r = 0; r < rows; r++) - { - for (int32 c = 0; c < cols; c++) - { - if (fieldSeparator == ' ' && cols > 1) - o << std::setw(maxColumnLength) << std::right; - else if (c > 0) - o << fieldSeparator; - - if (columnMajor) - value->dumpValue(o, r + c * rows); - else - value->dumpValue(o, ix++); - } - o << std::endl; - } - - } - else - { - // - // el1 el4 - // el2 el5 - // el3 el6 - // - size_t ix = 0; - for (int32 c = 0; c < cols; c++) - { - for (int32 r = 0; r < rows; r++) - { - if (fieldSeparator == ' ' && rows > 1) - o << std::setw(maxColumnLength) << std::right; - else if (r > 0) - o << fieldSeparator; - - if (columnMajor) - value->dumpValue(o, ix++); - else - value->dumpValue(o, r * cols + c); - } - o << std::endl; - } - } -} - - -// TODO use formatNTTable -void formatNTNameValue(std::ostream& o, PVStructurePtr const & pvStruct) -{ - PVStringArrayPtr name = pvStruct->getSubField("name"); - if (name.get() == 0) - { - std::cerr << "no string[] 'name' field in NTNameValue" << std::endl; - return; - } - - PVFieldPtr value = pvStruct->getSubField("value"); - if (value.get() == 0) - { - std::cerr << "no 'value' field in NTNameValue" << std::endl; - return; - } - - PVScalarArrayPtr array = TR1::dynamic_pointer_cast(value); - if (array.get() == 0) - { - std::cerr << "malformed NTNameValue, 'value' field is not scalar_t[]" << std::endl; - return; - } - - if (name->getLength() != array->getLength()) - { - std::cerr << "malformed NTNameValue, length of 'name' and 'value' array does not equal" << std::endl; - return; - } - - size_t numColumns = name->getLength(); - - // get names - PVStringArray::const_svector nameData = name->view(); - - // get max column name size - bool showHeader = (mode != TerseMode); - - size_t maxLabelColumnLength = showHeader ? getLongestString(nameData) : 0; - size_t maxColumnLength = showHeader ? getLongestString(array) : 0; - - // add some space - size_t padding = 2; - maxColumnLength += padding; - - if (transpose) - { - /* non-compact - maxLabelColumnLength += padding; - - // increase maxColumnLength to maxLabelColumnLength - if (maxLabelColumnLength > maxColumnLength) - maxColumnLength = maxLabelColumnLength; - */ - - // - // , , ... - // value values ... - // - - // first print names - if (showHeader) - { - for (size_t i = 0; i < numColumns; i++) - { - if (fieldSeparator == ' ') - { - int width = std::max(nameData[i].size()+padding, maxColumnLength); - o << std::setw(width) << std::right; - // non-compact o << std::setw(maxColumnLength) << std::right; - } - else if (i > 0) - { - o << fieldSeparator; - } - - o << nameData[i]; - } - o << std::endl; - } - - // then values - for (size_t i = 0; i < numColumns; i++) - { - if (fieldSeparator == ' ' && showHeader) - { - int width = std::max(nameData[i].size()+padding, maxColumnLength); - o << std::setw(width) << std::right; - // non-compact o << std::setw(maxColumnLength) << std::right; - } - else if (i > 0) - { - o << fieldSeparator; - } - array->dumpValue(o, i); - } - o << std::endl; - } - else - { - - // - // values... - // values... - // ... - // - - for (size_t i = 0; i < numColumns; i++) - { - if (showHeader) - { - if (fieldSeparator == ' ') - { - o << std::setw(maxLabelColumnLength) << std::left; - } - o << nameData[i]; - } - - if (fieldSeparator == ' ' && showHeader) - { - o << std::setw(maxColumnLength) << std::right; - } - else if (showHeader) - { - o << fieldSeparator; - } - - array->dumpValue(o, i); - o << std::endl; - } - - } - -} - -void formatNTURI(std::ostream& o, PVStructurePtr const & pvStruct) -{ - PVStringPtr scheme = TR1::dynamic_pointer_cast(pvStruct->getSubField("scheme")); - if (scheme.get() == 0) - { - std::cerr << "no string 'scheme' field in NTURI" << std::endl; - } - - PVStringPtr authority = TR1::dynamic_pointer_cast(pvStruct->getSubField("authority")); - - PVStringPtr path = TR1::dynamic_pointer_cast(pvStruct->getSubField("path")); - if (path.get() == 0) - { - std::cerr << "no string 'path' field in NTURI" << std::endl; - return; - } - - PVStructurePtr query = TR1::dynamic_pointer_cast(pvStruct->getSubField("query")); - - o << scheme->get() << "://"; - if (authority.get()) o << authority->get(); - o << '/' << path->get(); - - // query - if (query.get()) - { - PVFieldPtrArray fields = query->getPVFields(); - size_t numColumns = fields.size(); - - if (numColumns > 0) - { - o << '?'; - - for (size_t i = 0; i < numColumns; i++) - { - if (i) - o << '&'; - - // TODO encode value!!! - o << fields[i]->getFieldName() << '=' << *(fields[i].get()); - } - } - } - - o << std::endl; -} - - -void formatNTNDArray(std::ostream& /*o*/, PVStructurePtr const & pvStruct) -{ - - PVUnion::shared_pointer pvUnion = pvStruct->getSubField("value"); - if (pvUnion.get() == 0) - { - std::cerr << "no 'value' union field in NTNDArray" << std::endl; - return; - } - - PVScalarArray::shared_pointer value = pvUnion->get(); - if (value.get() == 0) - { - std::cerr << "'value' union field in given NTNDArray does not hold scalar array value" << std::endl; - return; - } - - // undefined - int32 cm = -1; - - PVStructureArray::shared_pointer pvAttributes = pvStruct->getSubField("attribute"); - PVStructureArray::const_svector attributes(pvAttributes->view()); - - for (PVStructureArray::const_svector::const_iterator iter = attributes.begin(); - iter != attributes.end(); - iter++) - { - PVStructure::shared_pointer attribute = *iter; - PVString::shared_pointer pvName = attribute->getSubField("name"); - if (pvName && pvName->get() == "ColorMode") - { - PVInt::shared_pointer pvCM = attribute->getSubField("value")->get(); - if (!pvCM) - break; - - cm = pvCM->get(); - break; - } - } - - if (cm == -1) - { - std::cerr << "no PVInt 'ColorMode' attribute in NTNDArray" << std::endl; - return; - } - - if (cm != 0 && cm != 1 && cm != 2) - { - std::cerr << "unsupported 'ColorMode', only {0,1,2} modes are supported" << std::endl; - return; - } - - - int32 rows, cols; - - PVStructureArray::shared_pointer dim = pvStruct->getSubField("dimension"); - if (dim.get() == 0) - { - std::cerr << "no 'dimension' structure array field in NTNDArray" << std::endl; - return; - } - - // dim[] = { rows, columns } or - // dim[] = { 3, rows, columns } - PVStructureArray::const_svector data = dim->view(); - size_t dims = dim->getLength(); - size_t imageSize; - if ((cm == 0 || cm == 1) && dims == 2) - { - cols = data[0]->getSubField("size")->get(); - rows = data[1]->getSubField("size")->get(); - imageSize = cols * rows; - } - else if (cm == 2 && dims == 3) - { - cols = data[1]->getSubField("size")->get(); - rows = data[2]->getSubField("size")->get(); - imageSize = cols * rows * 3; - } - else - { - std::cerr << "malformed NTNDArray, dimension[] is invalid for specified color mode" << std::endl; - return; - } - - if (rows <= 0 || cols <= 0) - { - std::cerr << "malformed NTNDArray, dimension[] gives negative size" << std::endl; - return; - } - - PVByteArrayPtr array = TR1::dynamic_pointer_cast(value); - if (array.get() == 0) - { - std::cerr << "currently only byte[] value is supported" << std::endl; - return; - } - - if (array->getLength() != imageSize) - { - std::cerr << "value array length does not match expected image size (" << - array->getLength() << " != " << imageSize << ")" << std::endl; - return; - } - - PVByteArray::const_svector img = array->view(); - /* - size_t len = static_cast(rows*cols); - for (size_t i = 0; i < len; i++) - o << img[i]; - */ - //eget -s testImage | gnuplot -e "set size ratio -1; set palette grey; set cbrange [0:255]; plot '-' binary array=(512,384) flipy format='%uchar' with image" - - FILE* gnuplotPipe = popen ("gnuplot -persist", "w"); // use -persist for backward compatibility (to support older gnuplot versions) - - const char *prologue = getenv("EGET_GNUPLOT_PROLOGUE"); - if (prologue) - fprintf(gnuplotPipe, "%s\n", prologue); - - fprintf(gnuplotPipe, "set format \"\"\n"); - fprintf(gnuplotPipe, "unset key\n"); - fprintf(gnuplotPipe, "unset border\n"); - fprintf(gnuplotPipe, "unset colorbox\n"); - fprintf(gnuplotPipe, "unset xtics\n"); - fprintf(gnuplotPipe, "unset ytics\n"); - - fprintf(gnuplotPipe, "set size ratio 1\n"); - fprintf(gnuplotPipe, "set xrange [0:%u]\n", cols-1); - fprintf(gnuplotPipe, "set yrange [0:%u]\n", rows-1); - - if (cm == 2) - { - // RGB - - fprintf(gnuplotPipe, "plot '-' binary array=(%u,%u) flipy format='%%uchar' with rgbimage\n", cols, rows); - - for (size_t i = 0; i < imageSize; i++) - fprintf(gnuplotPipe, "%c", img[i]); - } - else - { - // monochrome - - fprintf(gnuplotPipe, "set palette grey\n"); - fprintf(gnuplotPipe, "set cbrange [0:255]\n"); - - fprintf(gnuplotPipe, "plot '-' binary array=(%u,%u) flipy format='%%uchar' with image\n", cols, rows); - - for (size_t i = 0; i < imageSize; i++) - fprintf(gnuplotPipe, "%c", img[i]); - } - - fflush(gnuplotPipe); - pclose(gnuplotPipe); - -} - -typedef void(*NTFormatterFunc)(std::ostream& o, PVStructurePtr const & pvStruct); -typedef map NTFormatterLUTMap; -NTFormatterLUTMap ntFormatterLUT; - -void initializeNTFormatterLUT() -{ - ntFormatterLUT["epics:nt/NTScalar:1"] = formatNTScalar; - ntFormatterLUT["epics:nt/NTScalarArray:1"] = formatNTScalarArray; - ntFormatterLUT["epics:nt/NTEnum:1"] = formatNTEnum; - ntFormatterLUT["epics:nt/NTTable:1"] = formatNTTable; - ntFormatterLUT["epics:nt/NTMatrix:1"] = formatNTMatrix; - ntFormatterLUT["epics:nt/NTAny:1"] = formatNTAny; - ntFormatterLUT["epics:nt/NTNameValue:1"] = formatNTNameValue; - ntFormatterLUT["epics:nt/NTURI:1"] = formatNTURI; - ntFormatterLUT["epics:nt/NTNDArray:1"] = formatNTNDArray; -} - -void formatNT(std::ostream& o, PVFieldPtr const & pv) -{ - static bool lutInitialized = false; - if (!lutInitialized) - { - initializeNTFormatterLUT(); - lutInitialized = true; - } - - if (pv.get() == 0) - { - o << "(null)" << std::endl; - return; - } - - Type type = pv->getField()->getType(); - if (type==structure) - { - PVStructurePtr pvStruct = TR1::static_pointer_cast(pv); - { - string id = pvStruct->getField()->getID(); - - // remove minor - size_t pos = id.find_last_of('.'); - if (pos != string::npos) - id = id.substr(0, pos); - - NTFormatterLUTMap::const_iterator formatter = ntFormatterLUT.find(id); - if (formatter != ntFormatterLUT.end()) - { - (formatter->second)(o, pvStruct); - } - else - { - std::cerr << "non-normative type" << std::endl; - //o << *(pv.get()) << std::endl; - pvutil_ostream myos(std::cout); - myos << *(pv.get()) << std::endl; - } - - return; - } - } - - // no ID, just dump - pvutil_ostream myos(std::cout); - 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; - - pvutil_ostream myos(std::cout); - if (pv->getField()->getType() == structure) - myos << *(TR1::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) -{ - if (forceTerseWithName) - { - if (!channelName.empty()) - std::cout << channelName << fieldSeparator; - terseStructure(std::cout, pv) << std::endl; - } - else if (mode == ValueOnlyMode) - { - PVField::shared_pointer value = pv->getSubField("value"); - if (value.get() == 0) - { - std::cerr << "no 'value' field" << std::endl; - dumpValue(channelName, pv); - } - else - { - Type valueType = value->getField()->getType(); - if (valueType == scalar) - std::cout << *(value.get()) << std::endl; - else if (valueType == scalarArray) - { - //formatScalarArray(std::cout, TR1::dynamic_pointer_cast(value)); - formatVector(std::cout, "", TR1::dynamic_pointer_cast(value), false); - } - else - { - // switch to structure mode, unless it's T-type - if (valueType == structure && isTType(TR1::static_pointer_cast(value))) - { - formatTType(std::cout, TR1::static_pointer_cast(value)); - std::cout << std::endl; - } - else - dumpValue(channelName, pv); - } - } - } - else if (mode == TerseMode) - terseStructure(std::cout, pv) << std::endl; - else - dumpValue(channelName, pv); -} - -// only in ValueOnlyMode -// NOTE: names might be empty -void printValues(shared_vector const & names, vector const & values) -{ - size_t len = values.size(); - - vector scalars; - vector scalarArrays; - - for (size_t i = 0; i < len; i++) - { - PVField::shared_pointer value = values[i]->getSubField("value"); - if (value.get() != 0) - { - Type type = value->getField()->getType(); - if (type == scalarArray) - scalarArrays.push_back(TR1::dynamic_pointer_cast(value)); - else if (type == scalar) - { - PVScalar::shared_pointer scalar = TR1::dynamic_pointer_cast(value); - scalars.push_back(scalar); - - // make an array, i.e. PVStringArray, out of a scalar (since scalar is an array w/ element count == 1) - PVStringArray::shared_pointer StringArray = - TR1::dynamic_pointer_cast(getPVDataCreate()->createPVScalarArray(pvString)); - - PVStringArray::svector values; - values.push_back(scalar->getAs()); - StringArray->replace(freeze(values)); - - scalarArrays.push_back(StringArray); - } - } - else - { - // a structure detected, break - break; - } - } - - if (scalars.size() == len) - { - if (!transpose) - { - bool first = true; - for (size_t i = 0; i < len; i++) - { - if (first) - first = false; - else - std::cout << fieldSeparator; - std::cout << *(scalars[i].get()); - } - std::cout << std::endl; - } - else - { - for (size_t i = 0; i < len; i++) - std::cout << *(scalars[i].get()) << std::endl; - } - } - else if (scalarArrays.size() == len) - { - // format as a table - bool showHeader = false; //(mode != TerseMode); - formatTable(std::cout, names, scalarArrays, showHeader, transpose); - } - else - { - // force terse mode with names - for (size_t i = 0; i < len; i++) - printValue(names[i], values[i], true); - } -} - -#define DEFAULT_TIMEOUT 3.0 -#define DEFAULT_REQUEST "field(value)" -#define DEFAULT_RPC_REQUEST "" -#define DEFAULT_PROVIDER "pva" - -double timeOut = DEFAULT_TIMEOUT; -string request(DEFAULT_REQUEST); - - -void usage (void) -{ - fprintf (stderr, "\nUsage: eget [options] [... | -s ]\n\n" + fprintf (stderr, "\nUsage: eget [options] [-s] ... [-a =]...\n\n" "\noptions:\n" " -h: Help: Print this message\n" " -V: Print version and exit\n" @@ -1028,20 +25,20 @@ void usage (void) " -a : Service argument in 'name[=value]' or 'name value' form\n" " -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" - " -i: Do not format standard types (enum_t, time_t, ...)\n" - " -t: Terse mode\n" - " -T: Transpose vector, table, matrix\n" - " -x: Use column-major order to decode matrix\n" +// " -z: Pure pvAccess RPC based service (send NTURI.query as request argument)\n" +// " -N: Do not format NT types, dump structure instead\n" +// " -i: Do not format standard types (enum_t, time_t, ...)\n" +// " -t: Terse mode\n" +// " -T: Transpose vector, table, matrix\n" +// " -x: Use column-major order to decode matrix\n" " -p : Set default provider name, default is '%s'\n" - " -q: Quiet mode, print only error messages\n" +// " -q: Quiet mode, print only error messages\n" " -d: Enable debug output\n" - " -F : Use as an alternate output field fieldSeparator\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" +// " -F : Use as an alternate output field fieldSeparator\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" "\n\nexamples:\n\n" "#! Get the value of the PV corr:li32:53:bdes\n" "> eget corr:li32:53:bdes\n" @@ -1055,872 +52,21 @@ void usage (void) "#! Get polynomials for bunch of quads using a stdin to give a list of PV names\n" "> eget -s names -a pattern=QUAD:LTU1:8%%:POLYCOEF | eget -f -\n" "\n" - , DEFAULT_REQUEST, DEFAULT_TIMEOUT, DEFAULT_PROVIDER); + , request.c_str(), timeout, defaultProvider.c_str()); } - - -class ChannelGetRequesterImpl : public ChannelGetRequester -{ -private: - string m_channelName; - bool m_printValue; - - PVStructure::shared_pointer m_pvStructure; - BitSet::shared_pointer m_bitSet; - Mutex m_pointerMutex; - Event m_event; - - bool m_done; - -public: - - ChannelGetRequesterImpl(std::string channelName, bool printValue) : - m_channelName(channelName), - m_printValue(printValue), - m_done(false) - { - } - - virtual string getRequesterName() - { - return "ChannelGetRequesterImpl"; - } - - virtual void channelGetConnect(const epics::pvData::Status& status, - ChannelGet::shared_pointer const & channelGet, - epics::pvData::Structure::const_shared_pointer const & /*structure*/) - { - if (status.isSuccess()) - { - // show warning - if (!status.isOK()) - { - std::cerr << "[" << m_channelName << "] channel get create: " << dump_stack_only_on_debug(status) << std::endl; - } - - channelGet->lastRequest(); - channelGet->get(); - } - else - { - std::cerr << "[" << m_channelName << "] failed to create channel get: " << dump_stack_only_on_debug(status) << std::endl; - m_event.signal(); - } - } - - virtual void getDone(const epics::pvData::Status& status, - ChannelGet::shared_pointer const & /*channelGet*/, - epics::pvData::PVStructure::shared_pointer const & pvStructure, - epics::pvData::BitSet::shared_pointer const & bitSet) - { - if (status.isSuccess()) - { - // show warning - if (!status.isOK()) - { - std::cerr << "[" << m_channelName << "] channel get: " << dump_stack_only_on_debug(status) << std::endl; - } - - // access smart pointers - { - Lock lock(m_pointerMutex); - m_pvStructure = pvStructure; - m_bitSet = bitSet; - m_done = true; - - if (m_printValue) - { - printValue(m_channelName, m_pvStructure); - } - } - } - else - { - std::cerr << "[" << m_channelName << "] failed to get: " << dump_stack_only_on_debug(status) << std::endl; - } - - m_event.signal(); - } - - PVStructure::shared_pointer getPVStructure() - { - Lock lock(m_pointerMutex); - return m_pvStructure; - } - - bool waitUntilGet(double timeOut) - { - bool signaled = m_event.wait(timeOut); - if (!signaled) - { - std::cerr << "[" << m_channelName << "] get timeout" << std::endl; - return false; - } - - Lock lock(m_pointerMutex); - return m_done; - } -}; - - -class ChannelRPCRequesterImpl : public ChannelRPCRequester -{ -private: - Mutex m_pointerMutex; - Event m_event; - Event m_connectionEvent; - bool m_successfullyConnected; - string m_channelName; - - PVStructure::shared_pointer m_lastResponse; - bool m_done; - -public: - - ChannelRPCRequesterImpl(std::string channelName) : - m_successfullyConnected(false), - m_channelName(channelName), - m_done(false) - { - } - - virtual string getRequesterName() - { - return "ChannelRPCRequesterImpl"; - } - - virtual void channelRPCConnect(const epics::pvData::Status& status, ChannelRPC::shared_pointer const & /*channelRPC*/) - { - if (status.isSuccess()) - { - // show warning - if (!status.isOK()) - { - std::cerr << "[" << m_channelName << "] channel RPC create: " << dump_stack_only_on_debug(status) << std::endl; - } - - { - Lock lock(m_pointerMutex); - m_successfullyConnected = status.isSuccess(); - } - - m_connectionEvent.signal(); - } - else - { - std::cerr << "[" << m_channelName << "] failed to create channel RPC: " << dump_stack_only_on_debug(status) << std::endl; - m_connectionEvent.signal(); - } - } - - virtual void requestDone (const epics::pvData::Status &status, - ChannelRPC::shared_pointer const & /*channelRPC*/, - epics::pvData::PVStructure::shared_pointer const &pvResponse) - { - if (status.isSuccess()) - { - // show warning - if (!status.isOK()) - { - std::cerr << "[" << m_channelName << "] channel RPC: " << dump_stack_only_on_debug(status) << std::endl; - } - - // access smart pointers - { - Lock lock(m_pointerMutex); - - m_done = true; - m_lastResponse = pvResponse; - - /* - formatNT(std::cout, pvResponse); - std::cout << std::endl; - */ - } - } - else - { - std::cerr << "[" << m_channelName << "] failed to RPC: " << dump_stack_only_on_debug(status) << std::endl; - } - - m_event.signal(); - } - - /* - void request(epics::pvData::PVStructure::shared_pointer const &pvRequest) - { - Lock lock(m_pointerMutex); - m_channelRPC->request(pvRequest, false); - } - */ - - PVStructure::shared_pointer getLastResponse() - { - Lock lock(m_pointerMutex); - return m_lastResponse; - } - - bool waitUntilRPC(double timeOut) - { - bool signaled = m_event.wait(timeOut); - if (!signaled) - { - std::cerr << "[" << m_channelName << "] RPC timeout" << std::endl; - return false; - } - - Lock lock(m_pointerMutex); - return m_done; - } - - bool waitUntilConnected(double timeOut) - { - bool signaled = m_connectionEvent.wait(timeOut); - if (!signaled) - { - std::cerr << "[" << m_channelName << "] RPC create timeout" << std::endl; - return false; - } - - Lock lock(m_pointerMutex); - return m_successfullyConnected; - } -}; - - - - -/*+************************************************************************** - * - * Function: main - * - * Description: eget main() - * Evaluate command line options, set up PVA, connect the - * channels, print the data as requested - * - * Arg(s) In: [options] ... - * - * Arg(s) Out: none - * - * Return(s): Standard return code (0=success, 1=error) - * - **************************************************************************-*/ - int main (int argc, char *argv[]) { - int opt; /* getopt() current option */ - bool debug = false; - bool cleanupAndReport = false; - - bool serviceRequest = false; - bool pvRequestProvidedByUser = false; - bool onlyQuery = false; - - istream* inputStream = 0; - ifstream ifs; - bool fromStream = false; - - string service; - //string urlEncodedRequest; - vector< pair > parameters; - string defaultProvider = DEFAULT_PROVIDER; - - setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ - - while ((opt = getopt(argc, argv, ":hVr:s:a:w:zNtTmxp:qdcF:f:ni")) != -1) { - switch (opt) { - case 'h': /* Print usage */ - usage(); - return 0; - case 'V': /* Print version */ - { - Version version("eget", "cpp", - EPICS_PVA_MAJOR_VERSION, - EPICS_PVA_MINOR_VERSION, - EPICS_PVA_MAINTENANCE_VERSION, - EPICS_PVA_DEVELOPMENT_FLAG); - fprintf(stdout, "%s\n", version.getVersionString().c_str()); - return 0; - } - case 'w': /* Set PVA timeout value */ - if((epicsScanDouble(optarg, &timeOut)) != 1 || timeOut <= 0.0) - { - fprintf(stderr, "'%s' is not a valid timeout value " - "- ignored. ('eget -h' for help.)\n", optarg); - timeOut = DEFAULT_TIMEOUT; - } - break; - case 'r': /* Set pvRequest value */ - request = optarg; - pvRequestProvidedByUser = true; - // do not override terse mode - if (mode == ValueOnlyMode) mode = StructureMode; - break; - case 'a': /* Service parameters */ - { - string param = optarg; - size_t eqPos = param.find('='); - if (eqPos==0) - { - // no name - - fprintf(stderr, "Parameter not specified in '-a name=value' or '-a name value' form. ('eget -h' for help.)\n"); - return 1; - } - else if (eqPos==string::npos) - { - // is next argument actually a value, i.e. "-a name value" form - if (optind < argc && *argv[optind] != '-') - { - parameters.push_back(pair(param, argv[optind])); - optind++; - } - else - { - // no value - - //fprintf(stderr, "Parameter not specified in '-a name=value' or '-a name value' form. ('eget -h' for help.)\n"); - //return 1; - parameters.push_back(pair(param, "")); - } - } - else - { - parameters.push_back(pair(param.substr(0, eqPos), param.substr(eqPos+1, string::npos))); - } - /* - if (urlEncodedRequest.size()) - urlEncodedRequest += '&'; - char* encoded = url_encode(optarg); - urlEncodedRequest += encoded; - free(encoded); - */ - break; - } - case 's': /* Service name */ - service = optarg; - serviceRequest = true; - break; - case 'z': /* pvAccess RPC mode */ - onlyQuery = true; - break; - case 'N': /* Do not format NT types */ - dumpStructure = true; - break; - case 'i': /* T-types format mode */ - formatTTypesFlag = false; - break; - case 't': /* Terse mode */ - mode = TerseMode; - break; - case 'T': /* Terse mode */ - transpose = true; - break; - case 'm': /* Monitor mode */ - std::cerr<<"Monitor mode is broken and has been disabled until fixed\n"; - exit(1); - break; - case 'x': /* Column-major order mode */ - columnMajor = true; - break; - case 'p': /* Provider name */ - defaultProvider = optarg; - - // for now no only pva/ca schema is supported - // TODO - if (defaultProvider != "pva" && defaultProvider != "ca") - { - std::cerr << "invalid default provider '" << defaultProvider << "', only 'pva' and 'ca' are supported" << std::endl; - // TODO - return 1; - } - - break; - case 'q': /* Quiet mode */ - break; - case 'd': /* Debug log level */ - debug = true; - break; - case 'c': /* Clean-up and report used instance count */ - cleanupAndReport = true; - break; - case 'F': /* Store this for output formatting */ - fieldSeparator = (char) *optarg; - break; - case 'f': /* Use input stream as input */ - { - string fileName = optarg; - if (fileName == "-") - inputStream = &cin; - else - { - ifs.open(fileName.c_str(), ifstream::in); - if (!ifs) - { - fprintf(stderr, - "Failed to open file '%s'.\n", - fileName.c_str()); - return 1; - } - else - inputStream = &ifs; - } - - fromStream = true; - break; - } - case 'n': - enumMode = NumberEnum; - break; - case '?': - fprintf(stderr, - "Unrecognized option: '-%c'. ('eget -h' for help.)\n", - optopt); - return 1; - case ':': - fprintf(stderr, - "Option '-%c' requires an argument. ('eget -h' for help.)\n", - optopt); - return 1; - default : - usage(); - return 1; + bool found_s = false; + for(int i=1; i 0) - { - // do not allow reading file and command line specified pvs - fromStream = false; - } - else if (nPvs < 1 && !serviceRequest && !fromStream) - { - fprintf(stderr, "No PV name(s) specified. ('eget -h' for help.)\n"); - return 1; - } - - // only one pv, arguments provided without serviceRequest switch - if (nPvs == 1 && parameters.size() > 0) - { - // switch to serviceRequest - service = argv[optind]; - serviceRequest = true; - nPvs = 0; - } - else if (nPvs > 0 && serviceRequest) - { - fprintf(stderr, "PV name(s) specified and service query requested. ('eget -h' for help.)\n"); - return 1; - } - - SET_LOG_LEVEL(debug ? logLevelDebug : logLevelError); - - std::cout << std::boolalpha; - arrayCountFlag = false; - - bool allOK = true; - - // parse URI - // try to parse as URI if only one nPvs - URI uri; - bool validURI = - (serviceRequest || nPvs == 1) ? - URI::parse(serviceRequest ? service : argv[optind], uri) : - false; - - // if there is only one nPvs and it's a valid URI that has ? character, - // then it's an service (RPC) request - if (!serviceRequest && validURI && uri.query_indicated) - { - service = argv[optind]; - serviceRequest = true; - } - - // register "ca" provider - epics::pvAccess::ca::CAClientFactory::start(); - - // PVs mode - if (!serviceRequest) - { - vector pvs; - vector pvsAddress; - vector providers; - vector operations; - - if (validURI) - { - // standard get request - // for now no only pva/ca schema is supported, without authority - // TODO - if (uri.protocol != "pva" && uri.protocol != "ca") - { - std::cerr << "invalid URI scheme '" << uri.protocol << "', only 'pva' and 'ca' are supported" << std::endl; - // TODO - return 1; - } - - if (uri.path.length() <= 1) - { - std::cerr << "invalid URI, empty path" << std::endl; - // TODO - return 1; - } - - // skip trailing '/' - pvs.push_back(uri.path.substr(1)); - pvsAddress.push_back(uri.host); - providers.push_back(ChannelProviderRegistry::clients()->getProvider(uri.protocol)); - if(!providers.back()) { - std::cerr<<"Unknown provider \""< uris; - - if(!fromStream) { - for (int n = 0; optind < argc; n++, optind++) - { - uris.push_back(argv[optind]); - } - } else { - string cn; - while (true) - { - *inputStream >> cn; - if (!(*inputStream)) - break; - uris.push_back(cn); - } - } - - for (size_t n = 0; ngetProvider(uri.protocol)); - } - else - { - uri.protocol = defaultProvider; - pvs.push_back(uris[n]); - pvsAddress.push_back(std::string()); - providers.push_back(ChannelProviderRegistry::clients()->getProvider(defaultProvider)); - } - - if(!providers.back()) { - std::cerr<<"Unknown provider \""<createRequest(request); - if(pvRequest.get()==0) { - fprintf(stderr, "failed to parse request string\n"); - return 1; - } - - // first connect to all, this allows resource (e.g. TCP connection) sharing - vector channels(nPvs); - for (int n = 0; n < nPvs; n++) - { - if(!providers[n]) continue; - channels[n] = providers[n]->createChannel(pvs[n], DefaultChannelRequester::build(), - ChannelProvider::PRIORITY_DEFAULT, pvsAddress[n]); - - if(!channels[n]) { - std::cerr<<"No such channel '"< 1 && !fromStream; - - vector collectedValues; - shared_vector collectedNames; - if (collectValues) - { - collectedValues.reserve(nPvs); - collectedNames.reserve(nPvs); - } - - // for now a simple iterating sync implementation, guarantees order - for (int n = 0; n < nPvs; n++) - { - Channel::shared_pointer channel(channels[n]); - - if(!channel) { - allOK = false; - continue; - } - - { - TR1::shared_ptr getFieldRequesterImpl; - - // probe for value field - // but only if there is only one PV request (otherwise mode change makes a mess) - if (mode == ValueOnlyMode && nPvs == 1) - { - getFieldRequesterImpl.reset(new GetFieldRequesterImpl(channel)); - // get all to be immune to bad clients not supporting selective getField request - channel->getField(getFieldRequesterImpl, ""); - } - - if (getFieldRequesterImpl.get() == 0 || - getFieldRequesterImpl->waitUntilFieldGet(timeOut)) - { - // check probe - if (getFieldRequesterImpl.get()) - { - Structure::const_shared_pointer structure = - TR1::dynamic_pointer_cast(getFieldRequesterImpl->getField()); - if (structure.get() == 0 || structure->getField("value").get() == 0) - { - // fallback to structure - mode = StructureMode; - pvRequest = CreateRequest::create()->createRequest("field()"); - } - } - - TR1::shared_ptr getRequesterImpl( - new ChannelGetRequesterImpl(channel->getChannelName(), false) - ); - ChannelGet::shared_pointer channelGet = channel->createChannelGet(getRequesterImpl, pvRequest); - bool ok = getRequesterImpl->waitUntilGet(timeOut); - allOK &= ok; - if (ok) - { - if (collectValues) - { - collectedValues.push_back(getRequesterImpl->getPVStructure()); - collectedNames.push_back(channel->getChannelName()); - } - else - { - // print immediately - printValue(channel->getChannelName(), getRequesterImpl->getPVStructure(), fromStream); - } - } - } - else - { - allOK = false; - channel->destroy(); - std::cerr << "[" << channel->getChannelName() << "] failed to get channel introspection data" << std::endl; - } - } - } - - if (collectValues) - printValues(freeze(collectedNames), collectedValues); - - } - // service RPC mode + if(found_s) + return callMain(argc, argv); else - { - string authority; - - if (validURI) - { - if (uri.protocol != "pva") - { - std::cerr << "invalid URI scheme '" << uri.protocol << "', only 'pva' is supported" << std::endl; - // TODO - return 1; - } - - authority = uri.host; - - if (uri.path.length() <= 1) - { - std::cerr << "invalid URI, empty path" << std::endl; - // TODO - return 1; - } - - // skip trailing '/' - service = uri.path.substr(1); - - string::const_iterator end_i = uri.query.end(); - string::const_iterator begin_i = uri.query.begin(); - while (begin_i != end_i) - { - string::const_iterator pair_end_i = find(begin_i, end_i, '&'); - - string::const_iterator name_end_i = find(begin_i, pair_end_i, '='); - if (name_end_i != pair_end_i) - { - string name(begin_i, name_end_i); - string value(name_end_i+1, pair_end_i); - parameters.push_back(pair(name, value)); - } - else - { - //fprintf(stderr, "Parameter not specified in name=value form. ('eget -h' for help.)\n"); - //return 1; - string name(begin_i, pair_end_i); - parameters.push_back(pair(name, "")); - } - - begin_i = pair_end_i; - if (begin_i != end_i) - begin_i++; // skip '&' - } - } - - /* - std::cerr << "service : " << service << std::endl; - std::cerr << "parameters : " << std::endl; - - vector< pair >::iterator iter = parameters.begin(); - for (; iter != parameters.end(); iter++) - std::cerr << " " << iter->first << " = " << iter->second << std::endl; - //std::cerr << "encoded URL request: '" << urlEncodedRequest << "'" << std::endl; - */ - - // simply empty - PVStructure::shared_pointer pvRequest = - CreateRequest::create()->createRequest( - !pvRequestProvidedByUser ? DEFAULT_RPC_REQUEST : request - ); - if(pvRequest.get()==NULL) { - fprintf(stderr, "failed to parse request string\n"); - return 1; - } - - - StringArray queryFieldNames; - FieldConstPtrArray queryFields; - for (vector< pair >::iterator iter = parameters.begin(); - iter != parameters.end(); - iter++) - { - queryFieldNames.push_back(iter->first); - queryFields.push_back(getFieldCreate()->createScalar(pvString)); - } - - Structure::const_shared_pointer queryStructure( - getFieldCreate()->createStructure( - queryFieldNames, - queryFields - ) - ); - - - - StringArray uriFieldNames; - uriFieldNames.push_back("scheme"); - if (!authority.empty()) uriFieldNames.push_back("authority"); - uriFieldNames.push_back("path"); - uriFieldNames.push_back("query"); - - FieldConstPtrArray uriFields; - uriFields.push_back(getFieldCreate()->createScalar(pvString)); - if (!authority.empty()) uriFields.push_back(getFieldCreate()->createScalar(pvString)); - uriFields.push_back(getFieldCreate()->createScalar(pvString)); - uriFields.push_back(queryStructure); - - Structure::const_shared_pointer uriStructure( - getFieldCreate()->createStructure( - "epics:nt/NTURI:1.0", - uriFieldNames, - uriFields - ) - ); - - - - PVStructure::shared_pointer request( - getPVDataCreate()->createPVStructure(uriStructure) - ); - - request->getSubField("scheme")->put("pva"); - if (!authority.empty()) request->getSubField("authority")->put(authority); - request->getSubField("path")->put(service); - PVStructure::shared_pointer query = request->getSubField("query"); - for (vector< pair >::iterator iter = parameters.begin(); - iter != parameters.end(); - iter++) - { - query->getSubField(iter->first)->put(iter->second); - } - - - PVStructure::shared_pointer arg = onlyQuery ? query : request; - if (debug) - { - std::cout << "Request structure: " << std::endl << *(arg.get()) << std::endl; - } - - - ChannelProvider::shared_pointer provider = ChannelProviderRegistry::clients()->getProvider("pva"); - assert(provider); - - Channel::shared_pointer channel = - provider->createChannel(service, DefaultChannelRequester::build(), - ChannelProvider::PRIORITY_DEFAULT, authority); - - TR1::shared_ptr rpcRequesterImpl(new ChannelRPCRequesterImpl(channel->getChannelName())); - ChannelRPC::shared_pointer channelRPC = channel->createChannelRPC(rpcRequesterImpl, pvRequest); - - if (rpcRequesterImpl->waitUntilConnected(timeOut)) - { - channelRPC->lastRequest(); - channelRPC->request(arg); - allOK &= rpcRequesterImpl->waitUntilRPC(timeOut); - if (allOK) - { - if (dumpStructure) - { - if (rpcRequesterImpl->getLastResponse().get() == 0) - std::cout << "(null)" << std::endl; - else - { - //std::cout << *(rpcRequesterImpl->getLastResponse().get()) << std::endl; - pvutil_ostream myos(std::cout); - myos << *(rpcRequesterImpl->getLastResponse().get()) << std::endl; - } - } - else - formatNT(std::cout, rpcRequesterImpl->getLastResponse()); - std::cout << std::endl; - } - } - else - { - allOK = false; - } - - channel->destroy(); - } - - if (cleanupAndReport) - { - // TODO implement wait on context - epicsThreadSleep ( 3.0 ); - //std::cerr << "-----------------------------------------------------------------------" << std::endl; - //epicsExitCallAtExits(); - } - - return allOK ? 0 : 1; + return getMain(argc, argv); } diff --git a/pvtoolsSrc/pvcall.cpp b/pvtoolsSrc/pvcall.cpp new file mode 100644 index 0000000..9b3d012 --- /dev/null +++ b/pvtoolsSrc/pvcall.cpp @@ -0,0 +1,254 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ + +#include + +#include +#include + +#include +#include + +#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1) +# include +# define USE_JSON +#endif + +#include "pvutils.h" + +#ifndef EXECNAME +# define EXECNAME "pvcall" +#endif + +namespace { + +void callusage (void) +{ + fprintf (stderr, "\nUsage: " EXECNAME " [options] [=]...\n\n" + "\noptions:\n" + " -h: Help: Print this message\n" + " -V: Print version and exit\n" + " -s : legacy form of PV name\n" + " -a : legacy form of argument\n" + " -r : Request, specifies what fields to return and options, default is '%s'\n" + " -w : Wait time, specifies timeout, default is %f seconds for get, inf. for monitor\n" + " -p : Set default provider name, default is '%s'\n" + " -M : Output mode. default is 'nt'\n" + " -v: Show entire structure (implies Raw mode)\n" + " -d: Enable debug output\n" + " deprecated options:\n" + " -q, -t, -i, -n, -F: ignored\n" + " -f : errors\n" + "\nexample: " EXECNAME " pv:name:add lhs=1 rhs=2\n\n" + , request.c_str(), timeout, defaultProvider.c_str()); +} + +typedef std::pair arg_t; +typedef std::vector args_t; + +arg_t parseArg(const std::string& raw) { + size_t equal = raw.find_first_of('='); + if(equal==raw.npos) + throw std::runtime_error("Argument missing '='"); + + std::string sval(raw.substr(equal+1)); + + pvd::PVFieldPtr value; + if(sval.size()>=2 && sval[0]=='[' && sval[sval.size()-1]==']') { + pvd::shared_vector sarr; + + jarray(sarr, sval.c_str()); + + pvd::PVStringArrayPtr V(pvd::getPVDataCreate()->createPVScalarArray()); + V->replace(pvd::freeze(sarr)); + + } else if(sval.size()>=2 && sval[0]=='{' && sval[sval.size()-1]=='}') { +#ifdef USE_JSON + std::istringstream strm(sval); + + value = pvd::parseJSON(strm); +#else + throw std::runtime_error("Not built with JSON support"); +#endif + + } else { + pvd::PVStringPtr V(pvd::getPVDataCreate()->createPVScalar()); + V->put(sval); + value = V; + } + + return std::make_pair(raw.substr(0, equal), value); +} + +} //namespace + +#ifndef MAIN +# define MAIN main +#endif + +int MAIN (int argc, char *argv[]) +{ + int opt; /* getopt() current option */ + std::string pv; + + args_t args; + + while ((opt = getopt(argc, argv, ":hvVM:r:w:p:ds:a:")) != -1) { + switch (opt) { + case 'h': /* Print usage */ + callusage(); + return 0; + case 'v': + verbosity++; + break; + case 'V': /* Print version */ + { + pva::Version version(EXECNAME, "cpp", + EPICS_PVA_MAJOR_VERSION, + EPICS_PVA_MINOR_VERSION, + EPICS_PVA_MAINTENANCE_VERSION, + EPICS_PVA_DEVELOPMENT_FLAG); + fprintf(stdout, "%s\n", version.getVersionString().c_str()); + return 0; + } + break; + case 'M': + if(strcmp(optarg, "raw")==0) { + outmode = pvd::PVStructure::Formatter::Raw; + } else if(strcmp(optarg, "nt")==0) { + outmode = pvd::PVStructure::Formatter::NT; + } else if(strcmp(optarg, "json")==0) { + outmode = pvd::PVStructure::Formatter::JSON; + } else { + fprintf(stderr, "Unknown output mode '%s'\n", optarg); + outmode = pvd::PVStructure::Formatter::Raw; + } + break; + case 'w': /* Set PVA timeout value */ + { + double temp; + if((epicsScanDouble(optarg, &temp)) != 1) + { + fprintf(stderr, "'%s' is not a valid timeout value " + "- ignored. ('" EXECNAME " -h' for help.)\n", optarg); + } else { + timeout = temp; + } + } + break; + case 'r': /* Set PVA timeout value */ + request = optarg; + break; + case 'p': /* Set default provider */ + defaultProvider = optarg; + break; + case 'd': /* Debug log level */ + debugFlag = true; + break; + case 's': + pv = optarg; + break; + case 'a': + try { + args.push_back(parseArg(optarg)); + } catch(std::exception& e){ + std::cerr<<"Error parsing argument '"<createFieldBuilder()); + builder = builder->setId("epics:nt/NTURI:1.0") + ->add("scheme", pvd::pvString) + ->add("authority", pvd::pvString) + ->add("path", pvd::pvString) + ->addNestedStructure("query"); + + for(args_t::const_iterator it(args.begin()), end(args.end()); it!=end; ++it) { + builder = builder->add(it->first, it->second->getField()); + } + + pvd::StructureConstPtr type(builder->endNested() + ->createStructure()); + argument = pvd::getPVDataCreate()->createPVStructure(type); + + argument->getSubFieldT("scheme")->put(defaultProvider); + argument->getSubFieldT("path")->put(pv); + pvd::PVStructurePtr query(argument->getSubFieldT("query")); + + for(args_t::const_iterator it(args.begin()), end(args.end()); it!=end; ++it) { + query->getSubFieldT(it->first)->copy(*it->second); + } + } + + if(verbosity>=1) + std::cout<<"# Argument\n"<stream().format(outmode); + + pvac::ClientProvider prov(defaultProvider); + + pvac::ClientChannel chan(prov.connect(pv)); + + pvd::PVStructure::const_shared_pointer ret; + try { + ret = chan.rpc(timeout, argument, pvRequest); + }catch(pvac::Timeout&){ + std::cerr<<"Timeout\n"; + return 1; + }catch(std::exception& e) { + std::cerr<<"Error: "<=1) + std::cout<<"# Result\n"; + std::cout<stream().format(outmode); + + return 0; +} diff --git a/pvtoolsSrc/pvget.cpp b/pvtoolsSrc/pvget.cpp index 028079c..71ef543 100644 --- a/pvtoolsSrc/pvget.cpp +++ b/pvtoolsSrc/pvget.cpp @@ -1,6 +1,11 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ #include #include #include +#include #include #include #include @@ -8,476 +13,347 @@ #include -#if !defined(_WIN32) -#include -#define USE_SIGNAL -#endif - #include #include #include #include -#include -#include -#include +#include #include #include #include +#include #include -#include "pvutils.cpp" +#include +#include +#include -using namespace std; -namespace TR1 = std::tr1; -using namespace epics::pvData; -using namespace epics::pvAccess; +#include "pvutils.h" - - -typedef epicsGuard Guard; -typedef epicsGuardRelease UnGuard; +#ifndef EXECNAME +# define EXECNAME "pvget" +#endif namespace { -bool debugFlag = false; +size_t pvnamewidth; -string request("field()"); -string defaultProvider("pva"); - -enum PrintMode { ValueOnlyMode, StructureMode, TerseMode }; -PrintMode mode = ValueOnlyMode; +int haderror; void usage (void) { - fprintf (stderr, "\nUsage: pvget [options] ...\n\n" + fprintf (stderr, "\nUsage: " EXECNAME " [options] ...\n\n" "\noptions:\n" " -h: Help: Print this message\n" " -V: Print version and exit\n" " -r : Request, specifies what fields to return and options, default is '%s'\n" - " -w : Wait time, specifies timeout, default is 3 seconds for get, inf. for monitor\n" - " -t: Terse mode - print only value, without names\n" - " -i: Do not format standard types (enum_t, time_t, ...)\n" + " -w : Wait time, specifies timeout, default is %f seconds for get, inf. for monitor\n" " -m: Monitor mode\n" " -p : Set default provider name, default is '%s'\n" - " -v: Show entire structure\n" - " -q: Quiet mode, print only error messages\n" + " -M : Output mode. default is 'nt'\n" + " -v: Show entire structure (implies Raw mode)\n" " -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" - " -n: Force enum interpretation of values as numbers (default is enum string)\n" -// " time format:\n" -// " -u: print userTag\n" - "\nexample: pvget double01\n\n" - , request.c_str(), defaultProvider.c_str()); + " deprecated options:\n" + " -q, -t, -i, -n, -F: ignored\n" + " -f : errors\n" + "\nexample: " EXECNAME " double01\n\n" + , request.c_str(), timeout, defaultProvider.c_str()); } -void printValue(std::string const & channelName, PVStructure::shared_pointer const & pv) +struct Getter : public pvac::ClientChannel::GetCallback, public Tracker { - if (mode == ValueOnlyMode) + POINTER_DEFINITIONS(Getter); + + pvac::Operation op; + + Getter(pvac::ClientChannel& channel, const pvd::PVStructurePtr& pvRequest) { - PVField::shared_pointer value = pv->getSubField("value"); - if (value.get() == 0) - { - //std::cerr << "no 'value' field\n"; - pvutil_ostream myos(std::cout); - myos << channelName << "\n" << *(pv.get()) << "\n\n"; - } - else - { - Type valueType = value->getField()->getType(); - if (valueType != scalar && valueType != scalarArray) - { - // switch to structure mode, unless it's T-type - if (valueType == structure && isTType(TR1::static_pointer_cast(value))) - { - std::cout << std::setw(30) << std::left << channelName; - std::cout << fieldSeparator; - formatTType(std::cout, TR1::static_pointer_cast(value)); - std::cout << '\n'; - } - else - { - pvutil_ostream myos(std::cout); - myos << channelName << '\n' << *(pv.get()) << "\n\n"; - } - } + op = channel.get(this, pvRequest); + } + virtual ~Getter() {} + + virtual void getDone(const pvac::GetEvent& event) OVERRIDE FINAL + { + std::cout<stream() + .format(outmode)); + + if(verbosity>=2) + fmt.highlight(*event.valid); // show all, highlight valid else - { - if (fieldSeparator == ' ' && value->getField()->getType() == scalar) - std::cout << std::setw(30) << std::left << channelName; - else - std::cout << channelName; + fmt.show(*event.valid); // only show valid, highlight none - std::cout << fieldSeparator; - - terse(std::cout, value) << '\n'; - } + std::cout< inprog_t; - static inprog_t inprog; - static bool abort; - - Tracker() - { - Guard G(doneLock); - inprog.insert(this); - } - ~Tracker() - { - done(); - } - void done() - { - { - Guard G(doneLock); - inprog.erase(this); - } - doneEvt.signal(); - } -}; - -epicsMutex Tracker::doneLock; -epicsEvent Tracker::doneEvt; -Tracker::inprog_t Tracker::inprog; -bool Tracker::abort = false; - -#ifdef USE_SIGNAL -void alldone(int num) -{ - (void)num; - Tracker::abort = true; - Tracker::doneEvt.signal(); -} -#endif - -struct ChannelGetRequesterImpl : public ChannelGetRequester, public Tracker -{ - const string m_channelName; - operation_type::shared_pointer op; - - ChannelGetRequesterImpl(std::string channelName) : m_channelName(channelName) {} - virtual ~ChannelGetRequesterImpl() {} - - virtual string getRequesterName() { return "ChannelGetRequesterImpl"; } - - virtual void channelGetConnect(const epics::pvData::Status& status, ChannelGet::shared_pointer const & channelGet, - epics::pvData::Structure::const_shared_pointer const & /*structure*/) - { - if (status.isSuccess()) - { - if (!status.isOK() || debugFlag) - { - std::cerr << "[" << m_channelName << "] channel get create: " << status << '\n'; - } - - channelGet->lastRequest(); - channelGet->get(); - } - else - { - std::cerr << "[" << m_channelName << "] failed to create channel get: " << status << '\n'; - done(); - } - } - - virtual void getDone(const epics::pvData::Status& status, - ChannelGet::shared_pointer const & /*channelGet*/, - epics::pvData::PVStructure::shared_pointer const & pvStructure, - epics::pvData::BitSet::shared_pointer const & bitSet) - { - if (status.isSuccess()) - { - if (!status.isOK() || debugFlag) - { - std::cerr << "[" << m_channelName << "] channel get: " << status << '\n'; - } - - printValue(m_channelName, pvStructure); - - } - else - { - std::cerr << "[" << m_channelName << "] failed to get: " << status << '\n'; + break; } std::cout.flush(); - done(); } - }; -struct MonitorRequesterImpl : public MonitorRequester, public Tracker + + +struct Worker { + virtual ~Worker() {} + virtual void process(const pvac::MonitorEvent& event) =0; +}; + +// simple work queue with thread. +// moves monitor queue handling off of PVA thread(s) +struct WorkQueue : public epicsThreadRunable { + epicsMutex mutex; + typedef std::tr1::shared_ptr value_type; + typedef std::tr1::weak_ptr weak_type; + // work queue holds only weak_ptr + // so jobs must be kept alive seperately + typedef std::deque > queue_t; + queue_t queue; + epicsEvent event; + bool running; + pvd::Thread worker; + + WorkQueue() + :running(true) + ,worker(pvd::Thread::Config() + .name("Monitor handler") + .autostart(true) + .run(this)) + {} + ~WorkQueue() {close();} + + void close() + { + { + Guard G(mutex); + running = false; + } + event.signal(); + worker.exitWait(); + } + + void push(const weak_type& cb, const pvac::MonitorEvent& evt) + { + bool wake; + { + Guard G(mutex); + if(!running) return; // silently refuse to queue during/after close() + wake = queue.empty(); + queue.push_back(std::make_pair(cb, evt)); + } + if(wake) + event.signal(); + } + + virtual void run() OVERRIDE FINAL + { + Guard G(mutex); + + while(running) { + if(queue.empty()) { + UnGuard U(G); + event.wait(); + } else { + queue_t::value_type ent(queue.front()); + value_type cb(ent.first.lock()); + queue.pop_front(); + if(!cb) continue; + + try { + UnGuard U(G); + cb->process(ent.second); + }catch(std::exception& e){ + std::cout<<"Error in monitor handler : "< { + POINTER_DEFINITIONS(MonTracker); - const string m_channelName; - operation_type::shared_pointer op; + MonTracker(WorkQueue& monwork, pvac::ClientChannel& channel, const pvd::PVStructurePtr& pvRequest) + :monwork(monwork) + ,mon(channel.monitor(this, pvRequest)) + {} + virtual ~MonTracker() {mon.cancel();} - MonitorRequesterImpl(std::string channelName) : m_channelName(channelName) {} - virtual ~MonitorRequesterImpl() {} + WorkQueue& monwork; - virtual string getRequesterName() + pvd::BitSet valid; // only access for process() + + pvac::Monitor mon; // must be last data member + + virtual void monitorEvent(const pvac::MonitorEvent& evt) OVERRIDE FINAL { - return "MonitorRequesterImpl"; + // shared_from_this() will fail as Cancel is delivered in our dtor. + if(evt.event==pvac::MonitorEvent::Cancel) return; + + // running on internal provider worker thread + // minimize work here. + monwork.push(shared_from_this(), evt); } - virtual void monitorConnect(const epics::pvData::Status& status, Monitor::shared_pointer const & monitor, StructureConstPtr const & /*structure*/) + virtual void process(const pvac::MonitorEvent& evt) OVERRIDE FINAL { - if (status.isSuccess()) - { - Status startStatus = monitor->start(); - // show error - // TODO and exit - if (!startStatus.isSuccess() || debugFlag) - { - std::cerr << "[" << m_channelName << "] channel monitor start: " << startStatus << '\n'; - } - - } - else - { - std::cerr << "monitorConnect(" << status << ")\n"; + // running on our worker thread + switch(evt.event) { + case pvac::MonitorEvent::Fail: + std::cerr<\n"; + valid.clear(); + break; + case pvac::MonitorEvent::Data: { - MonitorElement* element(it.get()); + unsigned n; + for(n=0; n<2 && mon.poll(); n++) { + valid |= mon.changed; - if (mode == ValueOnlyMode) - { - PVField::shared_pointer value = element->pvStructurePtr->getSubField("value"); - if (value.get() == 0) - { - std::cerr << "no 'value' field" << '\n'; - std::cout << m_channelName << '\n'; - pvutil_ostream myos(std::cout); - myos << *(element->pvStructurePtr.get()) << "\n\n"; - } + pvd::PVStructure::Formatter fmt(mon.root->stream() + .format(outmode)); + + if(verbosity>=3) + fmt.highlight(mon.changed); // show all + else if(verbosity>=2) + fmt.highlight(mon.changed).show(valid); else - { - Type valueType = value->getField()->getType(); - if (valueType != scalar && valueType != scalarArray) - { - // switch to structure mode, unless it's T-type - if (valueType == structure && isTType(TR1::static_pointer_cast(value))) - { - std::cout << std::setw(30) << std::left << m_channelName; - std::cout << fieldSeparator; - formatTType(std::cout, TR1::static_pointer_cast(value)); - std::cout << '\n'; - } - else - { - std::cout << m_channelName << '\n'; - pvutil_ostream myos(std::cout); - myos << *(element->pvStructurePtr.get()) << "\n\n"; - } - } - else - { - printMonitoredValue (value, element); - } - } - } - else if (mode == TerseMode) - { - if (fieldSeparator == ' ') - std::cout << std::setw(30) << std::left << m_channelName; - else - std::cout << m_channelName; + fmt.show(mon.changed); // highlight none - std::cout << fieldSeparator; - - terseStructure(std::cout, element->pvStructurePtr) << '\n'; + std::cout<pvStructurePtr.get()) << "\n\n"; + if(n==2) { + // too many updates, re-queue to balance with others + monwork.push(shared_from_this(), evt); + } else if(n==0) { + LOG(pva::logLevelDebug, "%s Spurious Data event on channel", mon.name().c_str()); + } else { + if(mon.complete()) + done(); } - - std::cout.flush(); } - - } - - virtual void unlisten(Monitor::shared_pointer const & /*monitor*/) - { - if(debugFlag) - std::cerr << "unlisten" << m_channelName << '\n'; - done(); - } - -private: - // For value type scalar or scalarArray when mode is ValueOnlyMode - void printMonitoredValue (PVField::shared_pointer value, MonitorElement* element) - { - PVStructure::shared_pointer timeStamp(element->pvStructurePtr->getSubField("timeStamp")); - PVStructure::shared_pointer alarm(element->pvStructurePtr->getSubField("alarm")); - - if (fieldSeparator == ' ' && value->getField()->getType() == scalar) - std::cout << std::setw(30) << std::left; - - std::cout << m_channelName << fieldSeparator; - - if (timeStamp) - terseStructure(std::cout, timeStamp) << " "; - - terse(std::cout, value) << " "; - - if (alarm) - { - PVScalar::shared_pointer pvSeverity(alarm->getSubField("severity")); - - bool inAlarm = !pvSeverity ? false : (pvSeverity->getAs()!=0); - if (inAlarm) - terseStructure(std::cout, alarm); + break; } - - std::cout<< '\n'; + std::cout.flush(); } }; } // namespace +#ifndef MAIN +# define MAIN main +#endif -int main (int argc, char *argv[]) +int MAIN (int argc, char *argv[]) { int opt; /* getopt() current option */ +#ifdef PVMONITOR + bool monitor = true; +#else bool monitor = false; +#endif - istream* inputStream = 0; - ifstream ifs; - bool fromStream = false; epics::RefMonitor refmon; - double timeOut = -1.0; - bool explicit_timeout = false; - - setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ - // ================ Parse Arguments - while ((opt = getopt(argc, argv, ":hvVRr:w:tmp:qdcF:f:ni")) != -1) { + while ((opt = getopt(argc, argv, ":hvVRM:r:w:tmp:qdcF:f:ni")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(); return 0; case 'v': - mode = StructureMode; + verbosity++; break; case 'V': /* Print version */ { - Version version("pvget", "cpp", - EPICS_PVA_MAJOR_VERSION, - EPICS_PVA_MINOR_VERSION, - EPICS_PVA_MAINTENANCE_VERSION, - EPICS_PVA_DEVELOPMENT_FLAG); + pva::Version version(EXECNAME, "cpp", + EPICS_PVA_MAJOR_VERSION, + EPICS_PVA_MINOR_VERSION, + EPICS_PVA_MAINTENANCE_VERSION, + EPICS_PVA_DEVELOPMENT_FLAG); fprintf(stdout, "%s\n", version.getVersionString().c_str()); return 0; } case 'R': refmon.start(5.0); break; - case 'w': /* Set PVA timeout value */ - if((epicsScanDouble(optarg, &timeOut)) != 1 || timeOut <= 0.0) - { - fprintf(stderr, "'%s' is not a valid timeout value " - "- ignored. ('pvget -h' for help.)\n", optarg); + case 'M': + if(strcmp(optarg, "raw")==0) { + outmode = pvd::PVStructure::Formatter::Raw; + } else if(strcmp(optarg, "nt")==0) { + outmode = pvd::PVStructure::Formatter::NT; + } else if(strcmp(optarg, "json")==0) { + outmode = pvd::PVStructure::Formatter::JSON; } else { - explicit_timeout = true; + fprintf(stderr, "Unknown output mode '%s'\n", optarg); + outmode = pvd::PVStructure::Formatter::Raw; } break; + case 'w': /* Set PVA timeout value */ + { + double temp; + if((epicsScanDouble(optarg, &temp)) != 1) + { + fprintf(stderr, "'%s' is not a valid timeout value " + "- ignored. ('" EXECNAME " -h' for help.)\n", optarg); + } else { + timeout = temp; + } + } + break; case 'r': /* Set PVA timeout value */ - request = optarg; - // do not override terse mode - if (mode == ValueOnlyMode) mode = StructureMode; - break; + request = optarg; + break; case 't': /* Terse mode */ - mode = TerseMode; - break; case 'i': /* T-types format mode */ - formatTTypesFlag = false; + case 'F': /* Store this for output formatting */ + case 'n': + case 'q': /* Quiet mode */ + // deprecate break; + case 'f': /* Use input stream as input */ + fprintf(stderr, "Unsupported option -f\n"); + return 1; case 'm': /* Monitor mode */ monitor = true; break; case 'p': /* Set default provider */ defaultProvider = optarg; break; - case 'q': /* Quiet mode */ - break; case 'd': /* Debug log level */ debugFlag = true; break; case 'c': /* Clean-up and report used instance count */ break; - case 'F': /* Store this for output formatting */ - fieldSeparator = (char) *optarg; - break; - case 'f': /* Use input stream as input */ - { - string fileName = optarg; - if (fileName == "-") - inputStream = &cin; - else - { - ifs.open(fileName.c_str(), ifstream::in); - if (!ifs) - { - fprintf(stderr, - "Failed to open file '%s'.\n", - fileName.c_str()); - return 1; - } - else - inputStream = &ifs; - } - - fromStream = true; - break; - } - case 'n': - enumMode = NumberEnum; - break; case '?': fprintf(stderr, - "Unrecognized option: '-%c'. ('pvget -h' for help.)\n", + "Unrecognized option: '-%c'. ('" EXECNAME " -h' for help.)\n", optopt); return 1; case ':': fprintf(stderr, - "Option '-%c' requires an argument. ('pvget -h' for help.)\n", + "Option '-%c' requires an argument. ('" EXECNAME " -h' for help.)\n", optopt); return 1; default : @@ -486,162 +362,76 @@ int main (int argc, char *argv[]) } } - if(!explicit_timeout) { - if(monitor) - timeOut = -1.0; // forever - else - timeOut = 3.0; - } + if(monitor) + timeout = -1; - int nPvs = argc - optind; /* Remaining arg list are PV names */ - if (nPvs > 0) - { - // do not allow reading file and command line specified pvs - fromStream = false; - } - else if (nPvs < 1 && !fromStream) - { - fprintf(stderr, "No pv name(s) specified. ('pvget -h' for help.)\n"); - return 1; - } + if(verbosity>0) + outmode = pvd::PVStructure::Formatter::Raw; - vector pvs; /* Array of PV structures */ - if (fromStream) - { - string cn; - while (true) - { - *inputStream >> cn; - if (!(*inputStream)) - break; - pvs.push_back(cn); - } - - // set nPvs - nPvs = pvs.size(); - } - else - { - // copy PV names from command line - for (int n = 0; optind < argc; n++, optind++) - pvs.push_back(argv[optind]); - } - - - SET_LOG_LEVEL(debugFlag ? logLevelDebug : logLevelError); - - std::cout << std::boolalpha; - - // ================ Connect channels and start operations - - epics::pvAccess::ca::CAClientFactory::start(); - - bool allOK = true; - - std::set providers; - typedef std::map chan_cache_t; - chan_cache_t chan_cache; - - PVStructure::shared_pointer pvRequest; + pvd::PVStructure::shared_pointer pvRequest; try { - pvRequest = createRequest(request); + pvRequest = pvd::createRequest(request); } catch(std::exception& e){ fprintf(stderr, "failed to parse request string: %s\n", e.what()); return 1; } - // keep the operations, and associated channels, alive - std::vector > ops; - - for(size_t n=0; ngetProvider(uri.protocol)); - if(!provider) { - std::cerr<<"Unknown provider \""<createChannel(pvs[n], DefaultChannelRequester::build(), - ChannelProvider::PRIORITY_DEFAULT, uri.host); - } catch(std::exception& e) { - std::cerr<<"Provider "<second; - } - - if(monitor) { - std::tr1::shared_ptr req(new MonitorRequesterImpl(pvs[n])); - - req->op = channel->createMonitor(req, pvRequest); - - ops.push_back(req); - - } else { - std::tr1::shared_ptr req(new ChannelGetRequesterImpl(pvs[n])); - - req->op = channel->createChannelGet(req, pvRequest); - - ops.push_back(req); - } - - // make sure to keep the provider alive as Channels will be automatically closed - providers.insert(provider); + for(int i = optind; i < argc; i++) { + pvnamewidth = std::max(pvnamewidth, strlen(argv[i])); } - // Active channels continue to be referenced by get/monitor stored in 'ops' - chan_cache.clear(); + SET_LOG_LEVEL(debugFlag ? pva::logLevelDebug : pva::logLevelError); - // ========================== Wait for operations to complete, or timeout - -#ifdef USE_SIGNAL - signal(SIGINT, alldone); - signal(SIGTERM, alldone); - signal(SIGQUIT, alldone); -#endif - - if(debugFlag) - std::cerr<<"Waiting...\n"; + epics::pvAccess::ca::CAClientFactory::start(); { - Guard G(Tracker::doneLock); - while(Tracker::inprog.size() && !Tracker::abort) { - UnGuard U(G); - if(timeOut<=0) - Tracker::doneEvt.wait(); - else if(!Tracker::doneEvt.wait(timeOut)) { - allOK = false; - std::cerr<<"Timeout\n"; - break; + pvac::ClientProvider provider(defaultProvider); + + std::vector > tracked; + + epics::auto_ptr Q; + if(monitor) + Q.reset(new WorkQueue); + + for(int i = optind; i < argc; i++) { + pvac::ClientChannel chan(provider.connect(argv[i])); + + if(monitor) { + std::tr1::shared_ptr mon(new MonTracker(*Q, chan, pvRequest)); + + tracked.push_back(mon); + + } else { // Get + std::tr1::shared_ptr get(new Getter(chan, pvRequest)); + + tracked.push_back(get); + } + } + + // ========================== Wait for operations to complete, or timeout + + Tracker::prepare(); // install signal handler + + if(debugFlag) + std::cerr<<"Waiting...\n"; + + { + Guard G(Tracker::doneLock); + while(Tracker::inprog.size() && !Tracker::abort) { + UnGuard U(G); + if(timeout<=0) + Tracker::doneEvt.wait(); + else if(!Tracker::doneEvt.wait(timeout)) { + haderror = 1; + std::cerr<<"Timeout\n"; + break; + } } } } if(refmon.running()) { refmon.stop(); - // drop refs to operations, but keep ref to ClientProvider - ops.clear(); // show final counts refmon.current(); } @@ -650,5 +440,6 @@ int main (int argc, char *argv[]) if(debugFlag) std::cerr<<"Done\n"; - return allOK ? 0 : 1; + + return haderror ? 1 : 0; } diff --git a/pvtoolsSrc/pvinfo.cpp b/pvtoolsSrc/pvinfo.cpp index 2fb5afa..c9dc54d 100644 --- a/pvtoolsSrc/pvinfo.cpp +++ b/pvtoolsSrc/pvinfo.cpp @@ -1,5 +1,9 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ #include -#include +#include #include #include @@ -16,20 +20,12 @@ #include #include -#include "pvutils.cpp" +#include "pvutils.h" -using namespace std; -namespace TR1 = std::tr1; -using namespace epics::pvData; -using namespace epics::pvAccess; +namespace pvd = epics::pvData; +namespace pva = epics::pvAccess; - -#define DEFAULT_TIMEOUT 3.0 -#define DEFAULT_PROVIDER "pva" - -double timeOut = DEFAULT_TIMEOUT; -string defaultProvider(DEFAULT_PROVIDER); -const string noAddress; +namespace { void usage (void) { @@ -42,31 +38,37 @@ void usage (void) " -d: Enable debug output\n" " -c: Wait for clean shutdown and report used instance count (for expert users)" "\nExample: pvinfo double01\n\n" - , DEFAULT_TIMEOUT, DEFAULT_PROVIDER); + , timeout, defaultProvider.c_str()); } +int haderror; + +struct GetInfo : public pvac::ClientChannel::InfoCallback, + public Tracker +{ + pvac::Operation op; + virtual void infoDone(const pvac::InfoEvent& evt) { + switch(evt.event) { + case pvac::InfoEvent::Cancel: break; + case pvac::InfoEvent::Fail: + std::cerr<... - * - * Arg(s) Out: none - * - * Return(s): Standard return code (0=success, 1=error) - * - **************************************************************************-*/ int main (int argc, char *argv[]) { int opt; /* getopt() current option */ bool debug = false; - bool cleanupAndReport = false; setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ @@ -77,7 +79,7 @@ int main (int argc, char *argv[]) return 0; case 'V': /* Print version */ { - Version version("pvinfo", "cpp", + pva::Version version("pvinfo", "cpp", EPICS_PVA_MAJOR_VERSION, EPICS_PVA_MINOR_VERSION, EPICS_PVA_MAINTENANCE_VERSION, @@ -86,12 +88,16 @@ int main (int argc, char *argv[]) return 0; } case 'w': /* Set PVA timeout value */ - if((epicsScanDouble(optarg, &timeOut)) != 1 || timeOut <= 0.0) + { + double temp; + if((epicsScanDouble(optarg, &temp)) != 1 || timeout <= 0.0) { fprintf(stderr, "'%s' is not a valid timeout value " "- ignored. ('pvget -h' for help.)\n", optarg); - timeOut = DEFAULT_TIMEOUT; + } else { + timeout = temp; } + } break; case 'p': /* Set default provider */ defaultProvider = optarg; @@ -100,7 +106,6 @@ int main (int argc, char *argv[]) debug = true; break; case 'c': /* Clean-up and report used instance count */ - cleanupAndReport = true; break; case '?': fprintf(stderr, @@ -118,114 +123,41 @@ int main (int argc, char *argv[]) } } - int nPvs = argc - optind; /* Remaining arg list are PV names */ - if (nPvs < 1) + if (argc == optind) { fprintf(stderr, "No pv name(s) specified. ('pvinfo -h' for help.)\n"); return 1; } - vector pvs; /* Array of PV names */ - for (int n = 0; optind < argc; n++, optind++) - pvs.push_back(argv[optind]); /* Copy PV names from command line */ + SET_LOG_LEVEL(debug ? pva::logLevelDebug : pva::logLevelError); + std::vector infos(argc - optind); - SET_LOG_LEVEL(debug ? logLevelDebug : logLevelError); + pva::ca::CAClientFactory::start(); - std::cout << std::boolalpha; - - bool allOK = true; - - epics::pvAccess::ca::CAClientFactory::start(); - - { - std::vector pvNames; - std::vector pvAddresses; - std::vector providers; - - pvNames.reserve(nPvs); - pvAddresses.reserve(nPvs); - - for (int n = 0; n < nPvs; n++) { - URI uri; - bool validURI = URI::parse(pvs[n], uri); + pvac::ClientProvider prov(defaultProvider); - std::string providerName(defaultProvider); - std::string pvName(pvs[n]); - std::string address(noAddress); - if (validURI) - { - if (uri.path.length() <= 1) - { - std::cerr << "invalid URI '" << pvs[n] << "', empty path" << std::endl; - return 1; - } - providerName = uri.protocol; - pvName = uri.path.substr(1); - address = uri.host; - } - - pvNames.push_back(pvName); - pvAddresses.push_back(address); - providers.push_back(ChannelProviderRegistry::clients()->getProvider(providerName)); - if(!providers.back()) - { - std::cerr << "unknown provider name '" << providerName - << "', only 'pva' and 'ca' are supported" << std::endl; - allOK = false; - } + for(int i = optind; i channels(nPvs); - for (int n = 0; n < nPvs; n++) + Tracker::prepare(); // install signal handler + { - if(!providers[n]) continue; - channels[n] = providers[n]->createChannel(pvNames[n], DefaultChannelRequester::build(), - ChannelProvider::PRIORITY_DEFAULT, pvAddresses[n]); - } - - // for now a simple iterating sync implementation, guarantees order - for (int n = 0; n < nPvs; n++) - { - Channel::shared_pointer channel = channels[n]; - if(!channel) continue; - - TR1::shared_ptr getFieldRequesterImpl(new GetFieldRequesterImpl(channel)); - channel->getField(getFieldRequesterImpl, ""); - - if (getFieldRequesterImpl->waitUntilFieldGet(timeOut)) - { - Structure::const_shared_pointer structure = - TR1::dynamic_pointer_cast(getFieldRequesterImpl->getField()); - - channel->printInfo(); - if (structure) - { - std::cout << *structure << std::endl << std::endl; - } - else - { - std::cout << "(null introspection data)" << std::endl << std::endl; + Guard G(Tracker::doneLock); + while(Tracker::inprog.size() && !Tracker::abort) { + UnGuard U(G); + if(timeout<=0) + Tracker::doneEvt.wait(); + else if(!Tracker::doneEvt.wait(timeout)) { + haderror = 1; + std::cerr<<"Timeout\n"; + break; } } - else - { - allOK = false; - std::cerr << "[" << channel->getChannelName() << "] failed to get channel introspection data" << std::endl; - } } - } - if (cleanupAndReport) - { - // TODO implement wait on context - epicsThreadSleep ( 3.0 ); - //std::cout << "-----------------------------------------------------------------------" << std::endl; - //epicsExitCallAtExits(); - } - - return allOK ? 0 : 1; + return haderror ? 1 : 0; } diff --git a/pvtoolsSrc/pvlist.cpp b/pvtoolsSrc/pvlist.cpp index 3c44441..5f41402 100644 --- a/pvtoolsSrc/pvlist.cpp +++ b/pvtoolsSrc/pvlist.cpp @@ -1,3 +1,7 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ #include diff --git a/pvtoolsSrc/pvmonitor.cpp b/pvtoolsSrc/pvmonitor.cpp new file mode 100644 index 0000000..c3d2bc5 --- /dev/null +++ b/pvtoolsSrc/pvmonitor.cpp @@ -0,0 +1,3 @@ +#define PVMONITOR +#define EXECNAME "pvmonitor" +#include "pvget.cpp" diff --git a/pvtoolsSrc/pvput.cpp b/pvtoolsSrc/pvput.cpp index 3635e76..9c876b1 100644 --- a/pvtoolsSrc/pvput.cpp +++ b/pvtoolsSrc/pvput.cpp @@ -1,3 +1,7 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ #include #include #include @@ -29,30 +33,10 @@ #include -#include "pvutils.cpp" - -using namespace std; -namespace TR1 = std::tr1; -using namespace epics::pvData; -using namespace epics::pvAccess; +#include "pvutils.h" namespace { - -#define DEFAULT_TIMEOUT 3.0 -#define DEFAULT_REQUEST "field(value)" -#define DEFAULT_PROVIDER "pva" - -double timeOut = DEFAULT_TIMEOUT; -string request(DEFAULT_REQUEST); -string defaultProvider(DEFAULT_PROVIDER); -const string noAddress; - -enum PrintMode { ValueOnlyMode, StructureMode, TerseMode }; -PrintMode mode = ValueOnlyMode; - -bool debug = false; - void usage (bool details=false) { fprintf (stderr, @@ -71,18 +55,16 @@ void usage (bool details=false) " -v: Print version and exit\n" " -r : Request, specifies what fields to return and options, default is '%s'\n" " -w : Wait time, specifies timeout, default is %f second(s)\n" - " -t: Terse mode - print only successfully written value, without names\n" " -p : Set default provider name, default is '%s'\n" - " -v: Show entire structure\n" + " -M : Output mode. default is 'nt'\n" + " -v: Show entire structure (implies Raw mode)\n" " -q: Quiet mode, print only error messages\n" " -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" + " Deprecated options:\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" - , DEFAULT_REQUEST, DEFAULT_TIMEOUT, DEFAULT_PROVIDER); + " -n, -s, -F, -t: ignored\n" + " -f : error" + , request.c_str(), timeout, defaultProvider.c_str()); if(details) { fprintf (stderr, #ifdef USE_JSON @@ -119,136 +101,12 @@ void usage (bool details=false) } } -void printValue(std::string const & channelName, PVStructure::const_shared_pointer const & pv) +void printValue(std::string const & channelName, pvd::PVStructure::const_shared_pointer const & pv) { - if (mode == ValueOnlyMode) - { - PVField::const_shared_pointer value = pv->getSubField("value"); - if (value.get() == 0) - { - std::cerr << "no 'value' field" << std::endl; - std::cout << std::endl << *(pv.get()) << std::endl << std::endl; - } - else - { - Type valueType = value->getField()->getType(); - if (valueType != scalar && valueType != scalarArray) - { - // special case for enum - if (valueType == structure) - { - PVStructure::const_shared_pointer pvStructure = TR1::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; - } - else - { - if (fieldSeparator == ' ' && value->getField()->getType() == scalar) - std::cout << std::setw(30) << std::left << channelName; - else - std::cout << channelName; - - std::cout << fieldSeparator; - - terse(std::cout, value) << std::endl; - } - } - } - else if (mode == TerseMode) - terseStructure(std::cout, pv) << std::endl; - else - std::cout << std::endl << *(pv.get()) << std::endl << std::endl; + std::cout<stream().format(outmode); std::cout.flush(); } -void early(const char *inp, unsigned pos) -{ - fprintf(stderr, "Unexpected end of input: %s\n", inp); - throw std::runtime_error("Unexpected end of input"); -} - -// rudimentory parser for json array -// needed as long as Base < 3.15 is supported. -// for consistency, used with all version -void jarray(shared_vector& out, const char *inp) -{ - assert(inp[0]=='['); - const char * const orig = inp; - inp++; - - while(true) { - // starting a new token - - for(; *inp==' '; inp++) {} // skip leading whitespace - - if(*inp=='\0') early(inp, inp-orig); - - if(isalnum(*inp) || *inp=='+' || *inp=='-') { - // number - - const char *start = inp; - - while(isalnum(*inp) || *inp=='.' || *inp=='+' || *inp=='-') - inp++; - - if(*inp=='\0') early(inp, inp-orig); - - // inp points to first char after token - - out.push_back(std::string(start, inp-start)); - - } else if(*inp=='"') { - // quoted string - - const char *start = ++inp; // skip quote - - while(*inp!='\0' && *inp!='"') - inp++; - - if(*inp=='\0') early(inp, inp-orig); - - // inp points to trailing " - - out.push_back(std::string(start, inp-start)); - - inp++; // skip trailing " - - } else if(*inp==']') { - // no-op - } else { - fprintf(stderr, "Unknown token '%c' in \"%s\"", *inp, inp); - throw std::runtime_error("Unknown token"); - } - - for(; *inp==' '; inp++) {} // skip trailing whitespace - - if(*inp==',') inp++; - else if(*inp==']') break; - else { - fprintf(stderr, "Unknown token '%c' in \"%s\"", *inp, inp); - throw std::runtime_error("Unknown token"); - } - } - -} - struct Putter : public pvac::ClientChannel::PutCallback { epicsEvent wait; @@ -259,24 +117,22 @@ struct Putter : public pvac::ClientChannel::PutCallback Putter() :done(false) {} - typedef shared_vector bare_t; + typedef pvd::shared_vector bare_t; bare_t bare; typedef std::pair KV_t; typedef std::vector pairs_t; pairs_t pairs; - shared_vector jarr; - - PVStructure::const_shared_pointer current; + pvd::shared_vector jarr; virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) { - if(debug) std::cerr<<"Server defined structure\n"<createPVStructure(build)); + if(debugFlag) std::cerr<<"Server defined structure\n"<createPVStructure(build)); if(bare.size()==1 && bare[0][0]=='{') { - if(debug) fprintf(stderr, "In JSON top mode\n"); + if(debugFlag) fprintf(stderr, "In JSON top mode\n"); #ifdef USE_JSON std::istringstream strm(bare[0]); parseJSON(strm, root, &args.tosend); @@ -284,23 +140,23 @@ struct Putter : public pvac::ClientChannel::PutCallback #endif } else if(pairs.empty()) { - if(debug) fprintf(stderr, "In plain value mode\n"); + if(debugFlag) fprintf(stderr, "In plain value mode\n"); - PVFieldPtr fld(root->getSubField("value")); + pvd::PVFieldPtr fld(root->getSubField("value")); if(!fld) throw std::runtime_error("Structure has no .value"); - Type ftype = fld->getField()->getType(); + pvd::Type ftype = fld->getField()->getType(); - if(ftype==scalar) { + if(ftype==pvd::scalar) { if(bare.size()!=1) { throw std::runtime_error("Can't assign multiple values to scalar"); } - PVScalar* sfld(static_cast(fld.get())); + pvd::PVScalar* sfld(static_cast(fld.get())); sfld->putFrom(bare[0]); args.tosend.set(sfld->getFieldOffset()); - } else if(ftype==scalarArray) { - PVScalarArray* sfld(static_cast(fld.get())); + } else if(ftype==pvd::scalarArray) { + pvd::PVScalarArray* sfld(static_cast(fld.get())); // first element is "length" which we ignore for compatibility bare.slice(1); @@ -308,19 +164,20 @@ struct Putter : public pvac::ClientChannel::PutCallback sfld->putFrom(freeze(bare)); args.tosend.set(sfld->getFieldOffset()); - } else if(ftype==structure && fld->getField()->getID()=="enum_t") { + } else if(ftype==pvd::structure && fld->getField()->getID()=="enum_t") { if(bare.size()!=1) { throw std::runtime_error("Can't assign multiple values to enum"); } - PVStructure* sfld(static_cast(fld.get())); + pvd::PVStructure* sfld(static_cast(fld.get())); - PVScalar* idxfld(sfld->getSubFieldT("index").get()); - PVStringArray::const_svector choices(current->getSubFieldT("value.choices")->view()); + assert(!!args.previous); // ensure by calling put(..., true) below + pvd::PVScalar* idxfld(sfld->getSubFieldT("index").get()); + pvd::PVStringArray::const_svector choices(args.previous->getSubFieldT("value.choices")->view()); bool found=false; for(size_t i=0; iputFrom(i); + idxfld->putFrom(i); found=true; break; } @@ -337,20 +194,20 @@ struct Putter : public pvac::ClientChannel::PutCallback } } else { - if(debug) fprintf(stderr, "In field=value mode\n"); + if(debugFlag) fprintf(stderr, "In field=value mode\n"); for(pairs_t::const_iterator it=pairs.begin(), end=pairs.end(); it!=end; ++it) { - PVFieldPtr fld(root->getSubField(it->first)); + pvd::PVFieldPtr fld(root->getSubField(it->first)); if(!fld) { fprintf(stderr, "%s : Warning: no such field\n", it->first.c_str()); // ignore } else if(it->second[0]=='[') { - shared_vector arr; + pvd::shared_vector arr; jarray(arr, it->second.c_str()); - PVScalarArray* afld(dynamic_cast(fld.get())); + pvd::PVScalarArray* afld(dynamic_cast(fld.get())); if(!afld) { fprintf(stderr, "%s : Error not a scalar array field\n", it->first.c_str()); throw std::runtime_error("Not a scalar array field"); @@ -366,7 +223,7 @@ struct Putter : public pvac::ClientChannel::PutCallback throw std::runtime_error("JSON support not built"); #endif } else { - PVScalarPtr sfld(std::tr1::dynamic_pointer_cast(fld)); + pvd::PVScalarPtr sfld(std::tr1::dynamic_pointer_cast(fld)); if(!sfld) { fprintf(stderr, "%s : Error: need a scalar field\n", it->first.c_str()); } else { @@ -378,7 +235,7 @@ struct Putter : public pvac::ClientChannel::PutCallback } args.root = root; - if(debug) + if(debugFlag) std::cout<<"To be sent: "<("POSIXLY_CORRECT=")); /* Behave correct on GNU getopt systems; e.g. handle negative numbers */ - while ((opt = getopt(argc, argv, ":hvVr:w:tp:qdF:f:ns")) != -1) { + while ((opt = getopt(argc, argv, ":hvVM:r:w:tp:qdF:f:ns")) != -1) { switch (opt) { case 'h': /* Print usage */ usage(true); return 0; case 'v': - mode = StructureMode; + outmode = pvd::PVStructure::Formatter::Raw; break; case 'V': /* Print version */ { - Version version("pvput", "cpp", + pva::Version version("pvput", "cpp", EPICS_PVA_MAJOR_VERSION, EPICS_PVA_MINOR_VERSION, EPICS_PVA_MAINTENANCE_VERSION, @@ -427,24 +280,38 @@ int main (int argc, char *argv[]) fprintf(stdout, "%s\n", version.getVersionString().c_str()); return 0; } + case 'M': + if(strcmp(optarg, "raw")==0) { + outmode = pvd::PVStructure::Formatter::Raw; + } else if(strcmp(optarg, "nt")==0) { + outmode = pvd::PVStructure::Formatter::NT; + } else if(strcmp(optarg, "json")==0) { + outmode = pvd::PVStructure::Formatter::JSON; + } else { + fprintf(stderr, "Unknown output mode '%s'\n", optarg); + outmode = pvd::PVStructure::Formatter::Raw; + } + break; case 'w': /* Set PVA timeout value */ - if((epicsScanDouble(optarg, &timeOut)) != 1 || timeOut <= 0.0) + { + double temp; + if((epicsScanDouble(optarg, &temp)) != 1) { fprintf(stderr, "'%s' is not a valid timeout value " "- ignored. ('pvput -h' for help.)\n", optarg); - timeOut = DEFAULT_TIMEOUT; + } else { + timeout = temp; } + } break; case 'r': /* Set PVA timeout value */ request = optarg; - // do not override terse mode - if (mode == ValueOnlyMode) mode = StructureMode; break; case 't': /* Terse mode */ - mode = TerseMode; + // deprecated break; case 'd': /* Debug log level */ - debug = true; + debugFlag = true; break; case 'p': /* Set default provider */ defaultProvider = optarg; @@ -453,35 +320,13 @@ int main (int argc, char *argv[]) quiet = true; break; case 'F': /* Store this for output formatting */ - fieldSeparator = (char) *optarg; break; case 'f': /* Use input stream as input */ - { - string fileName = optarg; - if (fileName == "-") - inputStream = &cin; - else - { - ifs.open(fileName.c_str(), ifstream::in); - if (!ifs) - { - fprintf(stderr, - "Failed to open file '%s'.\n", - fileName.c_str()); - return 1; - } - else - inputStream = &ifs; - } - - fromStream = true; - break; - } + fprintf(stderr, "Unsupported option -f\n"); + return 1; case 'n': - enumMode = NumberEnum; break; case 's': - enumMode = StringEnum; break; case '?': fprintf(stderr, @@ -504,62 +349,22 @@ int main (int argc, char *argv[]) fprintf(stderr, "No pv name specified. ('pvput -h' for help.)\n"); return 1; } - string pv = argv[optind++]; + std::string pv = argv[optind++]; - URI uri; - bool validURI = URI::parse(pv, uri); - - string providerName(defaultProvider); - string pvName(pv); - string address(noAddress); - if (validURI) - { - if (uri.path.length() <= 1) - { - std::cerr << "invalid URI '" << pv << "', empty path" << std::endl; - return 1; - } - providerName = uri.protocol; - pvName = uri.path.substr(1); - address = uri.host; - } + std::string providerName(defaultProvider); + std::string pvName(pv); int nVals = argc - optind; /* Remaining arg list are PV names */ - if (nVals > 0) - { - // do not allow reading file and command line specified pvs - fromStream = false; - } - else if (nVals < 1 && !fromStream) + if (nVals < 1) { fprintf(stderr, "No value(s) specified. ('pvput -h' for help.)\n"); return 1; } - vector values; - if (fromStream) - { - string cn; - while (true) - { - *inputStream >> cn; - if (!(*inputStream)) - break; - values.push_back(cn); - } - } - else - { - // copy values from command line - for (int n = 0; optind < argc; n++, optind++) - values.push_back(argv[optind]); - } - - if(values.empty()) { - usage(); - fprintf(stderr, "\nNo values provided\n"); - return 1; - } + std::vector values; + // copy values from command line + for (int n = 0; optind < argc; n++, optind++) + values.push_back(argv[optind]); Putter thework; @@ -597,15 +402,15 @@ int main (int argc, char *argv[]) thework.bare.clear(); } - PVStructure::shared_pointer pvRequest; + pvd::PVStructure::shared_pointer pvRequest; try { - pvRequest = createRequest(request); + pvRequest = pvd::createRequest(request); } catch(std::exception& e){ fprintf(stderr, "failed to parse request string: %s\n", e.what()); return 1; } - SET_LOG_LEVEL(debug ? logLevelDebug : logLevelError); + SET_LOG_LEVEL(debugFlag ? pva::logLevelDebug : pva::logLevelError); std::cout << std::boolalpha; @@ -615,20 +420,18 @@ int main (int argc, char *argv[]) pvac::ClientChannel chan(ctxt.connect(pvName)); - thework.current = chan.get(timeOut, pvRequest); - - if (mode != TerseMode && !quiet) { + if (!quiet) { std::cout << "Old : "; - printValue(pvName, thework.current); + printValue(pvName, chan.get(timeout, pvRequest)); } { - pvac::Operation op(chan.put(&thework, pvRequest)); + pvac::Operation op(chan.put(&thework, pvRequest, true)); epicsGuard G(thework.lock); while(!thework.done) { epicsGuardRelease U(G); - if(!thework.wait.wait(timeOut)) { + if(!thework.wait.wait(timeout)) { fprintf(stderr, "Put timeout\n"); return 1; } @@ -639,10 +442,10 @@ int main (int argc, char *argv[]) fprintf(stderr, "Error: %s\n", thework.message.c_str()); } - if (mode != TerseMode && !quiet) { + if (!quiet) { std::cout << "New : "; } - printValue(pvName, chan.get(timeOut, pvRequest)); + printValue(pvName, chan.get(timeout, pvRequest)); return thework.result!=pvac::PutEvent::Success; } diff --git a/pvtoolsSrc/pvutils.cpp b/pvtoolsSrc/pvutils.cpp index 28ba4cb..30a3577 100644 --- a/pvtoolsSrc/pvutils.cpp +++ b/pvtoolsSrc/pvutils.cpp @@ -1,4 +1,12 @@ -#include "pvutils.h" +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ +#include +#if !defined(_WIN32) +#include +#define USE_SIGNAL +#endif #include @@ -12,466 +20,109 @@ #include #include -using namespace std; -namespace TR1 = std::tr1; +#include "pvutils.h" -using namespace epics::pvData; -using namespace epics::pvAccess; +double timeout = 5.0; +bool debugFlag = false; -std::ostream& operator<<(std::ostream& o, const dump_stack_only_on_debug& d) +pvd::PVStructure::Formatter::format_t outmode = pvd::PVStructure::Formatter::NT; +int verbosity; + +std::string request(""); +std::string defaultProvider("pva"); + +epicsMutex Tracker::doneLock; +epicsEvent Tracker::doneEvt; +Tracker::inprog_t Tracker::inprog; +bool Tracker::abort = false; + +#ifdef USE_SIGNAL +static +void alldone(int num) { - const Status &s = d.status; - o << '[' << Status::StatusTypeName[s.getType()] << "] "; - string msg = s.getMessage(); - if (!msg.empty()) - { - o << msg; - } - else - { - o << "(no error message)"; - } - // dump stack trace only if on debug mode - if (IS_LOGGABLE(logLevelDebug)) - { - string sd = s.getStackDump(); - if (!sd.empty()) - { - o << std::endl << sd; + (void)num; + Tracker::abort = true; + Tracker::doneEvt.signal(); +} +#endif + +void Tracker::prepare() +{ +#ifdef USE_SIGNAL + signal(SIGINT, alldone); + signal(SIGTERM, alldone); + signal(SIGQUIT, alldone); +#endif +} + +static +void early(const char *inp, unsigned pos) +{ + fprintf(stderr, "Unexpected end of input: %s\n", inp); + throw std::runtime_error("Unexpected end of input"); +} + +// rudimentory parser for json array +// needed as long as Base < 3.15 is supported. +// for consistency, used with all version +void jarray(pvd::shared_vector& out, const char *inp) +{ + assert(inp[0]=='['); + const char * const orig = inp; + inp++; + + while(true) { + // starting a new token + + for(; *inp==' '; inp++) {} // skip leading whitespace + + if(*inp=='\0') early(inp, inp-orig); + + if(isalnum(*inp) || *inp=='+' || *inp=='-') { + // number + + const char *start = inp; + + while(isalnum(*inp) || *inp=='.' || *inp=='+' || *inp=='-') + inp++; + + if(*inp=='\0') early(inp, inp-orig); + + // inp points to first char after token + + out.push_back(std::string(start, inp-start)); + + } else if(*inp=='"') { + // quoted string + + const char *start = ++inp; // skip quote + + while(*inp!='\0' && *inp!='"') + inp++; + + if(*inp=='\0') early(inp, inp-orig); + + // inp points to trailing " + + out.push_back(std::string(start, inp-start)); + + inp++; // skip trailing " + + } else if(*inp==']') { + // no-op + } else { + fprintf(stderr, "Unknown token '%c' in \"%s\"", *inp, inp); + throw std::runtime_error("Unknown token"); } - } - return o; -} -char fieldSeparator = ' '; + for(; *inp==' '; inp++) {} // skip trailing whitespace -char arrayCountFlag = true; - -EnumMode enumMode = AutoEnum; - -bool formatTTypesFlag = true; -bool printUserTagFlag = true; - -std::ostream& terse(std::ostream& o, PVField::const_shared_pointer const & pv) -{ - Type type = pv->getField()->getType(); - switch (type) - { - case scalar: - o << *(pv.get()); - return o; - case structure: - return terseStructure(o, TR1::static_pointer_cast(pv)); - break; - case scalarArray: - return terseScalarArray(o, TR1::static_pointer_cast(pv)); - break; - case structureArray: - return terseStructureArray(o, TR1::static_pointer_cast(pv)); - break; - case union_: - return terseUnion(o, TR1::static_pointer_cast(pv)); - break; - case unionArray: - return terseUnionArray(o, TR1::static_pointer_cast(pv)); - break; - default: - std::ostringstream msg("unknown Field type: "); - msg << type; - throw std::logic_error(msg.str()); - } -} - -std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure const & pvEnumT) -{ - PVInt::const_shared_pointer pvIndex = pvEnumT.getSubField("index"); - if (!pvIndex) - throw std::runtime_error("enum_t structure does not have 'int index' field"); - - PVStringArray::const_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::const_shared_pointer const & pvEnumT) -{ - return printEnumT(o, *pvEnumT); -} - -std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvTimeT) -{ - char timeText[32]; - epicsTimeStamp epicsTS; - - PVScalar::const_shared_pointer secf(pvTimeT->getSubField("secondsPastEpoch")), - nsecf(pvTimeT->getSubField("nanoseconds")), - tagf(pvTimeT->getSubField("userTag")); - - epicsTS.secPastEpoch = secf ? secf->getAs() : 0; - epicsTS.nsec = nsecf ? nsecf->getAs() : 0; - - epicsTS.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH; - - epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%dT%H:%M:%S.%03f", &epicsTS); - o << timeText; - if (printUserTagFlag && tagf) - o << fieldSeparator << tagf->getAs(); - - return o; -} - -const char* severityNames[] = { - "NO_ALARM", // 0 - "MINOR", - "MAJOR", - "INVALID", - "UNDEFINED" // 4 -}; - -const char* statusNames[] = { - "NO_STATUS", // 0 - "DEVICE", - "DRIVER", - "RECORD", - "DB", - "CONF", - "UNDEFINED", - "CLIENT" // 7 -}; - -std::ostream& printAlarmT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvAlarmT) -{ - PVInt::const_shared_pointer pvSeverity = pvAlarmT->getSubField("severity"); - if (!pvSeverity) - throw std::runtime_error("alarm_t structure does not have 'int severity' field"); - - PVInt::const_shared_pointer pvStatus = pvAlarmT->getSubField("status"); - if (!pvStatus) - throw std::runtime_error("alarm_t structure does not have 'int status' field"); - - PVString::const_shared_pointer pvMessage = pvAlarmT->getSubField("message"); - if (!pvMessage) - throw std::runtime_error("alarm_t structure does not have 'string message' field"); - - int32 v = pvSeverity->get(); - if (v < 0 || v > 4) - o << v; - else - o << severityNames[v]; - o << fieldSeparator; - - v = pvStatus->get(); - if (v < 0 || v > 7) - o << v; - else - o << statusNames[v]; - o << fieldSeparator; - if (pvMessage->get().empty()) - o << ""; - else - o << pvMessage->get(); - return o; -} - - -bool isTType(epics::pvData::PVStructure::const_shared_pointer const & pvStructure) -{ - // "forget" about Ttype if disabled - if (!formatTTypesFlag) - return false; - - string id = pvStructure->getStructure()->getID(); - return (id == "enum_t" || - id == "time_t" || - id == "alarm_t"); -} - -bool formatTType(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvStructure) -{ - // 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 true; - } - else if (id == "time_t") - { - printTimeT(o, pvStructure); - return true; - } - else if (id == "alarm_t") - { - printAlarmT(o, pvStructure); - return true; + if(*inp==',') inp++; + else if(*inp==']') break; + else { + fprintf(stderr, "Unknown token '%c' in \"%s\"", *inp, inp); + throw std::runtime_error("Unknown token"); } } - return false; -} - - -std::ostream& terseStructure(std::ostream& o, PVStructure::const_shared_pointer const & pvStructure) -{ - if (!pvStructure) - { - o << "(null)"; - return o; - } - - // special t-types support (enum_t and time_t, etc.) - if (formatTType(o, pvStructure)) - return o; - - PVFieldPtrArray fieldsData = pvStructure->getPVFields(); - size_t length = pvStructure->getStructure()->getNumberFields(); - bool first = true; - for (size_t i = 0; i < length; i++) { - if (first) - first = false; - else - o << fieldSeparator; - - terse(o, fieldsData[i]); - } - return o; -} - -std::ostream& terseUnion(std::ostream& o, PVUnion::const_shared_pointer const & pvUnion) -{ - if (!pvUnion || !pvUnion->get()) - { - o << "(null)"; - return o; - } - - return terse(o, pvUnion->get()); -} - -std::ostream& terseScalarArray(std::ostream& o, const PVScalarArray::const_shared_pointer &pvArray) -{ - size_t length = pvArray->getLength(); - if (arrayCountFlag) - { - if (length<=0) - { - o << '0'; - return o; - } - o << length << fieldSeparator; - } - - bool first = true; - for (size_t i = 0; i < length; i++) { - if (first) - first = false; - else - o << fieldSeparator; - - pvArray->dumpValue(o, i); - } - return o; - - // avoid brackets - /* - o << *(pvArray.get()); - return o; - */ -} - -std::ostream& terseStructureArray(std::ostream& o, PVStructureArray::const_shared_pointer const & pvArray) -{ - size_t length = pvArray->getLength(); - if (arrayCountFlag) - { - if (length<=0) - { - o << '0'; - return o; - } - o << length << fieldSeparator; - } - - PVStructureArray::const_svector data = pvArray->view(); - bool first = true; - for (size_t i = 0; i < length; i++) { - if (first) - first = false; - else - o << fieldSeparator; - - terseStructure(o, data[i]); - } - return o; -} - -std::ostream& terseUnionArray(std::ostream& o, PVUnionArray::const_shared_pointer const & pvArray) -{ - size_t length = pvArray->getLength(); - if (arrayCountFlag) - { - if (length<=0) - { - o << '0'; - return o; - } - o << length << fieldSeparator; - } - - PVUnionArray::const_svector data = pvArray->view(); - bool first = true; - for (size_t i = 0; i < length; i++) { - if (first) - first = false; - else - o << fieldSeparator; - - terseUnion(o, data[i]); - } - return o; -} - - - - - - - - - - -/* Converts a hex character to its integer value */ -char from_hex(char ch) { - return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; -} - -/* Converts an integer value to its hex character*/ -char to_hex(char code) { - static char hex[] = "0123456789abcdef"; - return hex[code & 15]; -} - -/* Returns a url-encoded version of str */ -/* IMPORTANT: be sure to free() the returned string after use */ -char *url_encode(const char *str) { - const char *pstr = str; - char *buf = (char*)malloc(strlen(str) * 3 + 1), *pbuf = buf; - bool firstEquals = true; - while (*pstr) { - if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') - *pbuf++ = *pstr; - else if (*pstr == ' ') - *pbuf++ = '+'; - else if (*pstr == '=' && firstEquals) - { - firstEquals = false; - *pbuf++ = '='; - } - else - *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15); - pstr++; - } - *pbuf = '\0'; - return buf; -} - -GetFieldRequesterImpl::GetFieldRequesterImpl(epics::pvAccess::Channel::shared_pointer channel) : - m_channel(channel) -{ - -} - -string GetFieldRequesterImpl::getRequesterName() -{ - return "GetFieldRequesterImpl"; -} - -void GetFieldRequesterImpl::getDone(const epics::pvData::Status& status, epics::pvData::FieldConstPtr const & field) -{ - if (status.isSuccess()) - { - // show warning - if (!status.isOK()) - { - std::cerr << "[" << m_channel->getChannelName() << "] getField: " << dump_stack_only_on_debug(status) << std::endl; - } - - // assign smart pointers - { - Lock lock(m_pointerMutex); - m_field = field; - } - } - else - { - std::cerr << "[" << m_channel->getChannelName() << "] failed to get channel introspection data: " << dump_stack_only_on_debug(status) << std::endl; - } - - m_event.signal(); -} - -bool GetFieldRequesterImpl::waitUntilFieldGet(double timeOut) -{ - return m_event.wait(timeOut); -} - -epics::pvData::FieldConstPtr GetFieldRequesterImpl::getField() -{ - Lock lock(m_pointerMutex); - return m_field; -} - - - -// TODO invalid characters check, etc. -bool URI::parse(const string& uri, URI& result) -{ - const string prot_end("://"); - string::const_iterator prot_i = search(uri.begin(), uri.end(), - prot_end.begin(), prot_end.end()); - if( prot_i == uri.end() || prot_i == uri.begin() ) - return false; - - result.protocol.reserve(distance(uri.begin(), prot_i)); - transform(uri.begin(), prot_i, - back_inserter(result.protocol), - ::tolower); // protocol is icase - - advance(prot_i, prot_end.length()); - if ( prot_i == uri.end() ) - return false; - - string::const_iterator path_i = find(prot_i, uri.end(), '/'); - result.host.assign(prot_i, path_i); - - string::const_iterator fragment_i = find(path_i, uri.end(), '#'); - if ( fragment_i != uri.end() ) - result.fragment.assign(fragment_i+1, uri.end()); - - string::const_iterator query_i = find(path_i, fragment_i, '?'); - result.path.assign(path_i, query_i); - result.query_indicated = (query_i != fragment_i); - if ( result.query_indicated ) - result.query.assign(++query_i, fragment_i); - - return true; -} - -bool starts_with(const string& s1, const string& s2) { - return s2.size() <= s1.size() && s1.compare(0, s2.size(), s2) == 0; } diff --git a/pvtoolsSrc/pvutils.h b/pvtoolsSrc/pvutils.h index 9377f4d..b84183b 100644 --- a/pvtoolsSrc/pvutils.h +++ b/pvtoolsSrc/pvutils.h @@ -1,264 +1,66 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ +#ifndef PVUTILS_H +#define PVUTILS_H + +#include +#include +#include + +#include +#include +#include + #include #include #include -/// terse mode functions -void convertStructure(std::string* buffer, epics::pvData::PVStructure *data, int notFirst); -void convertArray(std::string*, epics::pvData::PVScalarArray * pv, int notFirst); -void convertStructureArray(std::string*, epics::pvData::PVStructureArray * pvdata, int notFirst); - -std::ostream& terse(std::ostream& o, epics::pvData::PVField::const_shared_pointer const & pv); -std::ostream& terseUnion(std::ostream& o, epics::pvData::PVUnion::const_shared_pointer const & pvUnion); -std::ostream& terseStructure(std::ostream& o, const epics::pvData::PVStructure::const_shared_pointer &pvStructure); -std::ostream& terseScalarArray(std::ostream& o, epics::pvData::PVScalarArray::const_shared_pointer const & pvArray); -std::ostream& terseStructureArray(std::ostream& o, epics::pvData::PVStructureArray::const_shared_pointer const & pvArray); -std::ostream& terseUnionArray(std::ostream& o, epics::pvData::PVUnionArray::const_shared_pointer const & pvArray); - -enum EnumMode { AutoEnum, NumberEnum, StringEnum }; - -bool isTType(epics::pvData::PVStructure::const_shared_pointer const & pvStructure); -bool formatTType(std::ostream& o, const epics::pvData::PVStructure::const_shared_pointer &pvStructure); - -std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure const & pvEnumT); -std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvEnumT); -std::ostream& printTimeT(std::ostream& o, epics::pvData::PVStructure::const_shared_pointer const & pvTimeT); - -bool starts_with(const std::string& str, const std::string& part); - -/* Converts a hex character to its integer value */ -char from_hex(char ch); - -/* Converts an integer value to its hex character*/ -char to_hex(char code); - -/* Returns a url-encoded version of str */ -/* IMPORTANT: be sure to free() the returned string after use */ -char *url_encode(const char *str); - -#include - -struct URI { -public: - static bool parse(const std::string& uri, URI& result); -public: - std::string protocol, host, path, query, fragment; - bool query_indicated; -}; - -class GetFieldRequesterImpl : - public epics::pvAccess::GetFieldRequester -{ -private: - epics::pvAccess::Channel::shared_pointer m_channel; - epics::pvData::FieldConstPtr m_field; - epics::pvData::Event m_event; - epics::pvData::Mutex m_pointerMutex; - -public: - - GetFieldRequesterImpl(epics::pvAccess::Channel::shared_pointer channel); - - virtual std::string getRequesterName(); - - virtual void getDone(const epics::pvData::Status& status, epics::pvData::FieldConstPtr const & field); - - epics::pvData::FieldConstPtr getField(); - - bool waitUntilFieldGet(double timeOut); -}; +typedef epicsGuard Guard; +typedef epicsGuardRelease UnGuard; +namespace pvd = epics::pvData; +namespace pva = epics::pvAccess; -struct dump_stack_only_on_debug -{ - const epics::pvData::Status &status; +extern double timeout; +extern bool debugFlag; - dump_stack_only_on_debug(const epics::pvData::Status &s) : status(s) {} -}; +extern pvd::PVStructure::Formatter::format_t outmode; +extern int verbosity; -std::ostream& operator<<(std::ostream& os, const dump_stack_only_on_debug& d); +extern std::string request; +extern std::string defaultProvider; +struct Tracker { + static epicsMutex doneLock; + static epicsEvent doneEvt; + typedef std::set inprog_t; + static inprog_t inprog; + static bool abort; - - -#include -#include - -// usage: pvutil_ostream myos(std::cout); - -class pvutil_ostream -{ - std::ostream& strm; -public: - pvutil_ostream(std::ostream& os) - : strm(os) - {} - - 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&)); -}; - - -template -inline pvutil_ostream& -operator<<(pvutil_ostream& out, const T& value) -{ - out.strm << value; - return out; -} - -// overload for std::ostream specific io manipulators -inline pvutil_ostream& -operator<<(pvutil_ostream& out, std::ostream& (*func)(std::ostream&)) -{ - out.strm << 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) -{ - if (isTType(value)) + Tracker() { - o << epics::pvData::format::indent() << value->getStructure()->getID() - << ' ' << value->getFieldName() << ' '; //" # "; - formatTType(o.strm, value); - o << std::endl; - //dumpPVStructure(o, *value, false); - return o; + Guard G(doneLock); + inprog.insert(this); } - - 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) + ~Tracker() { - epics::pvData::format::indent_scope s(o.strm); - - epics::pvData::PVStructureArray::const_svector data(value->view()); - for (size_t i = 0; i < length; i++) - if (data[i].get() == NULL) - o << epics::pvData::format::indent() << "(none)" << std::endl; - else - o << data[i]; + done(); } - - 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) + void done() { - epics::pvData::format::indent_scope s(o.strm); - - epics::pvData::PVUnionArray::const_svector data(value->view()); - for (size_t i = 0; i < length; i++) - if (data[i].get() == NULL) - o << epics::pvData::format::indent() << "(none)" << std::endl; - else - 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.strm); - - 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.strm); - - 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); -} + static void prepare(); +}; +void jarray(pvd::shared_vector& out, const char *inp); + + +#endif /* PVUTILS_H */