pvutils rework

This commit is contained in:
Michael Davidsaver
2018-09-18 09:29:49 -07:00
parent 39d0706cf1
commit 6dea9d90cc
10 changed files with 893 additions and 3495 deletions

View File

@ -7,18 +7,30 @@ USR_CPPFLAGS += -I$(TOP)/src/remote
PROD_HOST += pvget PROD_HOST += pvget
pvget_SRCS += pvget.cpp pvget_SRCS += pvget.cpp
pvget_SRCS += pvutils.cpp
PROD_HOST += pvmonitor
pvmonitor_SRCS += pvmonitor.cpp
pvmonitor_SRCS += pvutils.cpp
PROD_HOST += pvput PROD_HOST += pvput
pvput_SRCS += pvput.cpp pvput_SRCS += pvput.cpp
pvput_SRCS += pvutils.cpp
PROD_HOST += pvcall
pvcall_SRCS += pvcall.cpp
pvcall_SRCS += pvutils.cpp
PROD_HOST += pvinfo PROD_HOST += pvinfo
pvinfo_SRCS += pvinfo.cpp pvinfo_SRCS += pvinfo.cpp
pvinfo_SRCS += pvutils.cpp
PROD_HOST += pvlist PROD_HOST += pvlist
pvlist_SRCS += pvlist.cpp pvlist_SRCS += pvlist.cpp
PROD_HOST += eget PROD_HOST += eget
eget_SRCS += eget.cpp eget_SRCS += eget.cpp
eget_SRCS += pvutils.cpp
PROD_LIBS += pvAccessCA pvAccess pvData ca Com PROD_LIBS += pvAccessCA pvAccess pvData ca Com

File diff suppressed because it is too large Load Diff

254
pvtoolsSrc/pvcall.cpp Normal file
View 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;
}

View File

@ -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 <iostream>
#include <vector> #include <vector>
#include <set> #include <set>
#include <deque>
#include <string> #include <string>
#include <istream> #include <istream>
#include <fstream> #include <fstream>
@ -8,476 +13,347 @@
#include <stdio.h> #include <stdio.h>
#if !defined(_WIN32)
#include <signal.h>
#define USE_SIGNAL
#endif
#include <epicsStdlib.h> #include <epicsStdlib.h>
#include <epicsGetopt.h> #include <epicsGetopt.h>
#include <epicsExit.h> #include <epicsExit.h>
#include <epicsGuard.h> #include <epicsGuard.h>
#include <pv/caProvider.h> #include <pv/pvData.h>
#include <pv/pvAccess.h>
#include <epicsThread.h>
#include <pv/logger.h> #include <pv/logger.h>
#include <pv/lock.h> #include <pv/lock.h>
#include <pv/event.h> #include <pv/event.h>
#include <pv/thread.h>
#include <pv/reftrack.h> #include <pv/reftrack.h>
#include "pvutils.cpp" #include <pv/caProvider.h>
#include <pv/logger.h>
#include <pva/client.h>
using namespace std; #include "pvutils.h"
namespace TR1 = std::tr1;
using namespace epics::pvData;
using namespace epics::pvAccess;
#ifndef EXECNAME
# define EXECNAME "pvget"
typedef epicsGuard<epicsMutex> Guard; #endif
typedef epicsGuardRelease<epicsMutex> UnGuard;
namespace { namespace {
bool debugFlag = false; size_t pvnamewidth;
string request("field()"); int haderror;
string defaultProvider("pva");
enum PrintMode { ValueOnlyMode, StructureMode, TerseMode };
PrintMode mode = ValueOnlyMode;
void usage (void) void usage (void)
{ {
fprintf (stderr, "\nUsage: pvget [options] <PV name>...\n\n" fprintf (stderr, "\nUsage: " EXECNAME " [options] <PV name>...\n\n"
"\noptions:\n" "\noptions:\n"
" -h: Help: Print this message\n" " -h: Help: Print this message\n"
" -V: Print version and exit\n" " -V: Print version and exit\n"
" -r <pv request>: Request, specifies what fields to return and options, default is '%s'\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" " -w <sec>: Wait time, specifies timeout, default is %f 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"
" -m: Monitor mode\n" " -m: Monitor mode\n"
" -p <provider>: Set default provider name, default is '%s'\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"
" -q: Quiet mode, print only error messages\n" " -v: Show entire structure (implies Raw mode)\n"
" -d: Enable debug output\n" " -d: Enable debug output\n"
" -F <ofs>: Use <ofs> as an alternate output field separator\n" " deprecated options:\n"
" -f <input file>: Use <input file> as an input that provides a list PV name(s) to be read, use '-' for stdin\n" " -q, -t, -i, -n, -F: ignored\n"
" enum format:\n" " -f <input file>: errors\n"
" -n: Force enum interpretation of values as numbers (default is enum string)\n" "\nexample: " EXECNAME " double01\n\n"
// " time format:\n" , request.c_str(), timeout, defaultProvider.c_str());
// " -u: print userTag\n"
"\nexample: pvget double01\n\n"
, request.c_str(), 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"); op = channel.get(this, pvRequest);
if (value.get() == 0) }
{ virtual ~Getter() {}
//std::cerr << "no 'value' field\n";
pvutil_ostream myos(std::cout); virtual void getDone(const pvac::GetEvent& event) OVERRIDE FINAL
myos << channelName << "\n" << *(pv.get()) << "\n\n"; {
} std::cout<<std::setw(pvnamewidth)<<std::left<<op.name()<<' ';
else switch(event.event) {
{ case pvac::GetEvent::Fail:
Type valueType = value->getField()->getType(); std::cerr<<"Error "<<event.message<<"\n";
if (valueType != scalar && valueType != scalarArray) haderror = 1;
{ break;
// switch to structure mode, unless it's T-type case pvac::GetEvent::Cancel:
if (valueType == structure && isTType(TR1::static_pointer_cast<PVStructure>(value))) break;
{ case pvac::GetEvent::Success: {
std::cout << std::setw(30) << std::left << channelName; pvd::PVStructure::Formatter fmt(event.value->stream()
std::cout << fieldSeparator; .format(outmode));
formatTType(std::cout, TR1::static_pointer_cast<PVStructure>(value));
std::cout << '\n'; if(verbosity>=2)
} fmt.highlight(*event.valid); // show all, highlight valid
else
{
pvutil_ostream myos(std::cout);
myos << channelName << '\n' << *(pv.get()) << "\n\n";
}
}
else else
{ fmt.show(*event.valid); // only show valid, highlight none
if (fieldSeparator == ' ' && value->getField()->getType() == scalar)
std::cout << std::setw(30) << std::left << channelName;
else
std::cout << channelName;
std::cout << fieldSeparator; std::cout<<fmt;
terse(std::cout, value) << '\n';
}
} }
} 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()
{
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(); std::cout.flush();
done(); 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<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()
{
{
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 : "<<e.what()<<"\n";
}
}
}
}
};
struct MonTracker : public pvac::ClientChannel::MonitorCallback,
public Worker,
public Tracker,
public std::tr1::enable_shared_from_this<MonTracker>
{ {
POINTER_DEFINITIONS(MonTracker);
const string m_channelName; MonTracker(WorkQueue& monwork, pvac::ClientChannel& channel, const pvd::PVStructurePtr& pvRequest)
operation_type::shared_pointer op; :monwork(monwork)
,mon(channel.monitor(this, pvRequest))
{}
virtual ~MonTracker() {mon.cancel();}
MonitorRequesterImpl(std::string channelName) : m_channelName(channelName) {} WorkQueue& monwork;
virtual ~MonitorRequesterImpl() {}
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()) // running on our worker thread
{ switch(evt.event) {
Status startStatus = monitor->start(); case pvac::MonitorEvent::Fail:
// show error std::cerr<<std::setw(pvnamewidth)<<std::left<<mon.name()<<" Error "<<evt.message<<"\n";
// TODO and exit haderror = 1;
if (!startStatus.isSuccess() || debugFlag)
{
std::cerr << "[" << m_channelName << "] channel monitor start: " << startStatus << '\n';
}
}
else
{
std::cerr << "monitorConnect(" << status << ")\n";
done(); done();
} break;
} case pvac::MonitorEvent::Cancel:
break;
virtual void channelDisconnect(bool destroy) { case pvac::MonitorEvent::Disconnect:
if(!destroy) { std::cout<<std::setw(pvnamewidth)<<std::left<<mon.name()<<" <Disconnect>\n";
std::cerr << m_channelName<<" Disconnected\n"; valid.clear();
} break;
} case pvac::MonitorEvent::Data:
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()); unsigned n;
for(n=0; n<2 && mon.poll(); n++) {
valid |= mon.changed;
if (mode == ValueOnlyMode) pvd::PVStructure::Formatter fmt(mon.root->stream()
{ .format(outmode));
PVField::shared_pointer value = element->pvStructurePtr->getSubField("value");
if (value.get() == 0) if(verbosity>=3)
{ fmt.highlight(mon.changed); // show all
std::cerr << "no 'value' field" << '\n'; else if(verbosity>=2)
std::cout << m_channelName << '\n'; fmt.highlight(mon.changed).show(valid);
pvutil_ostream myos(std::cout);
myos << *(element->pvStructurePtr.get()) << "\n\n";
}
else else
{ fmt.show(mon.changed); // highlight none
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; std::cout<<std::setw(pvnamewidth)<<std::left<<mon.name()<<' '<<fmt;
terseStructure(std::cout, element->pvStructurePtr) << '\n';
} }
else if(n==2) {
{ // too many updates, re-queue to balance with others
std::cout << m_channelName << '\n'; monwork.push(shared_from_this(), evt);
pvutil_ostream myos(std::cout); } else if(n==0) {
myos << *(element->pvStructurePtr.get()) << "\n\n"; LOG(pva::logLevelDebug, "%s Spurious Data event on channel", mon.name().c_str());
} else {
if(mon.complete())
done();
} }
std::cout.flush();
} }
break;
}
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.flush();
std::cout<< '\n';
} }
}; };
} // namespace } // namespace
#ifndef MAIN
# define MAIN main
#endif
int main (int argc, char *argv[]) int MAIN (int argc, char *argv[])
{ {
int opt; /* getopt() current option */ int opt; /* getopt() current option */
#ifdef PVMONITOR
bool monitor = true;
#else
bool monitor = false; bool monitor = false;
#endif
istream* inputStream = 0;
ifstream ifs;
bool fromStream = false;
epics::RefMonitor refmon; epics::RefMonitor refmon;
double timeOut = -1.0;
bool explicit_timeout = false;
setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */
// ================ Parse Arguments // ================ 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) { switch (opt) {
case 'h': /* Print usage */ case 'h': /* Print usage */
usage(); usage();
return 0; return 0;
case 'v': case 'v':
mode = StructureMode; verbosity++;
break; break;
case 'V': /* Print version */ case 'V': /* Print version */
{ {
Version version("pvget", "cpp", pva::Version version(EXECNAME, "cpp",
EPICS_PVA_MAJOR_VERSION, EPICS_PVA_MAJOR_VERSION,
EPICS_PVA_MINOR_VERSION, EPICS_PVA_MINOR_VERSION,
EPICS_PVA_MAINTENANCE_VERSION, EPICS_PVA_MAINTENANCE_VERSION,
EPICS_PVA_DEVELOPMENT_FLAG); EPICS_PVA_DEVELOPMENT_FLAG);
fprintf(stdout, "%s\n", version.getVersionString().c_str()); fprintf(stdout, "%s\n", version.getVersionString().c_str());
return 0; return 0;
} }
case 'R': case 'R':
refmon.start(5.0); refmon.start(5.0);
break; break;
case 'w': /* Set PVA timeout value */ case 'M':
if((epicsScanDouble(optarg, &timeOut)) != 1 || timeOut <= 0.0) if(strcmp(optarg, "raw")==0) {
{ outmode = pvd::PVStructure::Formatter::Raw;
fprintf(stderr, "'%s' is not a valid timeout value " } else if(strcmp(optarg, "nt")==0) {
"- ignored. ('pvget -h' for help.)\n", optarg); outmode = pvd::PVStructure::Formatter::NT;
} else if(strcmp(optarg, "json")==0) {
outmode = pvd::PVStructure::Formatter::JSON;
} else { } else {
explicit_timeout = true; fprintf(stderr, "Unknown output mode '%s'\n", optarg);
outmode = pvd::PVStructure::Formatter::Raw;
} }
break; 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 */ case 'r': /* Set PVA timeout value */
request = optarg; request = optarg;
// do not override terse mode break;
if (mode == ValueOnlyMode) mode = StructureMode;
break;
case 't': /* Terse mode */ case 't': /* Terse mode */
mode = TerseMode;
break;
case 'i': /* T-types format mode */ case 'i': /* T-types format mode */
formatTTypesFlag = false; case 'F': /* Store this for output formatting */
case 'n':
case 'q': /* Quiet mode */
// deprecate
break; break;
case 'f': /* Use input stream as input */
fprintf(stderr, "Unsupported option -f\n");
return 1;
case 'm': /* Monitor mode */ case 'm': /* Monitor mode */
monitor = true; monitor = true;
break; break;
case 'p': /* Set default provider */ case 'p': /* Set default provider */
defaultProvider = optarg; defaultProvider = optarg;
break; break;
case 'q': /* Quiet mode */
break;
case 'd': /* Debug log level */ case 'd': /* Debug log level */
debugFlag = true; debugFlag = true;
break; break;
case 'c': /* Clean-up and report used instance count */ case 'c': /* Clean-up and report used instance count */
break; 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 '?': case '?':
fprintf(stderr, fprintf(stderr,
"Unrecognized option: '-%c'. ('pvget -h' for help.)\n", "Unrecognized option: '-%c'. ('" EXECNAME " -h' for help.)\n",
optopt); optopt);
return 1; return 1;
case ':': case ':':
fprintf(stderr, fprintf(stderr,
"Option '-%c' requires an argument. ('pvget -h' for help.)\n", "Option '-%c' requires an argument. ('" EXECNAME " -h' for help.)\n",
optopt); optopt);
return 1; return 1;
default : default :
@ -486,162 +362,76 @@ int main (int argc, char *argv[])
} }
} }
if(!explicit_timeout) { if(monitor)
if(monitor) timeout = -1;
timeOut = -1.0; // forever
else
timeOut = 3.0;
}
int nPvs = argc - optind; /* Remaining arg list are PV names */ if(verbosity>0)
if (nPvs > 0) outmode = pvd::PVStructure::Formatter::Raw;
{
// 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;
}
vector<string> pvs; /* Array of PV structures */ pvd::PVStructure::shared_pointer pvRequest;
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;
try { try {
pvRequest = createRequest(request); pvRequest = pvd::createRequest(request);
} catch(std::exception& e){ } catch(std::exception& e){
fprintf(stderr, "failed to parse request string: %s\n", e.what()); fprintf(stderr, "failed to parse request string: %s\n", e.what());
return 1; return 1;
} }
// keep the operations, and associated channels, alive for(int i = optind; i < argc; i++) {
std::vector<std::tr1::shared_ptr<Tracker> > ops; pvnamewidth = std::max(pvnamewidth, strlen(argv[i]));
for(size_t n=0; n<pvs.size(); n++)
{
URI uri;
bool validURI = URI::parse(pvs[n], uri);
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();
}
ChannelProvider::shared_pointer provider(ChannelProviderRegistry::clients()->getProvider(uri.protocol));
if(!provider) {
std::cerr<<"Unknown provider \""<<uri.protocol<<"\" for channel "<<pvs[n]<<"\n";
return 1;
}
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;
}
if(monitor) {
std::tr1::shared_ptr<MonitorRequesterImpl> req(new MonitorRequesterImpl(pvs[n]));
req->op = channel->createMonitor(req, pvRequest);
ops.push_back(req);
} else {
std::tr1::shared_ptr<ChannelGetRequesterImpl> 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);
} }
// Active channels continue to be referenced by get/monitor stored in 'ops' SET_LOG_LEVEL(debugFlag ? pva::logLevelDebug : pva::logLevelError);
chan_cache.clear();
// ========================== Wait for operations to complete, or timeout epics::pvAccess::ca::CAClientFactory::start();
#ifdef USE_SIGNAL
signal(SIGINT, alldone);
signal(SIGTERM, alldone);
signal(SIGQUIT, alldone);
#endif
if(debugFlag)
std::cerr<<"Waiting...\n";
{ {
Guard G(Tracker::doneLock); pvac::ClientProvider provider(defaultProvider);
while(Tracker::inprog.size() && !Tracker::abort) {
UnGuard U(G); std::vector<std::tr1::shared_ptr<Tracker> > tracked;
if(timeOut<=0)
Tracker::doneEvt.wait(); epics::auto_ptr<WorkQueue> Q;
else if(!Tracker::doneEvt.wait(timeOut)) { if(monitor)
allOK = false; Q.reset(new WorkQueue);
std::cerr<<"Timeout\n";
break; for(int i = optind; i < argc; i++) {
pvac::ClientChannel chan(provider.connect(argv[i]));
if(monitor) {
std::tr1::shared_ptr<MonTracker> mon(new MonTracker(*Q, chan, pvRequest));
tracked.push_back(mon);
} else { // Get
std::tr1::shared_ptr<Getter> 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()) { if(refmon.running()) {
refmon.stop(); refmon.stop();
// drop refs to operations, but keep ref to ClientProvider
ops.clear();
// show final counts // show final counts
refmon.current(); refmon.current();
} }
@ -650,5 +440,6 @@ int main (int argc, char *argv[])
if(debugFlag) if(debugFlag)
std::cerr<<"Done\n"; std::cerr<<"Done\n";
return allOK ? 0 : 1;
return haderror ? 1 : 0;
} }

View File

@ -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 <iostream>
#include <pv/pvAccess.h> #include <pva/client.h>
#include <pv/caProvider.h> #include <pv/caProvider.h>
#include <stdio.h> #include <stdio.h>
@ -16,20 +20,12 @@
#include <pv/event.h> #include <pv/event.h>
#include <epicsExit.h> #include <epicsExit.h>
#include "pvutils.cpp" #include "pvutils.h"
using namespace std; namespace pvd = epics::pvData;
namespace TR1 = std::tr1; namespace pva = epics::pvAccess;
using namespace epics::pvData;
using namespace epics::pvAccess;
namespace {
#define DEFAULT_TIMEOUT 3.0
#define DEFAULT_PROVIDER "pva"
double timeOut = DEFAULT_TIMEOUT;
string defaultProvider(DEFAULT_PROVIDER);
const string noAddress;
void usage (void) void usage (void)
{ {
@ -42,31 +38,37 @@ void usage (void)
" -d: Enable debug output\n" " -d: Enable debug output\n"
" -c: Wait for clean shutdown and report used instance count (for expert users)" " -c: Wait for clean shutdown and report used instance count (for expert users)"
"\nExample: pvinfo double01\n\n" "\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 main (int argc, char *argv[])
{ {
int opt; /* getopt() current option */ int opt; /* getopt() current option */
bool debug = false; bool debug = false;
bool cleanupAndReport = false;
setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */
@ -77,7 +79,7 @@ int main (int argc, char *argv[])
return 0; return 0;
case 'V': /* Print version */ case 'V': /* Print version */
{ {
Version version("pvinfo", "cpp", pva::Version version("pvinfo", "cpp",
EPICS_PVA_MAJOR_VERSION, EPICS_PVA_MAJOR_VERSION,
EPICS_PVA_MINOR_VERSION, EPICS_PVA_MINOR_VERSION,
EPICS_PVA_MAINTENANCE_VERSION, EPICS_PVA_MAINTENANCE_VERSION,
@ -86,12 +88,16 @@ int main (int argc, char *argv[])
return 0; return 0;
} }
case 'w': /* Set PVA timeout value */ 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 " fprintf(stderr, "'%s' is not a valid timeout value "
"- ignored. ('pvget -h' for help.)\n", optarg); "- ignored. ('pvget -h' for help.)\n", optarg);
timeOut = DEFAULT_TIMEOUT; } else {
timeout = temp;
} }
}
break; break;
case 'p': /* Set default provider */ case 'p': /* Set default provider */
defaultProvider = optarg; defaultProvider = optarg;
@ -100,7 +106,6 @@ int main (int argc, char *argv[])
debug = true; debug = true;
break; break;
case 'c': /* Clean-up and report used instance count */ case 'c': /* Clean-up and report used instance count */
cleanupAndReport = true;
break; break;
case '?': case '?':
fprintf(stderr, fprintf(stderr,
@ -118,114 +123,41 @@ int main (int argc, char *argv[])
} }
} }
int nPvs = argc - optind; /* Remaining arg list are PV names */ if (argc == optind)
if (nPvs < 1)
{ {
fprintf(stderr, "No pv name(s) specified. ('pvinfo -h' for help.)\n"); fprintf(stderr, "No pv name(s) specified. ('pvinfo -h' for help.)\n");
return 1; return 1;
} }
vector<string> pvs; /* Array of PV names */ SET_LOG_LEVEL(debug ? pva::logLevelDebug : pva::logLevelError);
for (int n = 0; optind < argc; n++, optind++)
pvs.push_back(argv[optind]); /* Copy PV names from command line */
std::vector<GetInfo> 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<std::string> pvNames;
std::vector<std::string> pvAddresses;
std::vector<ChannelProvider::shared_pointer> providers;
pvNames.reserve(nPvs);
pvAddresses.reserve(nPvs);
for (int n = 0; n < nPvs; n++)
{ {
URI uri; pvac::ClientProvider prov(defaultProvider);
bool validURI = URI::parse(pvs[n], uri);
std::string providerName(defaultProvider); for(int i = optind; i<argc; i++) {
std::string pvName(pvs[n]); infos[i-optind].op = prov.connect(argv[i]).info(&infos[i-optind]);
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;
}
} }
// first connect to all, this allows resource (e.g. TCP connection) sharing Tracker::prepare(); // install signal handler
vector<Channel::shared_pointer> channels(nPvs);
for (int n = 0; n < nPvs; n++)
{ {
if(!providers[n]) continue; Guard G(Tracker::doneLock);
channels[n] = providers[n]->createChannel(pvNames[n], DefaultChannelRequester::build(), while(Tracker::inprog.size() && !Tracker::abort) {
ChannelProvider::PRIORITY_DEFAULT, pvAddresses[n]); UnGuard U(G);
} if(timeout<=0)
Tracker::doneEvt.wait();
// for now a simple iterating sync implementation, guarantees order else if(!Tracker::doneEvt.wait(timeout)) {
for (int n = 0; n < nPvs; n++) haderror = 1;
{ std::cerr<<"Timeout\n";
Channel::shared_pointer channel = channels[n]; break;
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) return haderror ? 1 : 0;
{
// TODO implement wait on context
epicsThreadSleep ( 3.0 );
//std::cout << "-----------------------------------------------------------------------" << std::endl;
//epicsExitCallAtExits();
}
return allOK ? 0 : 1;
} }

View File

@ -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> #include <stdio.h>

3
pvtoolsSrc/pvmonitor.cpp Normal file
View File

@ -0,0 +1,3 @@
#define PVMONITOR
#define EXECNAME "pvmonitor"
#include "pvget.cpp"

View File

@ -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 <iostream>
#include <vector> #include <vector>
#include <string> #include <string>
@ -29,30 +33,10 @@
#include <pv/caProvider.h> #include <pv/caProvider.h>
#include "pvutils.cpp" #include "pvutils.h"
using namespace std;
namespace TR1 = std::tr1;
using namespace epics::pvData;
using namespace epics::pvAccess;
namespace { 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) void usage (bool details=false)
{ {
fprintf (stderr, fprintf (stderr,
@ -71,18 +55,16 @@ void usage (bool details=false)
" -v: Print version and exit\n" " -v: Print version and exit\n"
" -r <pv request>: Request, specifies what fields to return and options, default is '%s'\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" " -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" " -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" " -q: Quiet mode, print only error messages\n"
" -d: Enable debug output\n" " -d: Enable debug output\n"
" -F <ofs>: Use <ofs> as an alternate output field separator\n" " Deprecated options:\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"
" default: Auto - try value as enum string, then as index number\n" " default: Auto - try value as enum string, then as index number\n"
" -n: Force enum interpretation of values as numbers\n" " -n, -s, -F, -t: ignored\n"
" -s: Force enum interpretation of values as strings\n" " -f <input file>: error"
, DEFAULT_REQUEST, DEFAULT_TIMEOUT, DEFAULT_PROVIDER); , request.c_str(), timeout, defaultProvider.c_str());
if(details) { if(details) {
fprintf (stderr, fprintf (stderr,
#ifdef USE_JSON #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) std::cout<<pv->stream().format(outmode);
{
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.flush(); 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 struct Putter : public pvac::ClientChannel::PutCallback
{ {
epicsEvent wait; epicsEvent wait;
@ -259,24 +117,22 @@ struct Putter : public pvac::ClientChannel::PutCallback
Putter() :done(false) {} Putter() :done(false) {}
typedef shared_vector<std::string> bare_t; typedef pvd::shared_vector<std::string> bare_t;
bare_t bare; bare_t bare;
typedef std::pair<std::string, std::string> KV_t; typedef std::pair<std::string, std::string> KV_t;
typedef std::vector<KV_t> pairs_t; typedef std::vector<KV_t> pairs_t;
pairs_t pairs; pairs_t pairs;
shared_vector<std::string> jarr; pvd::shared_vector<std::string> jarr;
PVStructure::const_shared_pointer current;
virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args)
{ {
if(debug) std::cerr<<"Server defined structure\n"<<build; if(debugFlag) std::cerr<<"Server defined structure\n"<<build;
PVStructurePtr root(getPVDataCreate()->createPVStructure(build)); pvd::PVStructurePtr root(pvd::getPVDataCreate()->createPVStructure(build));
if(bare.size()==1 && bare[0][0]=='{') { 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 #ifdef USE_JSON
std::istringstream strm(bare[0]); std::istringstream strm(bare[0]);
parseJSON(strm, root, &args.tosend); parseJSON(strm, root, &args.tosend);
@ -284,23 +140,23 @@ struct Putter : public pvac::ClientChannel::PutCallback
#endif #endif
} else if(pairs.empty()) { } 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) if(!fld)
throw std::runtime_error("Structure has no .value"); 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) { if(bare.size()!=1) {
throw std::runtime_error("Can't assign multiple values to scalar"); 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]); sfld->putFrom(bare[0]);
args.tosend.set(sfld->getFieldOffset()); args.tosend.set(sfld->getFieldOffset());
} else if(ftype==scalarArray) { } else if(ftype==pvd::scalarArray) {
PVScalarArray* sfld(static_cast<PVScalarArray*>(fld.get())); pvd::PVScalarArray* sfld(static_cast<pvd::PVScalarArray*>(fld.get()));
// first element is "length" which we ignore for compatibility // first element is "length" which we ignore for compatibility
bare.slice(1); bare.slice(1);
@ -308,19 +164,20 @@ struct Putter : public pvac::ClientChannel::PutCallback
sfld->putFrom(freeze(bare)); sfld->putFrom(freeze(bare));
args.tosend.set(sfld->getFieldOffset()); 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) { if(bare.size()!=1) {
throw std::runtime_error("Can't assign multiple values to enum"); 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()); assert(!!args.previous); // ensure by calling put(..., true) below
PVStringArray::const_svector choices(current->getSubFieldT<PVStringArray>("value.choices")->view()); 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; bool found=false;
for(size_t i=0; i<choices.size(); i++) { for(size_t i=0; i<choices.size(); i++) {
if(bare[0]==choices[i]) { if(bare[0]==choices[i]) {
idxfld->putFrom<int64>(i); idxfld->putFrom<pvd::int64>(i);
found=true; found=true;
break; break;
} }
@ -337,20 +194,20 @@ struct Putter : public pvac::ClientChannel::PutCallback
} }
} else { } 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) 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) { if(!fld) {
fprintf(stderr, "%s : Warning: no such field\n", it->first.c_str()); fprintf(stderr, "%s : Warning: no such field\n", it->first.c_str());
// ignore // ignore
} else if(it->second[0]=='[') { } else if(it->second[0]=='[') {
shared_vector<std::string> arr; pvd::shared_vector<std::string> arr;
jarray(arr, it->second.c_str()); jarray(arr, it->second.c_str());
PVScalarArray* afld(dynamic_cast<PVScalarArray*>(fld.get())); pvd::PVScalarArray* afld(dynamic_cast<pvd::PVScalarArray*>(fld.get()));
if(!afld) { if(!afld) {
fprintf(stderr, "%s : Error not a scalar array field\n", it->first.c_str()); fprintf(stderr, "%s : Error not a scalar array field\n", it->first.c_str());
throw std::runtime_error("Not a scalar array field"); 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"); throw std::runtime_error("JSON support not built");
#endif #endif
} else { } else {
PVScalarPtr sfld(std::tr1::dynamic_pointer_cast<PVScalar>(fld)); pvd::PVScalarPtr sfld(std::tr1::dynamic_pointer_cast<pvd::PVScalar>(fld));
if(!sfld) { if(!sfld) {
fprintf(stderr, "%s : Error: need a scalar field\n", it->first.c_str()); fprintf(stderr, "%s : Error: need a scalar field\n", it->first.c_str());
} else { } else {
@ -378,7 +235,7 @@ struct Putter : public pvac::ClientChannel::PutCallback
} }
args.root = root; args.root = root;
if(debug) if(debugFlag)
std::cout<<"To be sent: "<<args.tosend<<"\n"<<args.root; 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 */ int opt; /* getopt() current option */
bool quiet = false; bool quiet = false;
istream* inputStream = 0;
ifstream ifs;
bool fromStream = false;
setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */ 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 */ 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) { switch (opt) {
case 'h': /* Print usage */ case 'h': /* Print usage */
usage(true); usage(true);
return 0; return 0;
case 'v': case 'v':
mode = StructureMode; outmode = pvd::PVStructure::Formatter::Raw;
break; break;
case 'V': /* Print version */ case 'V': /* Print version */
{ {
Version version("pvput", "cpp", pva::Version version("pvput", "cpp",
EPICS_PVA_MAJOR_VERSION, EPICS_PVA_MAJOR_VERSION,
EPICS_PVA_MINOR_VERSION, EPICS_PVA_MINOR_VERSION,
EPICS_PVA_MAINTENANCE_VERSION, EPICS_PVA_MAINTENANCE_VERSION,
@ -427,24 +280,38 @@ int main (int argc, char *argv[])
fprintf(stdout, "%s\n", version.getVersionString().c_str()); fprintf(stdout, "%s\n", version.getVersionString().c_str());
return 0; 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 */ 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 " fprintf(stderr, "'%s' is not a valid timeout value "
"- ignored. ('pvput -h' for help.)\n", optarg); "- ignored. ('pvput -h' for help.)\n", optarg);
timeOut = DEFAULT_TIMEOUT; } else {
timeout = temp;
} }
}
break; break;
case 'r': /* Set PVA timeout value */ case 'r': /* Set PVA timeout value */
request = optarg; request = optarg;
// do not override terse mode
if (mode == ValueOnlyMode) mode = StructureMode;
break; break;
case 't': /* Terse mode */ case 't': /* Terse mode */
mode = TerseMode; // deprecated
break; break;
case 'd': /* Debug log level */ case 'd': /* Debug log level */
debug = true; debugFlag = true;
break; break;
case 'p': /* Set default provider */ case 'p': /* Set default provider */
defaultProvider = optarg; defaultProvider = optarg;
@ -453,35 +320,13 @@ int main (int argc, char *argv[])
quiet = true; quiet = true;
break; break;
case 'F': /* Store this for output formatting */ case 'F': /* Store this for output formatting */
fieldSeparator = (char) *optarg;
break; break;
case 'f': /* Use input stream as input */ case 'f': /* Use input stream as input */
{ fprintf(stderr, "Unsupported option -f\n");
string fileName = optarg; return 1;
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': case 'n':
enumMode = NumberEnum;
break; break;
case 's': case 's':
enumMode = StringEnum;
break; break;
case '?': case '?':
fprintf(stderr, fprintf(stderr,
@ -504,62 +349,22 @@ int main (int argc, char *argv[])
fprintf(stderr, "No pv name specified. ('pvput -h' for help.)\n"); fprintf(stderr, "No pv name specified. ('pvput -h' for help.)\n");
return 1; return 1;
} }
string pv = argv[optind++]; std::string pv = argv[optind++];
URI uri; std::string providerName(defaultProvider);
bool validURI = URI::parse(pv, uri); std::string pvName(pv);
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;
}
int nVals = argc - optind; /* Remaining arg list are PV names */ int nVals = argc - optind; /* Remaining arg list are PV names */
if (nVals > 0) if (nVals < 1)
{
// do not allow reading file and command line specified pvs
fromStream = false;
}
else if (nVals < 1 && !fromStream)
{ {
fprintf(stderr, "No value(s) specified. ('pvput -h' for help.)\n"); fprintf(stderr, "No value(s) specified. ('pvput -h' for help.)\n");
return 1; return 1;
} }
vector<string> values; std::vector<std::string> values;
if (fromStream) // copy values from command line
{ for (int n = 0; optind < argc; n++, optind++)
string cn; values.push_back(argv[optind]);
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;
}
Putter thework; Putter thework;
@ -597,15 +402,15 @@ int main (int argc, char *argv[])
thework.bare.clear(); thework.bare.clear();
} }
PVStructure::shared_pointer pvRequest; pvd::PVStructure::shared_pointer pvRequest;
try { try {
pvRequest = createRequest(request); pvRequest = pvd::createRequest(request);
} catch(std::exception& e){ } catch(std::exception& e){
fprintf(stderr, "failed to parse request string: %s\n", e.what()); fprintf(stderr, "failed to parse request string: %s\n", e.what());
return 1; return 1;
} }
SET_LOG_LEVEL(debug ? logLevelDebug : logLevelError); SET_LOG_LEVEL(debugFlag ? pva::logLevelDebug : pva::logLevelError);
std::cout << std::boolalpha; std::cout << std::boolalpha;
@ -615,20 +420,18 @@ int main (int argc, char *argv[])
pvac::ClientChannel chan(ctxt.connect(pvName)); pvac::ClientChannel chan(ctxt.connect(pvName));
thework.current = chan.get(timeOut, pvRequest); if (!quiet) {
if (mode != TerseMode && !quiet) {
std::cout << "Old : "; 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); epicsGuard<epicsMutex> G(thework.lock);
while(!thework.done) { while(!thework.done) {
epicsGuardRelease<epicsMutex> U(G); epicsGuardRelease<epicsMutex> U(G);
if(!thework.wait.wait(timeOut)) { if(!thework.wait.wait(timeout)) {
fprintf(stderr, "Put timeout\n"); fprintf(stderr, "Put timeout\n");
return 1; return 1;
} }
@ -639,10 +442,10 @@ int main (int argc, char *argv[])
fprintf(stderr, "Error: %s\n", thework.message.c_str()); fprintf(stderr, "Error: %s\n", thework.message.c_str());
} }
if (mode != TerseMode && !quiet) { if (!quiet) {
std::cout << "New : "; std::cout << "New : ";
} }
printValue(pvName, chan.get(timeOut, pvRequest)); printValue(pvName, chan.get(timeout, pvRequest));
return thework.result!=pvac::PutEvent::Success; return thework.result!=pvac::PutEvent::Success;
} }

View File

@ -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> #include <iostream>
@ -12,466 +20,109 @@
#include <pv/logger.h> #include <pv/logger.h>
#include <pv/pvTimeStamp.h> #include <pv/pvTimeStamp.h>
using namespace std; #include "pvutils.h"
namespace TR1 = std::tr1;
using namespace epics::pvData; double timeout = 5.0;
using namespace epics::pvAccess; 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; (void)num;
o << '[' << Status::StatusTypeName[s.getType()] << "] "; Tracker::abort = true;
string msg = s.getMessage(); Tracker::doneEvt.signal();
if (!msg.empty()) }
{ #endif
o << msg;
} void Tracker::prepare()
else {
{ #ifdef USE_SIGNAL
o << "(no error message)"; signal(SIGINT, alldone);
} signal(SIGTERM, alldone);
// dump stack trace only if on debug mode signal(SIGQUIT, alldone);
if (IS_LOGGABLE(logLevelDebug)) #endif
{ }
string sd = s.getStackDump();
if (!sd.empty()) static
{ void early(const char *inp, unsigned pos)
o << std::endl << sd; {
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<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");
} }
}
return o;
}
char fieldSeparator = ' '; for(; *inp==' '; inp++) {} // skip trailing whitespace
char arrayCountFlag = true; if(*inp==',') inp++;
else if(*inp==']') break;
EnumMode enumMode = AutoEnum; else {
fprintf(stderr, "Unknown token '%c' in \"%s\"", *inp, inp);
bool formatTTypesFlag = true; throw std::runtime_error("Unknown token");
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());
}
}
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;
} }

View File

@ -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/event.h>
#include <pv/pvData.h> #include <pv/pvData.h>
#include <pv/pvAccess.h> #include <pv/pvAccess.h>
/// terse mode functions typedef epicsGuard<epicsMutex> Guard;
void convertStructure(std::string* buffer, epics::pvData::PVStructure *data, int notFirst); typedef epicsGuardRelease<epicsMutex> UnGuard;
void convertArray(std::string*, epics::pvData::PVScalarArray * pv, int notFirst); namespace pvd = epics::pvData;
void convertStructureArray(std::string*, epics::pvData::PVStructureArray * pvdata, int notFirst); 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 };
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 <string>
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);
};
struct dump_stack_only_on_debug extern double timeout;
{ extern bool debugFlag;
const epics::pvData::Status &status;
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<Tracker*> inprog_t;
static inprog_t inprog;
static bool abort;
Tracker()
#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() Guard G(doneLock);
<< ' ' << value->getFieldName() << ' '; //" # "; inprog.insert(this);
formatTType(o.strm, value);
o << std::endl;
//dumpPVStructure(o, *value, false);
return o;
} }
~Tracker()
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); done();
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];
} }
void 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)
{ {
epics::pvData::format::indent_scope s(o.strm); {
Guard G(doneLock);
epics::pvData::PVUnionArray::const_svector data(value->view()); inprog.erase(this);
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];
}
} }
doneEvt.signal();
} }
return o;
}
template <> static void prepare();
inline pvutil_ostream& };
operator<<(pvutil_ostream& o, const epics::pvData::PVStructure& value)
{
return dumpPVStructure(o, value, true);
}
void jarray(pvd::shared_vector<std::string>& out, const char *inp);
#endif /* PVUTILS_H */