pvutils rework
This commit is contained in:
@ -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
|
||||
|
||||
|
1922
pvtoolsSrc/eget.cpp
1922
pvtoolsSrc/eget.cpp
File diff suppressed because it is too large
Load Diff
254
pvtoolsSrc/pvcall.cpp
Normal file
254
pvtoolsSrc/pvcall.cpp
Normal file
@ -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 <stdio.h>
|
||||
|
||||
#include <epicsStdlib.h>
|
||||
#include <epicsGetopt.h>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pva/client.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
# include <pv/json.h>
|
||||
# define USE_JSON
|
||||
#endif
|
||||
|
||||
#include "pvutils.h"
|
||||
|
||||
#ifndef EXECNAME
|
||||
# define EXECNAME "pvcall"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
void callusage (void)
|
||||
{
|
||||
fprintf (stderr, "\nUsage: " EXECNAME " [options] <PV name> [<arg1>=<value>]...\n\n"
|
||||
"\noptions:\n"
|
||||
" -h: Help: Print this message\n"
|
||||
" -V: Print version and exit\n"
|
||||
" -s <service name>: legacy form of PV name\n"
|
||||
" -a <service arg>: legacy form of argument\n"
|
||||
" -r <pv request>: Request, specifies what fields to return and options, default is '%s'\n"
|
||||
" -w <sec>: Wait time, specifies timeout, default is %f seconds for get, inf. for monitor\n"
|
||||
" -p <provider>: Set default provider name, default is '%s'\n"
|
||||
" -M <raw|nt|json>: 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 <input file>: errors\n"
|
||||
"\nexample: " EXECNAME " pv:name:add lhs=1 rhs=2\n\n"
|
||||
, request.c_str(), timeout, defaultProvider.c_str());
|
||||
}
|
||||
|
||||
typedef std::pair<std::string, pvd::PVFieldPtr> arg_t;
|
||||
typedef std::vector<arg_t> 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<std::string> sarr;
|
||||
|
||||
jarray(sarr, sval.c_str());
|
||||
|
||||
pvd::PVStringArrayPtr V(pvd::getPVDataCreate()->createPVScalarArray<pvd::PVStringArray>());
|
||||
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<pvd::PVString>());
|
||||
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 '"<<optarg<<"'\n";
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr,
|
||||
"Unrecognized option: '-%c'. ('" EXECNAME " -h' for help.)\n",
|
||||
optopt);
|
||||
return 1;
|
||||
case ':':
|
||||
fprintf(stderr,
|
||||
"Option '-%c' requires an argument. ('" EXECNAME " -h' for help.)\n",
|
||||
optopt);
|
||||
return 1;
|
||||
default :
|
||||
callusage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(!pv.empty()) {
|
||||
// ok
|
||||
} else if (argc <= optind) {
|
||||
fprintf(stderr, "No pv name specified. ('pvput -h' for help.)\n");
|
||||
return 1;
|
||||
} else {
|
||||
pv = argv[optind++];
|
||||
}
|
||||
|
||||
|
||||
for(int i=optind; i<argc; i++) {
|
||||
try {
|
||||
args.push_back(parseArg(argv[i]));
|
||||
} catch(std::exception& e){
|
||||
std::cerr<<"Error parsing argument '"<<optarg<<"'\n";
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
pvd::PVStructure::shared_pointer pvRequest;
|
||||
try {
|
||||
pvRequest = pvd::createRequest(request);
|
||||
} catch(std::exception& e){
|
||||
fprintf(stderr, "failed to parse request string: %s\n", e.what());
|
||||
return 1;
|
||||
}
|
||||
|
||||
pvd::PVStructurePtr argument;
|
||||
{
|
||||
pvd::FieldBuilderPtr builder(pvd::getFieldCreate()->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<pvd::PVString>("scheme")->put(defaultProvider);
|
||||
argument->getSubFieldT<pvd::PVString>("path")->put(pv);
|
||||
pvd::PVStructurePtr query(argument->getSubFieldT<pvd::PVStructure>("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"<<argument->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: "<<e.what()<<"\n";
|
||||
return 1;
|
||||
}
|
||||
assert(ret);
|
||||
|
||||
if(verbosity>=1)
|
||||
std::cout<<"# Result\n";
|
||||
std::cout<<ret->stream().format(outmode);
|
||||
|
||||
return 0;
|
||||
}
|
@ -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 <iostream>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <istream>
|
||||
#include <fstream>
|
||||
@ -8,397 +13,281 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <signal.h>
|
||||
#define USE_SIGNAL
|
||||
#endif
|
||||
|
||||
#include <epicsStdlib.h>
|
||||
#include <epicsGetopt.h>
|
||||
#include <epicsExit.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
#include <pv/caProvider.h>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <epicsThread.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/logger.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/event.h>
|
||||
#include <pv/thread.h>
|
||||
#include <pv/reftrack.h>
|
||||
|
||||
#include "pvutils.cpp"
|
||||
#include <pv/caProvider.h>
|
||||
#include <pv/logger.h>
|
||||
#include <pva/client.h>
|
||||
|
||||
using namespace std;
|
||||
namespace TR1 = std::tr1;
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
#include "pvutils.h"
|
||||
|
||||
|
||||
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
typedef epicsGuardRelease<epicsMutex> 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] <PV name>...\n\n"
|
||||
fprintf (stderr, "\nUsage: " EXECNAME " [options] <PV name>...\n\n"
|
||||
"\noptions:\n"
|
||||
" -h: Help: Print this message\n"
|
||||
" -V: Print version and exit\n"
|
||||
" -r <pv request>: Request, specifies what fields to return and options, default is '%s'\n"
|
||||
" -w <sec>: 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 <sec>: Wait time, specifies timeout, default is %f seconds for get, inf. for monitor\n"
|
||||
" -m: Monitor mode\n"
|
||||
" -p <provider>: Set default provider name, default is '%s'\n"
|
||||
" -v: Show entire structure\n"
|
||||
" -q: Quiet mode, print only error messages\n"
|
||||
" -M <raw|nt|json>: Output mode. default is 'nt'\n"
|
||||
" -v: Show entire structure (implies Raw mode)\n"
|
||||
" -d: Enable debug output\n"
|
||||
" -F <ofs>: Use <ofs> as an alternate output field separator\n"
|
||||
" -f <input file>: Use <input file> 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 <input file>: 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";
|
||||
op = channel.get(this, pvRequest);
|
||||
}
|
||||
virtual ~Getter() {}
|
||||
|
||||
virtual void getDone(const pvac::GetEvent& event) OVERRIDE FINAL
|
||||
{
|
||||
std::cout<<std::setw(pvnamewidth)<<std::left<<op.name()<<' ';
|
||||
switch(event.event) {
|
||||
case pvac::GetEvent::Fail:
|
||||
std::cerr<<"Error "<<event.message<<"\n";
|
||||
haderror = 1;
|
||||
break;
|
||||
case pvac::GetEvent::Cancel:
|
||||
break;
|
||||
case pvac::GetEvent::Success: {
|
||||
pvd::PVStructure::Formatter fmt(event.value->stream()
|
||||
.format(outmode));
|
||||
|
||||
if(verbosity>=2)
|
||||
fmt.highlight(*event.valid); // show all, highlight 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<PVStructure>(value)))
|
||||
{
|
||||
std::cout << std::setw(30) << std::left << channelName;
|
||||
std::cout << fieldSeparator;
|
||||
formatTType(std::cout, TR1::static_pointer_cast<PVStructure>(value));
|
||||
std::cout << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
pvutil_ostream myos(std::cout);
|
||||
myos << channelName << '\n' << *(pv.get()) << "\n\n";
|
||||
}
|
||||
}
|
||||
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<<fmt;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (mode == TerseMode)
|
||||
terseStructure(std::cout, pv) << '\n';
|
||||
else
|
||||
{
|
||||
pvutil_ostream myos(std::cout);
|
||||
myos << channelName << '\n' << *(pv.get()) << "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
// tracking get and monitor operations in progress
|
||||
|
||||
struct Tracker {
|
||||
static epicsMutex doneLock;
|
||||
static epicsEvent doneEvt;
|
||||
typedef std::set<Tracker*> inprog_t;
|
||||
static inprog_t inprog;
|
||||
static bool abort;
|
||||
|
||||
Tracker()
|
||||
{
|
||||
Guard G(doneLock);
|
||||
inprog.insert(this);
|
||||
}
|
||||
~Tracker()
|
||||
{
|
||||
std::cout.flush();
|
||||
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';
|
||||
}
|
||||
std::cout.flush();
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
virtual ~Worker() {}
|
||||
virtual void process(const pvac::MonitorEvent& event) =0;
|
||||
};
|
||||
|
||||
struct MonitorRequesterImpl : public MonitorRequester, public Tracker
|
||||
// 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<Worker> value_type;
|
||||
typedef std::tr1::weak_ptr<Worker> weak_type;
|
||||
// work queue holds only weak_ptr
|
||||
// so jobs must be kept alive seperately
|
||||
typedef std::deque<std::pair<weak_type, pvac::MonitorEvent> > 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()
|
||||
{
|
||||
|
||||
const string m_channelName;
|
||||
operation_type::shared_pointer op;
|
||||
|
||||
MonitorRequesterImpl(std::string channelName) : m_channelName(channelName) {}
|
||||
virtual ~MonitorRequesterImpl() {}
|
||||
|
||||
virtual string getRequesterName()
|
||||
{
|
||||
return "MonitorRequesterImpl";
|
||||
Guard G(mutex);
|
||||
running = false;
|
||||
}
|
||||
event.signal();
|
||||
worker.exitWait();
|
||||
}
|
||||
|
||||
virtual void monitorConnect(const epics::pvData::Status& status, Monitor::shared_pointer const & monitor, StructureConstPtr const & /*structure*/)
|
||||
void push(const weak_type& cb, const pvac::MonitorEvent& evt)
|
||||
{
|
||||
if (status.isSuccess())
|
||||
bool wake;
|
||||
{
|
||||
Status startStatus = monitor->start();
|
||||
// show error
|
||||
// TODO and exit
|
||||
if (!startStatus.isSuccess() || debugFlag)
|
||||
{
|
||||
std::cerr << "[" << m_channelName << "] channel monitor start: " << startStatus << '\n';
|
||||
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 : "<<e.what()<<"\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct MonTracker : public pvac::ClientChannel::MonitorCallback,
|
||||
public Worker,
|
||||
public Tracker,
|
||||
public std::tr1::enable_shared_from_this<MonTracker>
|
||||
{
|
||||
POINTER_DEFINITIONS(MonTracker);
|
||||
|
||||
MonTracker(WorkQueue& monwork, pvac::ClientChannel& channel, const pvd::PVStructurePtr& pvRequest)
|
||||
:monwork(monwork)
|
||||
,mon(channel.monitor(this, pvRequest))
|
||||
{}
|
||||
virtual ~MonTracker() {mon.cancel();}
|
||||
|
||||
WorkQueue& monwork;
|
||||
|
||||
pvd::BitSet valid; // only access for process()
|
||||
|
||||
pvac::Monitor mon; // must be last data member
|
||||
|
||||
virtual void monitorEvent(const pvac::MonitorEvent& evt) OVERRIDE FINAL
|
||||
{
|
||||
// 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 process(const pvac::MonitorEvent& evt) OVERRIDE FINAL
|
||||
{
|
||||
// running on our worker thread
|
||||
switch(evt.event) {
|
||||
case pvac::MonitorEvent::Fail:
|
||||
std::cerr<<std::setw(pvnamewidth)<<std::left<<mon.name()<<" Error "<<evt.message<<"\n";
|
||||
haderror = 1;
|
||||
done();
|
||||
break;
|
||||
case pvac::MonitorEvent::Cancel:
|
||||
break;
|
||||
case pvac::MonitorEvent::Disconnect:
|
||||
std::cout<<std::setw(pvnamewidth)<<std::left<<mon.name()<<" <Disconnect>\n";
|
||||
valid.clear();
|
||||
break;
|
||||
case pvac::MonitorEvent::Data:
|
||||
{
|
||||
unsigned n;
|
||||
for(n=0; n<2 && mon.poll(); n++) {
|
||||
valid |= mon.changed;
|
||||
|
||||
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
|
||||
{
|
||||
std::cerr << "monitorConnect(" << status << ")\n";
|
||||
fmt.show(mon.changed); // highlight none
|
||||
|
||||
std::cout<<std::setw(pvnamewidth)<<std::left<<mon.name()<<' '<<fmt;
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void channelDisconnect(bool destroy) {
|
||||
if(!destroy) {
|
||||
std::cerr << m_channelName<<" Disconnected\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void monitorEvent(Monitor::shared_pointer const & monitor)
|
||||
{
|
||||
if(debugFlag)
|
||||
std::cerr << "[" << m_channelName << "] channel monitor event: \n";
|
||||
|
||||
for(MonitorElement::Ref it(monitor); it; ++it)
|
||||
{
|
||||
MonitorElement* element(it.get());
|
||||
|
||||
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";
|
||||
}
|
||||
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<PVStructure>(value)))
|
||||
{
|
||||
std::cout << std::setw(30) << std::left << m_channelName;
|
||||
std::cout << fieldSeparator;
|
||||
formatTType(std::cout, TR1::static_pointer_cast<PVStructure>(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;
|
||||
|
||||
std::cout << fieldSeparator;
|
||||
|
||||
terseStructure(std::cout, element->pvStructurePtr) << '\n';
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << m_channelName << '\n';
|
||||
pvutil_ostream myos(std::cout);
|
||||
myos << *(element->pvStructurePtr.get()) << "\n\n";
|
||||
}
|
||||
|
||||
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<PVStructure>("timeStamp"));
|
||||
PVStructure::shared_pointer alarm(element->pvStructurePtr->getSubField<PVStructure>("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<PVScalar>("severity"));
|
||||
|
||||
bool inAlarm = !pvSeverity ? false : (pvSeverity->getAs<uint32>()!=0);
|
||||
if (inAlarm)
|
||||
terseStructure(std::cout, alarm);
|
||||
}
|
||||
|
||||
std::cout<< '\n';
|
||||
}
|
||||
};
|
||||
|
||||
} // 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",
|
||||
pva::Version version(EXECNAME, "cpp",
|
||||
EPICS_PVA_MAJOR_VERSION,
|
||||
EPICS_PVA_MINOR_VERSION,
|
||||
EPICS_PVA_MAINTENANCE_VERSION,
|
||||
@ -409,75 +298,62 @@ int main (int argc, char *argv[])
|
||||
case 'R':
|
||||
refmon.start(5.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 */
|
||||
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. ('pvget -h' for help.)\n", optarg);
|
||||
"- ignored. ('" EXECNAME " -h' for help.)\n", optarg);
|
||||
} else {
|
||||
explicit_timeout = true;
|
||||
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;
|
||||
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,140 +362,55 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
if(!explicit_timeout) {
|
||||
if(monitor)
|
||||
timeOut = -1.0; // forever
|
||||
else
|
||||
timeOut = 3.0;
|
||||
}
|
||||
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<string> 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<ChannelProvider::shared_pointer> providers;
|
||||
typedef std::map<std::string, Channel::shared_pointer> 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<std::tr1::shared_ptr<Tracker> > ops;
|
||||
for(int i = optind; i < argc; i++) {
|
||||
pvnamewidth = std::max(pvnamewidth, strlen(argv[i]));
|
||||
}
|
||||
|
||||
SET_LOG_LEVEL(debugFlag ? pva::logLevelDebug : pva::logLevelError);
|
||||
|
||||
epics::pvAccess::ca::CAClientFactory::start();
|
||||
|
||||
for(size_t n=0; n<pvs.size(); n++)
|
||||
{
|
||||
URI uri;
|
||||
bool validURI = URI::parse(pvs[n], uri);
|
||||
pvac::ClientProvider provider(defaultProvider);
|
||||
|
||||
if (validURI) {
|
||||
if (uri.path.length() <= 1)
|
||||
{
|
||||
std::cerr << "invalid URI '" << pvs[n] << "', empty path\n";
|
||||
return 1;
|
||||
}
|
||||
pvs[n] = uri.path.substr(1);;
|
||||
} else {
|
||||
uri.protocol = defaultProvider;
|
||||
uri.host.clear();
|
||||
}
|
||||
std::vector<std::tr1::shared_ptr<Tracker> > tracked;
|
||||
|
||||
ChannelProvider::shared_pointer provider(ChannelProviderRegistry::clients()->getProvider(uri.protocol));
|
||||
if(!provider) {
|
||||
std::cerr<<"Unknown provider \""<<uri.protocol<<"\" for channel "<<pvs[n]<<"\n";
|
||||
return 1;
|
||||
}
|
||||
epics::auto_ptr<WorkQueue> Q;
|
||||
if(monitor)
|
||||
Q.reset(new WorkQueue);
|
||||
|
||||
Channel::shared_pointer channel;
|
||||
chan_cache_t::const_iterator it = chan_cache.find(pvs[n]);
|
||||
if(it==chan_cache.end()) {
|
||||
try {
|
||||
channel = provider->createChannel(pvs[n], DefaultChannelRequester::build(),
|
||||
ChannelProvider::PRIORITY_DEFAULT, uri.host);
|
||||
} catch(std::exception& e) {
|
||||
std::cerr<<"Provider "<<uri.protocol<<" can't create channel \""<<pvs[n]<<"\"\n";
|
||||
return 1;
|
||||
}
|
||||
chan_cache[pvs[n]] = channel;
|
||||
} else {
|
||||
channel = it->second;
|
||||
}
|
||||
for(int i = optind; i < argc; i++) {
|
||||
pvac::ClientChannel chan(provider.connect(argv[i]));
|
||||
|
||||
if(monitor) {
|
||||
std::tr1::shared_ptr<MonitorRequesterImpl> req(new MonitorRequesterImpl(pvs[n]));
|
||||
std::tr1::shared_ptr<MonTracker> mon(new MonTracker(*Q, chan, pvRequest));
|
||||
|
||||
req->op = channel->createMonitor(req, pvRequest);
|
||||
tracked.push_back(mon);
|
||||
|
||||
ops.push_back(req);
|
||||
} else { // Get
|
||||
std::tr1::shared_ptr<Getter> get(new Getter(chan, pvRequest));
|
||||
|
||||
} else {
|
||||
std::tr1::shared_ptr<ChannelGetRequesterImpl> req(new ChannelGetRequesterImpl(pvs[n]));
|
||||
|
||||
req->op = channel->createChannelGet(req, pvRequest);
|
||||
|
||||
ops.push_back(req);
|
||||
tracked.push_back(get);
|
||||
}
|
||||
|
||||
// make sure to keep the provider alive as Channels will be automatically closed
|
||||
providers.insert(provider);
|
||||
}
|
||||
|
||||
// Active channels continue to be referenced by get/monitor stored in 'ops'
|
||||
chan_cache.clear();
|
||||
|
||||
// ========================== Wait for operations to complete, or timeout
|
||||
|
||||
#ifdef USE_SIGNAL
|
||||
signal(SIGINT, alldone);
|
||||
signal(SIGTERM, alldone);
|
||||
signal(SIGQUIT, alldone);
|
||||
#endif
|
||||
Tracker::prepare(); // install signal handler
|
||||
|
||||
if(debugFlag)
|
||||
std::cerr<<"Waiting...\n";
|
||||
@ -628,20 +419,19 @@ int main (int argc, char *argv[])
|
||||
Guard G(Tracker::doneLock);
|
||||
while(Tracker::inprog.size() && !Tracker::abort) {
|
||||
UnGuard U(G);
|
||||
if(timeOut<=0)
|
||||
if(timeout<=0)
|
||||
Tracker::doneEvt.wait();
|
||||
else if(!Tracker::doneEvt.wait(timeOut)) {
|
||||
allOK = false;
|
||||
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;
|
||||
}
|
||||
|
@ -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 <iostream>
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pva/client.h>
|
||||
#include <pv/caProvider.h>
|
||||
|
||||
#include <stdio.h>
|
||||
@ -16,20 +20,12 @@
|
||||
#include <pv/event.h>
|
||||
#include <epicsExit.h>
|
||||
|
||||
#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<<op.name()<<" Error: "<<evt.message<<"\n";
|
||||
haderror = 1;
|
||||
break;
|
||||
case pvac::InfoEvent::Success:
|
||||
std::cout<<op.name()<<" "<<evt.type<<"\n";
|
||||
}
|
||||
done();
|
||||
std::cout.flush();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/*+**************************************************************************
|
||||
*
|
||||
* Function: main
|
||||
*
|
||||
* Description: pvinfo main()
|
||||
* Evaluate command line options, set up PVA, connect the
|
||||
* channels, print the data as requested
|
||||
*
|
||||
* Arg(s) In: [options] <pv-name>...
|
||||
*
|
||||
* 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,11 +88,15 @@ 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 */
|
||||
@ -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<string> 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<GetInfo> infos(argc - optind);
|
||||
|
||||
SET_LOG_LEVEL(debug ? logLevelDebug : logLevelError);
|
||||
|
||||
std::cout << std::boolalpha;
|
||||
|
||||
bool allOK = true;
|
||||
|
||||
epics::pvAccess::ca::CAClientFactory::start();
|
||||
pva::ca::CAClientFactory::start();
|
||||
|
||||
{
|
||||
std::vector<std::string> pvNames;
|
||||
std::vector<std::string> pvAddresses;
|
||||
std::vector<ChannelProvider::shared_pointer> providers;
|
||||
pvac::ClientProvider prov(defaultProvider);
|
||||
|
||||
pvNames.reserve(nPvs);
|
||||
pvAddresses.reserve(nPvs);
|
||||
for(int i = optind; i<argc; i++) {
|
||||
infos[i-optind].op = prov.connect(argv[i]).info(&infos[i-optind]);
|
||||
}
|
||||
|
||||
Tracker::prepare(); // install signal handler
|
||||
|
||||
for (int n = 0; n < nPvs; n++)
|
||||
{
|
||||
URI uri;
|
||||
bool validURI = URI::parse(pvs[n], uri);
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// first connect to all, this allows resource (e.g. TCP connection) sharing
|
||||
vector<Channel::shared_pointer> channels(nPvs);
|
||||
for (int n = 0; n < nPvs; n++)
|
||||
{
|
||||
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> getFieldRequesterImpl(new GetFieldRequesterImpl(channel));
|
||||
channel->getField(getFieldRequesterImpl, "");
|
||||
|
||||
if (getFieldRequesterImpl->waitUntilFieldGet(timeOut))
|
||||
{
|
||||
Structure::const_shared_pointer structure =
|
||||
TR1::dynamic_pointer_cast<const Structure>(getFieldRequesterImpl->getField());
|
||||
|
||||
channel->printInfo();
|
||||
if (structure)
|
||||
{
|
||||
std::cout << *structure << std::endl << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "(null introspection data)" << std::endl << std::endl;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -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 <stdio.h>
|
||||
|
||||
|
3
pvtoolsSrc/pvmonitor.cpp
Normal file
3
pvtoolsSrc/pvmonitor.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#define PVMONITOR
|
||||
#define EXECNAME "pvmonitor"
|
||||
#include "pvget.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 <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@ -29,30 +33,10 @@
|
||||
|
||||
#include <pv/caProvider.h>
|
||||
|
||||
#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 <pv request>: Request, specifies what fields to return and options, default is '%s'\n"
|
||||
" -w <sec>: Wait time, specifies timeout, default is %f second(s)\n"
|
||||
" -t: Terse mode - print only successfully written value, without names\n"
|
||||
" -p <provider>: Set default provider name, default is '%s'\n"
|
||||
" -v: Show entire structure\n"
|
||||
" -M <raw|nt|json>: 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 <ofs>: Use <ofs> as an alternate output field separator\n"
|
||||
" -f <input file>: Use <input file> 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 <input file>: 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<const PVStructure>(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<<pv->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<std::string>& 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<std::string> bare_t;
|
||||
typedef pvd::shared_vector<std::string> bare_t;
|
||||
bare_t bare;
|
||||
|
||||
typedef std::pair<std::string, std::string> KV_t;
|
||||
typedef std::vector<KV_t> pairs_t;
|
||||
pairs_t pairs;
|
||||
|
||||
shared_vector<std::string> jarr;
|
||||
|
||||
PVStructure::const_shared_pointer current;
|
||||
pvd::shared_vector<std::string> jarr;
|
||||
|
||||
virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args)
|
||||
{
|
||||
if(debug) std::cerr<<"Server defined structure\n"<<build;
|
||||
PVStructurePtr root(getPVDataCreate()->createPVStructure(build));
|
||||
if(debugFlag) std::cerr<<"Server defined structure\n"<<build;
|
||||
pvd::PVStructurePtr root(pvd::getPVDataCreate()->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<PVScalar*>(fld.get()));
|
||||
pvd::PVScalar* sfld(static_cast<pvd::PVScalar*>(fld.get()));
|
||||
sfld->putFrom(bare[0]);
|
||||
args.tosend.set(sfld->getFieldOffset());
|
||||
|
||||
} else if(ftype==scalarArray) {
|
||||
PVScalarArray* sfld(static_cast<PVScalarArray*>(fld.get()));
|
||||
} else if(ftype==pvd::scalarArray) {
|
||||
pvd::PVScalarArray* sfld(static_cast<pvd::PVScalarArray*>(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<PVStructure*>(fld.get()));
|
||||
pvd::PVStructure* sfld(static_cast<pvd::PVStructure*>(fld.get()));
|
||||
|
||||
PVScalar* idxfld(sfld->getSubFieldT<PVScalar>("index").get());
|
||||
PVStringArray::const_svector choices(current->getSubFieldT<PVStringArray>("value.choices")->view());
|
||||
assert(!!args.previous); // ensure by calling put(..., true) below
|
||||
pvd::PVScalar* idxfld(sfld->getSubFieldT<pvd::PVScalar>("index").get());
|
||||
pvd::PVStringArray::const_svector choices(args.previous->getSubFieldT<pvd::PVStringArray>("value.choices")->view());
|
||||
|
||||
bool found=false;
|
||||
for(size_t i=0; i<choices.size(); i++) {
|
||||
if(bare[0]==choices[i]) {
|
||||
idxfld->putFrom<int64>(i);
|
||||
idxfld->putFrom<pvd::int64>(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<std::string> arr;
|
||||
pvd::shared_vector<std::string> arr;
|
||||
jarray(arr, it->second.c_str());
|
||||
|
||||
PVScalarArray* afld(dynamic_cast<PVScalarArray*>(fld.get()));
|
||||
pvd::PVScalarArray* afld(dynamic_cast<pvd::PVScalarArray*>(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<PVScalar>(fld));
|
||||
pvd::PVScalarPtr sfld(std::tr1::dynamic_pointer_cast<pvd::PVScalar>(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: "<<args.tosend<<"\n"<<args.root;
|
||||
}
|
||||
|
||||
@ -402,24 +259,20 @@ int main (int argc, char *argv[])
|
||||
int opt; /* getopt() current option */
|
||||
bool quiet = false;
|
||||
|
||||
istream* inputStream = 0;
|
||||
ifstream ifs;
|
||||
bool fromStream = false;
|
||||
|
||||
setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */
|
||||
putenv(const_cast<char*>("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());
|
||||
fprintf(stderr, "Unsupported option -f\n");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
inputStream = &ifs;
|
||||
}
|
||||
|
||||
fromStream = true;
|
||||
break;
|
||||
}
|
||||
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<string> values;
|
||||
if (fromStream)
|
||||
{
|
||||
string cn;
|
||||
while (true)
|
||||
{
|
||||
*inputStream >> cn;
|
||||
if (!(*inputStream))
|
||||
break;
|
||||
values.push_back(cn);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::string> values;
|
||||
// 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;
|
||||
}
|
||||
|
||||
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<epicsMutex> G(thework.lock);
|
||||
while(!thework.done) {
|
||||
epicsGuardRelease<epicsMutex> 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;
|
||||
}
|
||||
|
@ -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 <stdio.h>
|
||||
#if !defined(_WIN32)
|
||||
#include <signal.h>
|
||||
#define USE_SIGNAL
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@ -12,466 +20,109 @@
|
||||
#include <pv/logger.h>
|
||||
#include <pv/pvTimeStamp.h>
|
||||
|
||||
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())
|
||||
(void)num;
|
||||
Tracker::abort = true;
|
||||
Tracker::doneEvt.signal();
|
||||
}
|
||||
#endif
|
||||
|
||||
void Tracker::prepare()
|
||||
{
|
||||
o << msg;
|
||||
#ifdef USE_SIGNAL
|
||||
signal(SIGINT, alldone);
|
||||
signal(SIGTERM, alldone);
|
||||
signal(SIGQUIT, alldone);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
||||
static
|
||||
void early(const char *inp, unsigned pos)
|
||||
{
|
||||
o << "(no error message)";
|
||||
fprintf(stderr, "Unexpected end of input: %s\n", inp);
|
||||
throw std::runtime_error("Unexpected end of input");
|
||||
}
|
||||
// dump stack trace only if on debug mode
|
||||
if (IS_LOGGABLE(logLevelDebug))
|
||||
|
||||
// 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<std::string>& out, const char *inp)
|
||||
{
|
||||
string sd = s.getStackDump();
|
||||
if (!sd.empty())
|
||||
{
|
||||
o << std::endl << sd;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
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");
|
||||
}
|
||||
|
||||
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<const PVStructure>(pv));
|
||||
break;
|
||||
case scalarArray:
|
||||
return terseScalarArray(o, TR1::static_pointer_cast<const PVScalarArray>(pv));
|
||||
break;
|
||||
case structureArray:
|
||||
return terseStructureArray(o, TR1::static_pointer_cast<const PVStructureArray>(pv));
|
||||
break;
|
||||
case union_:
|
||||
return terseUnion(o, TR1::static_pointer_cast<const PVUnion>(pv));
|
||||
break;
|
||||
case unionArray:
|
||||
return terseUnionArray(o, TR1::static_pointer_cast<const PVUnionArray>(pv));
|
||||
break;
|
||||
default:
|
||||
std::ostringstream msg("unknown Field type: ");
|
||||
msg << type;
|
||||
throw std::logic_error(msg.str());
|
||||
if(*inp==',') inp++;
|
||||
else if(*inp==']') break;
|
||||
else {
|
||||
fprintf(stderr, "Unknown token '%c' in \"%s\"", *inp, inp);
|
||||
throw std::runtime_error("Unknown token");
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& printEnumT(std::ostream& o, epics::pvData::PVStructure const & pvEnumT)
|
||||
{
|
||||
PVInt::const_shared_pointer pvIndex = pvEnumT.getSubField<PVInt>("index");
|
||||
if (!pvIndex)
|
||||
throw std::runtime_error("enum_t structure does not have 'int index' field");
|
||||
|
||||
PVStringArray::const_shared_pointer pvChoices = pvEnumT.getSubField<PVStringArray>("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<int32>(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<PVScalar>("secondsPastEpoch")),
|
||||
nsecf(pvTimeT->getSubField<PVScalar>("nanoseconds")),
|
||||
tagf(pvTimeT->getSubField<PVScalar>("userTag"));
|
||||
|
||||
epicsTS.secPastEpoch = secf ? secf->getAs<int64>() : 0;
|
||||
epicsTS.nsec = nsecf ? nsecf->getAs<int32>() : 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<int32>();
|
||||
|
||||
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<PVInt>("severity");
|
||||
if (!pvSeverity)
|
||||
throw std::runtime_error("alarm_t structure does not have 'int severity' field");
|
||||
|
||||
PVInt::const_shared_pointer pvStatus = pvAlarmT->getSubField<PVInt>("status");
|
||||
if (!pvStatus)
|
||||
throw std::runtime_error("alarm_t structure does not have 'int status' field");
|
||||
|
||||
PVString::const_shared_pointer pvMessage = pvAlarmT->getSubField<PVString>("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 << "<no message>";
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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 <ostream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <epicsEvent.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
#include <pv/event.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvAccess.h>
|
||||
|
||||
/// 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);
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
typedef epicsGuardRelease<epicsMutex> UnGuard;
|
||||
namespace pvd = epics::pvData;
|
||||
namespace pva = epics::pvAccess;
|
||||
|
||||
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 };
|
||||
extern double timeout;
|
||||
extern bool debugFlag;
|
||||
|
||||
bool isTType(epics::pvData::PVStructure::const_shared_pointer const & pvStructure);
|
||||
bool formatTType(std::ostream& o, const epics::pvData::PVStructure::const_shared_pointer &pvStructure);
|
||||
extern pvd::PVStructure::Formatter::format_t outmode;
|
||||
extern int verbosity;
|
||||
|
||||
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);
|
||||
extern std::string request;
|
||||
extern std::string defaultProvider;
|
||||
|
||||
bool starts_with(const std::string& str, const std::string& part);
|
||||
struct Tracker {
|
||||
static epicsMutex doneLock;
|
||||
static epicsEvent doneEvt;
|
||||
typedef std::set<Tracker*> inprog_t;
|
||||
static inprog_t inprog;
|
||||
static bool abort;
|
||||
|
||||
/* Converts a hex character to its integer value */
|
||||
char from_hex(char ch);
|
||||
Tracker()
|
||||
{
|
||||
Guard G(doneLock);
|
||||
inprog.insert(this);
|
||||
}
|
||||
~Tracker()
|
||||
{
|
||||
done();
|
||||
}
|
||||
void done()
|
||||
{
|
||||
{
|
||||
Guard G(doneLock);
|
||||
inprog.erase(this);
|
||||
}
|
||||
doneEvt.signal();
|
||||
}
|
||||
|
||||
/* 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 <string>
|
||||
|
||||
struct URI {
|
||||
public:
|
||||
static bool parse(const std::string& uri, URI& result);
|
||||
public:
|
||||
std::string protocol, host, path, query, fragment;
|
||||
bool query_indicated;
|
||||
static void prepare();
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
void jarray(pvd::shared_vector<std::string>& out, const char *inp);
|
||||
|
||||
|
||||
struct dump_stack_only_on_debug
|
||||
{
|
||||
const epics::pvData::Status &status;
|
||||
|
||||
dump_stack_only_on_debug(const epics::pvData::Status &s) : status(s) {}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const dump_stack_only_on_debug& d);
|
||||
|
||||
|
||||
|
||||
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
|
||||
// usage: pvutil_ostream myos(std::cout);
|
||||
|
||||
class pvutil_ostream
|
||||
{
|
||||
std::ostream& strm;
|
||||
public:
|
||||
pvutil_ostream(std::ostream& os)
|
||||
: strm(os)
|
||||
{}
|
||||
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
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))
|
||||
{
|
||||
o << epics::pvData::format::indent() << value->getStructure()->getID()
|
||||
<< ' ' << value->getFieldName() << ' '; //" # ";
|
||||
formatTType(o.strm, value);
|
||||
o << std::endl;
|
||||
//dumpPVStructure(o, *value, false);
|
||||
return o;
|
||||
}
|
||||
|
||||
return o << *value;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline pvutil_ostream&
|
||||
operator<<(pvutil_ostream& o, const epics::pvData::PVStructureArray::shared_pointer & value)
|
||||
{
|
||||
o << epics::pvData::format::indent() << value->getStructureArray()->getID()
|
||||
<< ' ' << value->getFieldName() << std::endl;
|
||||
size_t length = value->getLength();
|
||||
if (length > 0)
|
||||
{
|
||||
epics::pvData::format::indent_scope s(o.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];
|
||||
}
|
||||
|
||||
return o;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline pvutil_ostream&
|
||||
operator<<(pvutil_ostream& o, const epics::pvData::PVUnionArray::shared_pointer & value)
|
||||
{
|
||||
o << epics::pvData::format::indent() << value->getUnionArray()->getID()
|
||||
<< ' ' << value->getFieldName() << std::endl;
|
||||
size_t length = value->getLength();
|
||||
if (length > 0)
|
||||
{
|
||||
epics::pvData::format::indent_scope s(o.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<epics::pvData::PVStructure>(fieldField);
|
||||
else if (type == epics::pvData::structureArray)
|
||||
o << std::tr1::static_pointer_cast<epics::pvData::PVStructureArray>(fieldField);
|
||||
else if (type == epics::pvData::union_)
|
||||
o << std::tr1::static_pointer_cast<epics::pvData::PVUnion>(fieldField);
|
||||
else if (type == epics::pvData::unionArray)
|
||||
o << std::tr1::static_pointer_cast<epics::pvData::PVUnionArray>(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<length; i++) {
|
||||
o << fieldsData[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline pvutil_ostream&
|
||||
operator<<(pvutil_ostream& o, const epics::pvData::PVStructure& value)
|
||||
{
|
||||
return dumpPVStructure(o, value, true);
|
||||
}
|
||||
|
||||
#endif /* PVUTILS_H */
|
||||
|
Reference in New Issue
Block a user