pvac: generalize sync. put()
This commit is contained in:
@ -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";
|
||||||
|
|
||||||
|
@ -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));
|
{
|
||||||
waiter.wait(timeout);
|
Operation op(get(&waiter, pvRequest));
|
||||||
if(waiter.result.event==pvac::GetEvent::Success)
|
waiter.wait(timeout);
|
||||||
|
}
|
||||||
|
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
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user