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"; 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"; 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) pvd::PVStructure::const_shared_pointer pvRequest)
{ {
GetWait waiter; GetWait waiter;
{
Operation op(get(&waiter, pvRequest)); Operation op(get(&waiter, pvRequest));
waiter.wait(timeout); waiter.wait(timeout);
if(waiter.result.event==pvac::GetEvent::Success) }
switch(waiter.result.event) {
case GetEvent::Success:
return waiter.result.value; return waiter.result.value;
else case GetEvent::Fail:
throw std::runtime_error(waiter.result.message); 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 pvd::PVStructure::const_shared_pointer
@ -126,81 +133,150 @@ 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 } //namespace
void namespace detail {
ClientChannel::putValue(const void* value,
pvd::ScalarType vtype, struct PutBuilder::Exec : public pvac::ClientChannel::PutCallback,
double timeout, public WaitCommon
const epics::pvData::PVStructure::const_shared_pointer &pvRequest)
{ {
PutValScalar waiter(value, vtype); detail::PutBuilder& builder;
Operation op(put(&waiter, pvRequest)); pvac::PutEvent result;
waiter.wait(timeout);
if(waiter.result.event==PutEvent::Success) Exec(detail::PutBuilder& builder)
return; :builder(builder)
else {}
throw std::runtime_error(waiter.result.message); 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);
}
} 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!?!");
}
} }
void } // namespace detail
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);
}
struct MonitorSync::SImpl : public ClientChannel::MonitorCallback struct MonitorSync::SImpl : public ClientChannel::MonitorCallback
{ {

View File

@ -6,6 +6,7 @@
#define PVATESTCLIENT_H #define PVATESTCLIENT_H
#include <stdexcept> #include <stdexcept>
#include <list>
#include <epicsMutex.h> #include <epicsMutex.h>
@ -176,6 +177,10 @@ struct Timeout : public std::runtime_error
Timeout(); Timeout();
}; };
namespace detail {
class PutBuilder;
}
/** Represents a single channel /** Represents a single channel
* *
* This class has two sets of methods, those which block for completion, and * This class has two sets of methods, those which block for completion, and
@ -282,27 +287,9 @@ public:
Operation put(PutCallback* cb, Operation put(PutCallback* cb,
epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer()); epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer());
//! Put to the 'value' field and block until complete. //! Synchronious put operation
//! Accepts a scalar value inline
template<epics::pvData::ScalarType ID> detail::PutBuilder put(const epics::pvData::PVStructure::const_shared_pointer &pvRequest = epics::pvData::PVStructure::const_shared_pointer());
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());
//! Monitor event notification //! Monitor event notification
struct MonitorCallback { struct MonitorCallback {
@ -349,6 +336,59 @@ private:
std::tr1::shared_ptr<epics::pvAccess::Channel> getChannel(); 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. //! Central client context.
class epicsShareClass ClientProvider class epicsShareClass ClientProvider
{ {
@ -385,6 +425,14 @@ public:
void disconnect(); void disconnect();
}; };
detail::PutBuilder
ClientChannel::put(const epics::pvData::PVStructure::const_shared_pointer& pvRequest)
{
return detail::PutBuilder(*this, pvRequest);
}
//! @} //! @}
}//namespace pvac }//namespace pvac