pvac: add synchronous put to 'value' field

This commit is contained in:
Michael Davidsaver
2017-07-18 14:02:14 +02:00
parent 43019344d2
commit 1a47e5fb0a
6 changed files with 196 additions and 13 deletions

View File

@ -9,6 +9,15 @@ The shortest possible PVA get() example.
*/
/**
@page examples_miniput Simple Client Put Example
The shortest possible PVA put() example.
@include miniput.cpp
*/
/**
@page examples_getme Client Get Example
This example demonstrates a client which issues a Get operation on startup,

View File

@ -15,6 +15,7 @@
- Simplest (shortest) possible
- @ref examples_miniget
- @ref examples_miniput
- More complete
- @ref examples_getme
- @ref examples_putme

View File

@ -10,6 +10,7 @@ TESTPROD_HOST += monitorme
TESTPROD_HOST += spamme
TESTPROD_HOST += miniget
TESTPROD_HOST += miniput
include $(TOP)/configure/RULES
#----------------------------------------

33
examples/miniput.cpp Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
// The simplest possible PVA put
#include <iostream>
#include "pva/client.h"
int main(int argc, char *argv[])
{
try {
if(argc<=2) {
std::cerr<<"Usage: "<<argv[0]<<" <pvname> <value>\n";
return 1;
}
pvac::ClientProvider provider("pva");
pvac::ClientChannel channel(provider.connect(argv[1]));
std::cout<<"Before "<<channel.name()<<" : "<<channel.get()<<"\n";
channel.putValue<epics::pvData::pvString>(argv[2]);
std::cout<<"After "<<channel.name()<<" : "<<channel.get()<<"\n";
}catch(std::exception& e){
std::cerr<<"Error: "<<e.what()<<"\n";
return 1;
}
}

View File

@ -22,14 +22,31 @@ typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
namespace {
struct GetWait : pvac::ClientChannel::GetCallback
struct WaitCommon
{
epicsMutex mutex;
epicsEvent event;
bool done;
WaitCommon() :done(false) {}
void wait(double timeout)
{
Guard G(mutex);
while(!done) {
UnGuard U(G);
if(!event.wait(timeout)) {
throw pvac::Timeout();
}
}
}
};
struct GetWait : public pvac::ClientChannel::GetCallback,
public WaitCommon
{
pvac::GetEvent result;
GetWait() :done(false) {}
GetWait() {}
virtual ~GetWait() {}
virtual void getDone(const pvac::GetEvent& evt)
{
@ -55,16 +72,7 @@ pvac::ClientChannel::get(double timeout,
{
GetWait waiter;
Operation op(get(&waiter, pvRequest));
{
Guard G(waiter.mutex);
while(!waiter.done) {
UnGuard U(G);
if(!waiter.event.wait(timeout)) {
op.cancel();
throw Timeout();
}
}
}
waiter.wait(timeout);
if(waiter.result.event==pvac::GetEvent::Success)
return waiter.result.value;
else
@ -94,4 +102,105 @@ pvac::ClientChannel::rpc(double timeout,
throw std::runtime_error(waiter.result.message);
}
namespace {
struct PutValCommon : public pvac::ClientChannel::PutCallback,
public WaitCommon
{
pvac::PutEvent result;
PutValCommon() {}
virtual ~PutValCommon() {}
virtual void putDone(const PutEvent& evt)
{
{
Guard G(mutex);
if(done) {
LOG(pva::logLevelWarn, "oops, double event to PutCallback");
} else {
result = evt;
done = true;
}
}
event.signal();
}
};
struct PutValScalar : public PutValCommon
{
const void* value;
pvd::ScalarType vtype;
PutValScalar(const void* value, pvd::ScalarType vtype) :value(value), vtype(vtype) {}
virtual ~PutValScalar() {}
virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args)
{
pvd::PVStructurePtr root(pvd::getPVDataCreate()->createPVStructure(build));
pvd::PVScalarPtr value(root->getSubField<pvd::PVScalar>("value"));
if(value) {
value->putFrom(this->value, vtype);
args.tosend.set(value->getFieldOffset());
} else {
// TODO: handle enum
throw std::runtime_error("PV has no scalar 'value' sub-field");
}
args.root = root;
}
};
struct PutValArray : public PutValCommon
{
pvd::shared_vector<const void> arr;
PutValArray(const pvd::shared_vector<const void>& arr) :arr(arr) {}
virtual ~PutValArray() {}
virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args)
{
pvd::PVStructurePtr root(pvd::getPVDataCreate()->createPVStructure(build));
pvd::PVScalarArrayPtr value(root->getSubField<pvd::PVScalarArray>("value"));
if(value) {
value->putFrom(arr);
args.tosend.set(value->getFieldOffset());
} else {
throw std::runtime_error("PV has no scalar array 'value' sub-field");
}
args.root = root;
}
};
} //namespace
void
ClientChannel::putValue(const void* value,
pvd::ScalarType vtype,
double timeout,
pvd::PVStructure::const_shared_pointer pvRequest)
{
PutValScalar waiter(value, vtype);
Operation op(put(&waiter, pvRequest));
waiter.wait(timeout);
if(waiter.result.event==PutEvent::Success)
return;
else
throw std::runtime_error(waiter.result.message);
}
void
ClientChannel::putValue(const epics::pvData::shared_vector<const void>& value,
double timeout,
epics::pvData::PVStructure::const_shared_pointer pvRequest)
{
PutValArray waiter(value);
Operation op(put(&waiter, pvRequest));
waiter.wait(timeout);
if(waiter.result.event==PutEvent::Success)
return;
else
throw std::runtime_error(waiter.result.message);
}
}//namespace pvac

View File

@ -135,7 +135,15 @@ struct epicsShareClass Timeout : public std::runtime_error
Timeout();
};
//! Represents a single channel
/** Represents a single channel
*
* This class has two sets of methods, those which block for completion, and
* those which use callbacks to signal completion.
*
* Those which block accept a 'timeout' argument (in seconds).
*
* Those which use callbacks accept a 'cb' argument and return an Operation or Monitor handle object.
*/
class epicsShareClass ClientChannel
{
struct Impl;
@ -233,6 +241,28 @@ public:
Operation put(PutCallback* cb,
epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
//! Put to the 'value' field and block until complete.
//! Accepts a scalar value
template<epics::pvData::ScalarType ID>
inline void putValue(typename epics::pvData::meta::arg_type<typename epics::pvData::ScalarTypeTraits<ID>::type>::type value,
double timeout = 3.0,
epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer())
{
putValue(&value, ID, timeout, pvRequest);
}
//! Put to the 'value' field and block until complete.
//! Accepts untyped scalar value
void putValue(const void* value, epics::pvData::ScalarType vtype,
double timeout,
epics::pvData::PVStructure::const_shared_pointer pvRequest);
//! Put to the 'value' field and block until complete.
//! Accepts scalar array
void putValue(const epics::pvData::shared_vector<const void>& value,
double timeout,
epics::pvData::PVStructure::const_shared_pointer pvRequest);
//! Monitor event notification
struct epicsShareClass MonitorCallback {
virtual ~MonitorCallback() {}