diff --git a/examples/miniput.cpp b/examples/miniput.cpp index a360e6e..2991e73 100644 --- a/examples/miniput.cpp +++ b/examples/miniput.cpp @@ -22,7 +22,9 @@ int main(int argc, char *argv[]) std::cout<<"Before "<(argv[2]); + channel.put() + .set("value", argv[2]) + .exec(); std::cout<<"After "<createPVStructure(build)); - pvd::PVScalarPtr value(root->getSubField("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 arr; - - PutValArray(const pvd::shared_vector& 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("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(ftype.get()); + pvd::PVUnion *ufld = static_cast(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(idx)->putFrom(it->value); + } + + } else if(ftype->getType()==pvd::scalar) { + static_cast(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(ftype.get()); + pvd::PVUnion *ufld = static_cast(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(idx)->putFrom(it->value); + } + + } else if(ftype->getType()==pvd::scalarArray) { + static_cast(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 -ClientChannel::putValue(const epics::pvData::shared_vector& 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); -} +} // namespace detail struct MonitorSync::SImpl : public ClientChannel::MonitorCallback { diff --git a/src/client/pva/client.h b/src/client/pva/client.h index ff5df34..19d3d38 100644 --- a/src/client/pva/client.h +++ b/src/client/pva/client.h @@ -6,6 +6,7 @@ #define PVATESTCLIENT_H #include +#include #include @@ -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 - inline void putValue(typename epics::pvData::meta::arg_type::type>::type value, - double timeout = 3.0, - epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer()) - { - putValue(static_cast(&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& 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 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 + 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 > scalars_t; + scalars_t scalars; + + typedef std::list > > 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 + 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& value, bool required=true) { + arrays.push_back(arrays_t::value_type(name, value, required)); + return *this; + } + template + PutBuilder& set(const std::string& name, const epics::pvData::shared_vector& value, bool required=true) { + return set(name, epics::pvData::static_shared_vector_cast(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