pvac: generalize sync. put()

This commit is contained in:
Michael Davidsaver
2017-09-30 12:18:26 -05:00
parent 9460fab294
commit b806b7ca42
3 changed files with 222 additions and 96 deletions

View File

@ -22,7 +22,9 @@ int main(int argc, char *argv[])
std::cout<<"Before "<<channel.name()<<" : "<<channel.get()<<"\n";
channel.putValue<epics::pvData::pvString>(argv[2]);
channel.put()
.set("value", argv[2])
.exec();
std::cout<<"After "<<channel.name()<<" : "<<channel.get()<<"\n";

View File

@ -71,12 +71,19 @@ pvac::ClientChannel::get(double timeout,
pvd::PVStructure::const_shared_pointer pvRequest)
{
GetWait waiter;
{
Operation op(get(&waiter, pvRequest));
waiter.wait(timeout);
if(waiter.result.event==pvac::GetEvent::Success)
}
switch(waiter.result.event) {
case GetEvent::Success:
return waiter.result.value;
else
case GetEvent::Fail:
throw std::runtime_error(waiter.result.message);
default:
case GetEvent::Cancel: // cancel implies timeout, which should already be thrown
THROW_EXCEPTION2(std::logic_error, "Cancelled!?!?");
}
}
pvd::PVStructure::const_shared_pointer
@ -126,82 +133,151 @@ struct PutValCommon : public pvac::ClientChannel::PutCallback,
}
};
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) OVERRIDE FINAL
{
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) OVERRIDE FINAL
{
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,
const epics::pvData::PVStructure::const_shared_pointer &pvRequest)
namespace detail {
struct PutBuilder::Exec : public pvac::ClientChannel::PutCallback,
public WaitCommon
{
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);
detail::PutBuilder& builder;
pvac::PutEvent result;
Exec(detail::PutBuilder& builder)
:builder(builder)
{}
virtual ~Exec() {}
virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) OVERRIDE FINAL
{
pvd::PVDataCreatePtr create(pvd::getPVDataCreate());
pvd::PVStructurePtr root(create->createPVStructure(build));
for(PutBuilder::scalars_t::const_iterator it = builder.scalars.begin(), end = builder.scalars.end();
it!=end; ++it)
{
if(it->value.empty())
continue;
pvd::PVFieldPtr fld(root->getSubField(it->name));
if(!fld && it->required)
throw std::runtime_error(std::string("Server does not have required field ")+it->name);
else if(!fld)
continue; // !it->required
const pvd::FieldConstPtr& ftype(fld->getField());
if(ftype->getType()==pvd::union_) {
const pvd::Union *utype = static_cast<const pvd::Union*>(ftype.get());
pvd::PVUnion *ufld = static_cast<pvd::PVUnion*>(fld.get());
if(utype->isVariant()) {
pvd::PVScalarPtr scalar(create->createPVScalar(it->value.type()));
scalar->putFrom(it->value);
ufld->set(scalar);
} else {
// attempt automagic assignment to descriminating union
pvd::int32 idx = utype->guess(pvd::scalar, it->value.type());
if(idx==-1)
throw std::runtime_error(std::string("Unable to descriminate union field ")+it->name);
ufld->select<pvd::PVScalar>(idx)->putFrom(it->value);
}
void
ClientChannel::putValue(const epics::pvData::shared_vector<const void>& value,
double timeout,
const 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);
} else if(ftype->getType()==pvd::scalar) {
static_cast<pvd::PVScalar*>(fld.get())->putFrom(it->value);
} else {
throw std::runtime_error(std::string("Type mis-match assigning scalar to field ")+it->name);
}
args.tosend.set(fld->getFieldOffset());
}
for(PutBuilder::arrays_t::const_iterator it = builder.arrays.begin(), end = builder.arrays.end();
it!=end; ++it)
{
if(it->value.empty())
continue;
pvd::PVFieldPtr fld(root->getSubField(it->name));
if(!fld && it->required)
throw std::runtime_error(std::string("Server does not have required field ")+it->name);
else if(!fld)
continue; // !it->required
const pvd::FieldConstPtr& ftype(fld->getField());
if(ftype->getType()==pvd::union_) {
const pvd::Union *utype = static_cast<const pvd::Union*>(ftype.get());
pvd::PVUnion *ufld = static_cast<pvd::PVUnion*>(fld.get());
if(utype->isVariant()) {
pvd::PVScalarArrayPtr scalar(create->createPVScalarArray(it->value.original_type()));
scalar->putFrom(it->value);
ufld->set(scalar);
} else {
// attempt automagic assignment to descriminating union
pvd::int32 idx = utype->guess(pvd::scalarArray, it->value.original_type());
if(idx==-1)
throw std::runtime_error(std::string("Unable to descriminate union field ")+it->name);
ufld->select<pvd::PVScalarArray>(idx)->putFrom(it->value);
}
} else if(ftype->getType()==pvd::scalarArray) {
static_cast<pvd::PVScalarArray*>(fld.get())->putFrom(it->value);
// TODO
} else {
throw std::runtime_error(std::string("Type mis-match assigning scalar to field ")+it->name);
}
args.tosend.set(fld->getFieldOffset());
}
args.root = root;
}
virtual void putDone(const PutEvent& evt) OVERRIDE FINAL
{
{
Guard G(mutex);
if(done) {
LOG(pva::logLevelWarn, "oops, double event to PutCallback");
} else {
result = evt;
done = true;
}
}
event.signal();
}
};
void PutBuilder::exec(double timeout)
{
Exec work(*this);
{
Operation op(channel.put(&work, request));
work.wait(timeout);
}
switch(work.result.event) {
case PutEvent::Success: return;
case PutEvent::Fail:
throw std::runtime_error(work.result.message);
case PutEvent::Cancel:
THROW_EXCEPTION2(std::logic_error, "Cancelled!?!");
}
}
} // namespace detail
struct MonitorSync::SImpl : public ClientChannel::MonitorCallback
{
const bool ourevent;

View File

@ -6,6 +6,7 @@
#define PVATESTCLIENT_H
#include <stdexcept>
#include <list>
#include <epicsMutex.h>
@ -176,6 +177,10 @@ struct Timeout : public std::runtime_error
Timeout();
};
namespace detail {
class PutBuilder;
}
/** Represents a single channel
*
* This class has two sets of methods, those which block for completion, and
@ -282,27 +287,9 @@ 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(static_cast<const void*>(&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 = 3.0,
const epics::pvData::PVStructure::const_shared_pointer& pvRequest = epics::pvData::PVStructure::const_shared_pointer());
//! Put to the 'value' field and block until complete.
//! Accepts scalar array
void putValue(const epics::pvData::shared_vector<const void>& value,
double timeout = 3.0,
const epics::pvData::PVStructure::const_shared_pointer& pvRequest = epics::pvData::PVStructure::const_shared_pointer());
//! Synchronious put operation
inline
detail::PutBuilder put(const epics::pvData::PVStructure::const_shared_pointer &pvRequest = epics::pvData::PVStructure::const_shared_pointer());
//! Monitor event notification
struct MonitorCallback {
@ -349,6 +336,59 @@ private:
std::tr1::shared_ptr<epics::pvAccess::Channel> getChannel();
};
namespace detail {
//! Helper to accumulate values to for a Put operation.
//! Make sure to call exec() to begin operation.
class epicsShareClass PutBuilder {
ClientChannel& channel;
epics::pvData::PVStructure::const_shared_pointer request;
template<typename V>
struct triple {
std::string name;
bool required;
V value;
triple(const std::string& name, const V& value, bool required =true)
:name(name), required(required), value(value)
{}
};
typedef std::list<triple<epics::pvData::AnyScalar> > scalars_t;
scalars_t scalars;
typedef std::list<triple<epics::pvData::shared_vector<const void> > > arrays_t;
arrays_t arrays;
struct Exec;
friend class pvac::ClientChannel;
PutBuilder(ClientChannel& channel, const epics::pvData::PVStructure::const_shared_pointer& request)
:channel(channel), request(request)
{}
public:
PutBuilder& set(const std::string& name, const epics::pvData::AnyScalar& value, bool required=true) {
scalars.push_back(scalars_t::value_type(name, value, required));
return *this;
}
template<typename T>
PutBuilder& set(const std::string& name, T value, bool required=true) {
return set(name, epics::pvData::AnyScalar(value), required);
}
PutBuilder& set(const std::string& name, const epics::pvData::shared_vector<const void>& value, bool required=true) {
arrays.push_back(arrays_t::value_type(name, value, required));
return *this;
}
template<typename T>
PutBuilder& set(const std::string& name, const epics::pvData::shared_vector<const T>& value, bool required=true) {
return set(name, epics::pvData::static_shared_vector_cast<const void>(value), required);
}
void exec(double timeout=3.0);
};
}// namespace detail
//! Central client context.
class epicsShareClass ClientProvider
{
@ -385,6 +425,14 @@ public:
void disconnect();
};
detail::PutBuilder
ClientChannel::put(const epics::pvData::PVStructure::const_shared_pointer& pvRequest)
{
return detail::PutBuilder(*this, pvRequest);
}
//! @}
}//namespace pvac