diff --git a/rtt/SendHandle.hpp b/rtt/SendHandle.hpp index c783ea770..acb1633c4 100644 --- a/rtt/SendHandle.hpp +++ b/rtt/SendHandle.hpp @@ -121,7 +121,7 @@ namespace RTT /** * Collect this operator if the method has no arguments. */ - SendStatus collect() + SendStatus collect() const { if (this->impl) return this->impl->collect(); diff --git a/rtt/internal/AssignCommand.hpp b/rtt/internal/AssignCommand.hpp index a33953742..161b0f06c 100644 --- a/rtt/internal/AssignCommand.hpp +++ b/rtt/internal/AssignCommand.hpp @@ -62,32 +62,33 @@ namespace RTT { public: typedef typename AssignableDataSource::shared_ptr LHSSource; - typedef typename DataSource::const_ptr RHSSource; + typedef typename DataSource::shared_ptr RHSSource; private: LHSSource lhs; RHSSource rhs; - bool news; public: /** * Assign \a r (rvalue) to \a l (lvalue); */ AssignCommand( LHSSource l, RHSSource r ) - : lhs( l ), rhs( r ), news(false) + : lhs( l ), rhs( r ) { } void readArguments() { - news = rhs->evaluate(); } bool execute() { - if (news) { - lhs->set( rhs->rvalue() ); - news=false; - return true; - } - return false; + // we ignore evaluate, which return value is legacy + // we always assign the current state of rhs + rhs->evaluate(); + lhs->set( rhs->rvalue() ); + return true; + } + + void reset() { + rhs->reset(); } virtual base::ActionInterface* clone() const diff --git a/rtt/internal/CollectSignature.hpp b/rtt/internal/CollectSignature.hpp index 0ba0ee69b..edb023676 100644 --- a/rtt/internal/CollectSignature.hpp +++ b/rtt/internal/CollectSignature.hpp @@ -121,14 +121,14 @@ namespace RTT CollectSignature(ToCollect implementation) : cimpl(implementation) {} ~CollectSignature() {} - SendStatus collect() + SendStatus collect() const { if (this->cimpl) return this->cimpl->collect(); return SendFailure; } - SendStatus collectIfDone() + SendStatus collectIfDone() const { if (this->cimpl) return this->cimpl->collectIfDone(); @@ -154,14 +154,14 @@ namespace RTT /** * Collect this operator if the method has one argument. */ - SendStatus collect(arg1_type a1) + SendStatus collect(arg1_type a1) const { if (cimpl) return cimpl->collect( a1 ); return SendFailure; } - SendStatus collectIfDone(arg1_type a1) + SendStatus collectIfDone(arg1_type a1) const { if (cimpl) return cimpl->collectIfDone( a1 ); @@ -184,14 +184,14 @@ namespace RTT /** * Collect this operator if the method has two arguments. */ - SendStatus collect(arg1_type t1, arg2_type t2) + SendStatus collect(arg1_type t1, arg2_type t2) const { if (cimpl) return cimpl->collect(t1, t2); return SendFailure; } - SendStatus collectIfDone(arg1_type t1, arg2_type t2) + SendStatus collectIfDone(arg1_type t1, arg2_type t2) const { if (cimpl) return cimpl->collectIfDone(t1, t2); @@ -215,14 +215,14 @@ namespace RTT /** * Collect this operator if the method has three arguments. */ - SendStatus collect(arg1_type t1, arg2_type t2, arg3_type t3) + SendStatus collect(arg1_type t1, arg2_type t2, arg3_type t3) const { if (cimpl) return cimpl->collect(t1, t2, t3); return SendFailure; } - SendStatus collectIfDone(arg1_type t1, arg2_type t2, arg3_type t3) + SendStatus collectIfDone(arg1_type t1, arg2_type t2, arg3_type t3) const { if (cimpl) return cimpl->collectIfDone(t1, t2, t3); @@ -247,14 +247,14 @@ namespace RTT /** * Collect this operator if the method has four arguments. */ - SendStatus collect(arg1_type t1, arg2_type t2, arg3_type t3, arg4_type t4) + SendStatus collect(arg1_type t1, arg2_type t2, arg3_type t3, arg4_type t4) const { if (cimpl) return cimpl->collect(t1, t2, t3, t4); return SendFailure; } - SendStatus collectIfDone(arg1_type t1, arg2_type t2, arg3_type t3, arg4_type t4) + SendStatus collectIfDone(arg1_type t1, arg2_type t2, arg3_type t3, arg4_type t4) const { if (cimpl) return cimpl->collectIfDone(t1, t2, t3, t4); @@ -280,14 +280,14 @@ namespace RTT /** * Collect this operator if the method has four arguments. */ - SendStatus collect(arg1_type t1, arg2_type t2, arg3_type t3, arg4_type t4, arg5_type t5) + SendStatus collect(arg1_type t1, arg2_type t2, arg3_type t3, arg4_type t4, arg5_type t5) const { if (cimpl) return cimpl->collect(t1, t2, t3, t4, t5); return SendFailure; } - SendStatus collectIfDone(arg1_type t1, arg2_type t2, arg3_type t3, arg4_type t4, arg5_type t5) + SendStatus collectIfDone(arg1_type t1, arg2_type t2, arg3_type t3, arg4_type t4, arg5_type t5) const { if (cimpl) return cimpl->collectIfDone(t1, t2, t3, t4, t5); @@ -314,14 +314,14 @@ namespace RTT /** * Collect this operator if the method has four arguments. */ - SendStatus collect(arg1_type t1, arg2_type t2, arg3_type t3, arg4_type t4, arg5_type t5, arg6_type t6) + SendStatus collect(arg1_type t1, arg2_type t2, arg3_type t3, arg4_type t4, arg5_type t5, arg6_type t6) const { if (cimpl) return cimpl->collect(t1, t2, t3, t4, t5, t6); return SendFailure; } - SendStatus collectIfDone(arg1_type t1, arg2_type t2, arg3_type t3, arg4_type t4, arg5_type t5, arg6_type t6) + SendStatus collectIfDone(arg1_type t1, arg2_type t2, arg3_type t3, arg4_type t4, arg5_type t5, arg6_type t6) const { if (cimpl) return cimpl->collectIfDone(t1, t2, t3, t4, t5, t6); diff --git a/rtt/internal/DataSources.hpp b/rtt/internal/DataSources.hpp index d2f6ae250..38a69d6c3 100644 --- a/rtt/internal/DataSources.hpp +++ b/rtt/internal/DataSources.hpp @@ -520,7 +520,6 @@ namespace RTT // call alias->get() with alias->evaluate(). action->readArguments(); bool r = action->execute(); - action->reset(); // alias may only be evaluated after action was executed. alias->evaluate(); return r; @@ -530,7 +529,6 @@ namespace RTT { action->readArguments(); action->execute(); - action->reset(); return alias->get(); } @@ -544,7 +542,7 @@ namespace RTT return alias->rvalue(); } - virtual void reset() { alias->reset(); } + virtual void reset() { action->reset(); alias->reset(); } virtual ActionAliasDataSource* clone() const { return new ActionAliasDataSource(action, alias.get()); @@ -580,7 +578,6 @@ namespace RTT // call alias->get() with alias->evaluate(). action->readArguments(); bool r = action->execute(); - action->reset(); // alias may only be evaluated after action was executed. alias->evaluate(); return r; @@ -590,7 +587,6 @@ namespace RTT { action->readArguments(); action->execute(); - action->reset(); return alias->get(); } @@ -613,7 +609,7 @@ namespace RTT return alias->rvalue(); } - virtual void reset() { alias->reset(); } + virtual void reset() { action->reset(); alias->reset(); } virtual ActionAliasAssignableDataSource* clone() const { return new ActionAliasAssignableDataSource(action, alias.get()); diff --git a/rtt/internal/FusedFunctorDataSource.hpp b/rtt/internal/FusedFunctorDataSource.hpp index fad59a9e1..ee7784789 100644 --- a/rtt/internal/FusedFunctorDataSource.hpp +++ b/rtt/internal/FusedFunctorDataSource.hpp @@ -333,24 +333,27 @@ namespace RTT */ template struct FusedMSendDataSource - : public DataSource > + : public AssignableDataSource > { typedef SendHandle result_type; typedef result_type value_t; - typedef typename DataSource::const_reference_t const_reference_t; + typedef typename AssignableDataSource::param_t param_t; + typedef typename AssignableDataSource::const_reference_t const_reference_t; + typedef typename AssignableDataSource::reference_t reference_t; typedef create_sequence< typename boost::function_types::parameter_types::type> SequenceFactory; typedef typename SequenceFactory::type DataSourceSequence; typename base::OperationCallerBase::shared_ptr ff; DataSourceSequence args; mutable SendHandle sh; // mutable because of get() const + mutable bool isqueued; public: typedef boost::intrusive_ptr > shared_ptr; FusedMSendDataSource(typename base::OperationCallerBase::shared_ptr g, const DataSourceSequence& s = DataSourceSequence() ) : - ff(g), args(s) + ff(g), args(s), sh(), isqueued(false) { } @@ -359,6 +362,14 @@ namespace RTT args = a1; } + virtual void set( param_t t ) { + sh = t; + } + + reference_t set() { + return sh; + } + value_t value() const { return sh; @@ -371,21 +382,38 @@ namespace RTT value_t get() const { + if (isqueued) + return sh; // put the member's object as first since SequenceFactory does not know about the OperationCallerBase type. sh = bf::invoke(&base::OperationCallerBase::send, bf::cons*, typename SequenceFactory::data_type>(ff.get(), SequenceFactory::data(args))); + if ( sh.ready() ) // only queued if sh contains a collectable operation + isqueued = true; return sh; } + void reset() { + isqueued = false; + } + virtual FusedMSendDataSource* clone() const { return new FusedMSendDataSource (ff, args); } + virtual FusedMSendDataSource* copy( std::map< const base::DataSourceBase*, base::DataSourceBase*>& alreadyCloned) const { - return new FusedMSendDataSource (ff, SequenceFactory::copy(args, alreadyCloned)); + // we need copy semantics because FusedMCollectDataSource tracks us. + if ( alreadyCloned[this] != 0 ) { + assert( dynamic_cast*>( alreadyCloned[this] ) == static_cast*>( alreadyCloned[this] ) ); + return static_cast*>( alreadyCloned[this] ); + } + // Other pieces in the code rely on insertion in the map : + alreadyCloned[this] = new FusedMSendDataSource(ff, SequenceFactory::copy(args, alreadyCloned)); + // return copy + return static_cast*>( alreadyCloned[this] ); } }; @@ -452,18 +480,32 @@ namespace RTT { return new FusedMCollectDataSource ( args, isblocking); } + virtual FusedMCollectDataSource* copy( std::map< const base::DataSourceBase*, base::DataSourceBase*>& alreadyCloned) const { - return new FusedMCollectDataSource ( SequenceFactory::copy(args, alreadyCloned), isblocking); + // we need copy semantics because CmdCollectCondition tracks us. + // WARNING: This is a tricky precedent... should all DataSources with state + multiple living references then implement this ? Should we assert on this ? + if ( alreadyCloned[this] != 0 ) { + assert ( dynamic_cast*>( alreadyCloned[this] ) == static_cast*>( alreadyCloned[this] ) ); + return static_cast*>( alreadyCloned[this] ); + } + // Other pieces in the code rely on insertion in the map : + alreadyCloned[this] = new FusedMCollectDataSource(SequenceFactory::copy(args, alreadyCloned), isblocking); + // return copy + return static_cast*>( alreadyCloned[this] ); } }; /** * A Function object that reacts to a Signal by writing the arguments in * data sources and calling an action object. + * + * Implementation note: this class does not require copy/clone semantics because + * it is re-created for each SM instantiation, so it already gets the copy/cloned + * Data Sources in its constructor. */ template struct FusedMSignal : public base::DisposableInterface diff --git a/rtt/internal/LocalOperationCaller.hpp b/rtt/internal/LocalOperationCaller.hpp index 6d2f4ae61..33bdd6bb0 100644 --- a/rtt/internal/LocalOperationCaller.hpp +++ b/rtt/internal/LocalOperationCaller.hpp @@ -201,7 +201,7 @@ namespace RTT } - SendStatus collectIfDone_impl() { + SendStatus collectIfDone_impl() const { if ( this->retv.isExecuted()) { this->retv.checkError(); return SendSuccess; @@ -212,7 +212,7 @@ namespace RTT // collect_impl belongs in LocalOperationCallerImpl because it would need // to be repeated in each BindStorage spec. template - SendStatus collectIfDone_impl( T1& a1 ) { + SendStatus collectIfDone_impl( T1& a1 ) const { if ( this->retv.isExecuted()) { this->retv.checkError(); bf::vector_tie(a1) = bf::filter_if< is_arg_return > >(this->vStore); @@ -222,7 +222,7 @@ namespace RTT } template - SendStatus collectIfDone_impl( T1& a1, T2& a2 ) { + SendStatus collectIfDone_impl( T1& a1, T2& a2 ) const { if ( this->retv.isExecuted()) { this->retv.checkError(); bf::vector_tie(a1,a2) = bf::filter_if< is_arg_return > >(this->vStore); @@ -232,7 +232,7 @@ namespace RTT } template - SendStatus collectIfDone_impl( T1& a1, T2& a2, T3& a3 ) { + SendStatus collectIfDone_impl( T1& a1, T2& a2, T3& a3 ) const { if ( this->retv.isExecuted()) { this->retv.checkError(); bf::vector_tie(a1,a2,a3) = bf::filter_if< is_arg_return > >(this->vStore); @@ -242,7 +242,7 @@ namespace RTT } template - SendStatus collectIfDone_impl( T1& a1, T2& a2, T3& a3, T4& a4 ) { + SendStatus collectIfDone_impl( T1& a1, T2& a2, T3& a3, T4& a4 ) const { if ( this->retv.isExecuted()) { this->retv.checkError(); bf::vector_tie(a1,a2,a3,a4) = bf::filter_if< is_arg_return > >(this->vStore); @@ -252,7 +252,7 @@ namespace RTT } template - SendStatus collectIfDone_impl( T1& a1, T2& a2, T3& a3, T4& a4, T5& a5 ) { + SendStatus collectIfDone_impl( T1& a1, T2& a2, T3& a3, T4& a4, T5& a5 ) const { if ( this->retv.isExecuted()) { this->retv.checkError(); bf::vector_tie(a1,a2,a3,a4,a5) = bf::filter_if< is_arg_return > >(this->vStore); @@ -262,7 +262,7 @@ namespace RTT } template - SendStatus collectIfDone_impl( T1& a1, T2& a2, T3& a3, T4& a4, T5& a5, T6& a6 ) { + SendStatus collectIfDone_impl( T1& a1, T2& a2, T3& a3, T4& a4, T5& a5, T6& a6 ) const { if ( this->retv.isExecuted()) { this->retv.checkError(); bf::vector_tie(a1,a2,a3,a4,a5,a6) = bf::filter_if< is_arg_return > >(this->vStore); @@ -272,7 +272,7 @@ namespace RTT } template - SendStatus collectIfDone_impl( T1& a1, T2& a2, T3& a3, T4& a4, T5& a5, T6& a6, T7& a7 ) { + SendStatus collectIfDone_impl( T1& a1, T2& a2, T3& a3, T4& a4, T5& a5, T6& a6, T7& a7 ) const { if ( this->retv.isExecuted()) { this->retv.checkError(); bf::vector_tie(a1,a2,a3,a4,a5,a6,a7) = bf::filter_if< is_arg_return > >(this->vStore); @@ -281,7 +281,7 @@ namespace RTT return SendNotReady; } - bool checkCaller() { + bool checkCaller() const { if (!this->caller) { log(Error) << "You're using call() an OwnThread operation or collect() on a sent operation without setting a caller in the OperationCaller. This often causes deadlocks." <engine() in a component or GlobalEngine::Instance() in a non-component function. Returning a CollectFailure." <caller->waitForMessages( boost::bind(&Store::RStoreType::isExecuted,boost::ref(this->retv)) ); return this->collectIfDone_impl(); } template - SendStatus collect_impl( T1& a1 ) { + SendStatus collect_impl( T1& a1 ) const { if (!checkCaller()) return CollectFailure; this->caller->waitForMessages( boost::bind(&Store::RStoreType::isExecuted,boost::ref(this->retv)) ); return this->collectIfDone_impl(a1); } template - SendStatus collect_impl( T1& a1, T2& a2 ) { + SendStatus collect_impl( T1& a1, T2& a2 ) const { if (!checkCaller()) return CollectFailure; this->caller->waitForMessages( boost::bind(&Store::RStoreType::isExecuted,boost::ref(this->retv)) ); return this->collectIfDone_impl(a1,a2); } template - SendStatus collect_impl( T1& a1, T2& a2, T3& a3 ) { + SendStatus collect_impl( T1& a1, T2& a2, T3& a3 ) const { if (!checkCaller()) return CollectFailure; this->caller->waitForMessages( boost::bind(&Store::RStoreType::isExecuted,boost::ref(this->retv)) ); return this->collectIfDone_impl(a1,a2,a3); } template - SendStatus collect_impl( T1& a1, T2& a2, T3& a3, T4& a4) { + SendStatus collect_impl( T1& a1, T2& a2, T3& a3, T4& a4) const { if (!checkCaller()) return CollectFailure; this->caller->waitForMessages( boost::bind(&Store::RStoreType::isExecuted,boost::ref(this->retv)) ); return this->collectIfDone_impl(a1,a2,a3,a4); } template - SendStatus collect_impl( T1& a1, T2& a2, T3& a3, T4& a4, T5& a5) { + SendStatus collect_impl( T1& a1, T2& a2, T3& a3, T4& a4, T5& a5) const { if (!checkCaller()) return CollectFailure; this->caller->waitForMessages( boost::bind(&Store::RStoreType::isExecuted,boost::ref(this->retv)) ); return this->collectIfDone_impl(a1,a2,a3,a4, a5); } template - SendStatus collect_impl( T1& a1, T2& a2, T3& a3, T4& a4, T5& a5, T6& a6) { + SendStatus collect_impl( T1& a1, T2& a2, T3& a3, T4& a4, T5& a5, T6& a6) const { if (!checkCaller()) return CollectFailure; this->caller->waitForMessages( boost::bind(&Store::RStoreType::isExecuted,boost::ref(this->retv)) ); return this->collectIfDone_impl(a1,a2,a3,a4,a5,a6); } template - SendStatus collect_impl( T1& a1, T2& a2, T3& a3, T4& a4, T5& a5, T6& a6, T7& a7) { + SendStatus collect_impl( T1& a1, T2& a2, T3& a3, T4& a4, T5& a5, T6& a6, T7& a7) const { if (!checkCaller()) return CollectFailure; this->caller->waitForMessages( boost::bind(&Store::RStoreType::isExecuted,boost::ref(this->retv)) ); return this->collectIfDone_impl(a1,a2,a3,a4,a5,a6,a7); @@ -527,7 +527,7 @@ namespace RTT return NA::na(); } - result_type ret_impl() + result_type ret_impl() const { this->retv.checkError(); return this->retv.result(); // may return void. @@ -539,7 +539,7 @@ namespace RTT * all arguments. */ template - result_type ret_impl(T1 a1) + result_type ret_impl(T1 a1) const { this->retv.checkError(); typedef mpl::and_, mpl::not_ > > > pred; @@ -550,7 +550,7 @@ namespace RTT } template - result_type ret_impl(T1 a1, T2 a2) + result_type ret_impl(T1 a1, T2 a2) const { this->retv.checkError(); typedef mpl::and_, mpl::not_ > > > pred; @@ -561,7 +561,7 @@ namespace RTT } template - result_type ret_impl(T1 a1, T2 a2, T3 a3) + result_type ret_impl(T1 a1, T2 a2, T3 a3) const { this->retv.checkError(); typedef mpl::and_, mpl::not_ > > > pred; @@ -572,7 +572,7 @@ namespace RTT } template - result_type ret_impl(T1 a1, T2 a2, T3 a3, T4 a4) + result_type ret_impl(T1 a1, T2 a2, T3 a3, T4 a4) const { this->retv.checkError(); typedef mpl::and_, mpl::not_ > > > pred; @@ -583,7 +583,7 @@ namespace RTT } template - result_type ret_impl(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) + result_type ret_impl(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) const { this->retv.checkError(); typedef mpl::and_, mpl::not_ > > > pred; @@ -594,7 +594,7 @@ namespace RTT } template - result_type ret_impl(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6) + result_type ret_impl(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6) const { this->retv.checkError(); typedef mpl::and_, mpl::not_ > > > pred; @@ -605,7 +605,7 @@ namespace RTT } template - result_type ret_impl(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7) + result_type ret_impl(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7) const { this->retv.checkError(); typedef mpl::and_, mpl::not_ > > > pred; diff --git a/rtt/internal/OperationCallerC.cpp b/rtt/internal/OperationCallerC.cpp index 3cc6f6b33..23ea25f25 100644 --- a/rtt/internal/OperationCallerC.cpp +++ b/rtt/internal/OperationCallerC.cpp @@ -221,9 +221,10 @@ namespace RTT } bool OperationCallerC::call() { - if (m) + if (m) { + m->reset(); return m->evaluate(); - else { + } else { Logger::log() <reset(); #ifndef NDEBUG bool result = #endif diff --git a/rtt/internal/ReturnSignature.hpp b/rtt/internal/ReturnSignature.hpp index a3e4c685c..56a870d16 100644 --- a/rtt/internal/ReturnSignature.hpp +++ b/rtt/internal/ReturnSignature.hpp @@ -65,7 +65,7 @@ namespace RTT ReturnSignature(ToInvoke implementation) : impl(implementation) {} ~ReturnSignature() {} - result_type ret() { + result_type ret() const { if (impl) return impl->ret(); return NA::na(); @@ -85,13 +85,13 @@ namespace RTT ReturnSignature(ToInvoke implementation) : impl(implementation) {} ~ReturnSignature() {} - result_type ret(arg1_type a1) { + result_type ret(arg1_type a1) const { if (impl) return impl->ret( a1 ); return NA::na(); } - result_type ret() { + result_type ret() const { if (impl) return impl->ret(); return NA::na(); @@ -112,13 +112,13 @@ namespace RTT ReturnSignature(ToInvoke implementation) : impl(implementation) {} ~ReturnSignature() {} - result_type ret(arg1_type a1, arg2_type a2) { + result_type ret(arg1_type a1, arg2_type a2) const { if (impl) return impl->ret( a1,a2 ); return NA::na(); } - result_type ret() { + result_type ret() const { if (impl) return impl->ret(); return NA::na(); @@ -139,13 +139,13 @@ namespace RTT ReturnSignature(ToInvoke implementation) : impl(implementation) {} ~ReturnSignature() { } - result_type ret(arg1_type a1, arg2_type a2, arg3_type a3) { + result_type ret(arg1_type a1, arg2_type a2, arg3_type a3) const { if (impl) return impl->ret( a1,a2,a3 ); return NA::na(); } - result_type ret() { + result_type ret() const { if (impl) return impl->ret(); return NA::na(); @@ -168,13 +168,13 @@ namespace RTT ReturnSignature(ToInvoke implementation) : impl(implementation) {} ~ReturnSignature() { } - result_type ret(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) { + result_type ret(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4) const { if (impl) return impl->ret( a1,a2,a3,a4 ); return NA::na(); } - result_type ret() { + result_type ret() const { if (impl) return impl->ret(); return NA::na(); @@ -197,13 +197,13 @@ namespace RTT ReturnSignature(ToInvoke implementation) : impl(implementation) {} ~ReturnSignature() { } - result_type ret(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, arg5_type a5) { + result_type ret(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, arg5_type a5) const { if (impl) return impl->ret( a1,a2,a3,a4,a5 ); return NA::na(); } - result_type ret() { + result_type ret() const { if (impl) return impl->ret(); return NA::na(); @@ -227,13 +227,13 @@ namespace RTT ReturnSignature(ToInvoke implementation) : impl(implementation) {} ~ReturnSignature() { } - result_type ret(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, arg5_type a5, arg6_type a6) { + result_type ret(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, arg5_type a5, arg6_type a6) const { if (impl) return impl->ret( a1,a2,a3,a4,a5,a6 ); return NA::na(); } - result_type ret() { + result_type ret() const { if (impl) return impl->ret(); return NA::na(); @@ -258,13 +258,13 @@ namespace RTT ReturnSignature(ToInvoke implementation) : impl(implementation) {} ~ReturnSignature() { } - result_type ret(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, arg5_type a5, arg6_type a6, arg7_type a7) { + result_type ret(arg1_type a1, arg2_type a2, arg3_type a3, arg4_type a4, arg5_type a5, arg6_type a6, arg7_type a7) const { if (impl) return impl->ret( a1,a2,a3,a4,a5,a6,a7 ); return NA::na(); } - result_type ret() { + result_type ret() const { if (impl) return impl->ret(); return NA::na(); diff --git a/rtt/scripting/CallFunction.hpp b/rtt/scripting/CallFunction.hpp index 91c53582d..296c3fd5a 100644 --- a/rtt/scripting/CallFunction.hpp +++ b/rtt/scripting/CallFunction.hpp @@ -57,8 +57,6 @@ namespace RTT * An action which calls a FunctionFraph for execution * in a ExecutionEngine. * Script functions are always executed in the thread of the component. - * - * It is an DataSource such that it can be executed by program scripts. */ class RTT_SCRIPTING_API CallFunction : public base::ActionInterface, public base::DisposableInterface @@ -66,14 +64,6 @@ namespace RTT base::ActionInterface* minit; ExecutionEngine* mrunner; ExecutionEngine* mcaller; - /** - * _v is only necessary for the copy/clone semantics. - */ - internal::AssignableDataSource::shared_ptr _v; - /** - * _foo contains the exact same pointer as _v->get(), but also serves - * as a shared_ptr handle for cleanup after clone(). - */ boost::shared_ptr _foo; bool maccept; @@ -108,22 +98,20 @@ namespace RTT * function into the processor, in order to initialise it. * @param foo The function to run in the processor. * @param p The target processor which will run the function. - * @param v Implementation specific parameter to support copy/clone semantics. */ CallFunction( base::ActionInterface* init_com, boost::shared_ptr foo, - ExecutionEngine* p, ExecutionEngine* caller, - internal::AssignableDataSource* v = 0 , - internal::AssignableDataSource* a = 0 ) + ExecutionEngine* p, ExecutionEngine* caller + ) : minit(init_com), mrunner(p), mcaller(caller), - _v( v==0 ? new internal::UnboundDataSource< internal::ValueDataSource >(foo.get()) : v ), _foo( foo ), maccept(false) { } ~CallFunction() { this->reset(); + delete minit; } /** @@ -251,18 +239,13 @@ namespace RTT base::ActionInterface* clone() const { - // _v is shared_ptr, so don't clone. - return new CallFunction( minit->clone(), _foo, mrunner, mcaller, _v.get() ); + return new CallFunction( minit->clone(), _foo, mrunner, mcaller); } base::ActionInterface* copy( std::map& alreadyCloned ) const { - // this may seem strange, but : - // make a copy of foo (a function), make a copy of _v (a datasource), store pointer to new foo in _v ! boost::shared_ptr fcpy( _foo->copy(alreadyCloned) ); - internal::AssignableDataSource* vcpy = _v->copy(alreadyCloned); - vcpy->set( fcpy.get() ); // since we own _foo, we may manipulate the copy of _v - return new CallFunction( minit->copy(alreadyCloned), fcpy , mrunner, mcaller, vcpy ); + return new CallFunction( minit->copy(alreadyCloned), fcpy , mrunner, mcaller ); } }; diff --git a/rtt/scripting/CmdFunction.hpp b/rtt/scripting/CmdFunction.hpp new file mode 100644 index 000000000..71d141ad1 --- /dev/null +++ b/rtt/scripting/CmdFunction.hpp @@ -0,0 +1,204 @@ +/*************************************************************************** + tag: Peter Soetens Sat Oct 25 00:52:59 2014 +0200 CmdFunction.hpp + + CmdFunction.hpp - description + ------------------- + begin : Sat Oct 25 2014 + copyright : (C) 2014 Peter Soetens + email : peter@thesourceworks.com + + *************************************************************************** + * This library is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public * + * License as published by the Free Software Foundation; * + * version 2 of the License. * + * * + * As a special exception, you may use this file as part of a free * + * software library without restriction. Specifically, if other files * + * instantiate templates or use macros or inline functions from this * + * file, or you compile this file and link it with other files to * + * produce an executable, this file does not by itself cause the * + * resulting executable to be covered by the GNU General Public * + * License. This exception does not however invalidate any other * + * reasons why the executable file might be covered by the GNU General * + * Public License. * + * * + * This library is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * You should have received a copy of the GNU General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 59 Temple Place, * + * Suite 330, Boston, MA 02111-1307 USA * + * * + ***************************************************************************/ + + +#ifndef ORO_CMD_FUNCTION_HPP +#define ORO_CMD_FUNCTION_HPP + +#include "../SendStatus.hpp" +#include "../base/ActionInterface.hpp" +#include "../base/DisposableInterface.hpp" +#include "ConditionInterface.hpp" +#include "../internal/DataSources.hpp" +#include "ProgramInterface.hpp" +#include "../internal/DataSource.hpp" +#include "../ExecutionEngine.hpp" +#include +#include + +#include + +namespace RTT +{ namespace scripting { + using namespace detail; + using namespace std; + + /** + * A DataSource which sends a FunctionFraph for execution + * in a ExecutionEngine. + */ + class RTT_SCRIPTING_API CmdFunction + : public internal::DataSource + { + base::ActionInterface* minit; + ExecutionEngine* mrunner; + ExecutionEngine* mcaller; + boost::shared_ptr _foo; + mutable SendStatus ss; + mutable bool isqueued; + mutable bool maccept; + + public: + /** + * Create a Command to send a function to a ExecutionEngine. + * @param init_com The command to execute before sending the + * function into the processor, in order to initialise it. + * @param foo The function to run in the processor. + * @param p The target processor which will run the function. + */ + CmdFunction( base::ActionInterface* init_com, + boost::shared_ptr foo, + ExecutionEngine* p, ExecutionEngine* caller + ) + : minit(init_com), + mrunner(p), mcaller(caller), + _foo( foo ), ss(SendFailure), isqueued(false), maccept(false) + { + } + + ~CmdFunction() { + this->reset(); + delete minit; + } + + virtual SendStatus get() const { + try { + // this is asyn behaviour : + if (isqueued == false ) { + isqueued = true; + ss = SendNotReady; + // is called before runFunction is executed. + minit->readArguments(); + maccept = minit->execute() && mrunner->runFunction( _foo.get() ); + // we ignore the ret value of start(). It could have been auto-started during loading() of the function. + if ( _foo->needsStart() ) { // _foo might be auto-started in runFunction() + _foo->start(); + } + if ( ! maccept ) { + return ss = SendFailure; + } + } + if ( _foo->inError() ) { + return ss = CollectFailure; + } + if ( _foo->isStopped() ) { + return ss = SendSuccess; + } + } catch (...) { + cout << "CmdFunction threw an exception" <isLoaded()) mrunner->removeFunction( _foo.get() ); + maccept = false; + isqueued = false; + ss = SendFailure; + } + + CmdFunction* clone() const + { + return new CmdFunction( minit->clone(), _foo, mrunner, mcaller ); + } + + CmdFunction* copy( std::map& alreadyCloned ) const + { + // if somehow a copy exists, return the copy, otherwise return this (see Attribute copy) + if ( alreadyCloned[this] == 0 ) { + boost::shared_ptr fcpy( _foo->copy(alreadyCloned) ); + alreadyCloned[this] = new CmdFunction( minit->copy(alreadyCloned), fcpy , mrunner, mcaller); + } + + assert ( dynamic_cast( alreadyCloned[this] ) == static_cast( alreadyCloned[this] ) ); + return static_cast( alreadyCloned[this] ); + } + + }; + + /** + * A DataSource that collects the result of a CmdFunction + */ + struct RTT_SCRIPTING_API CmdCollectCondition + : public ConditionInterface + { + DataSource::shared_ptr collectds; + public: + + CmdCollectCondition(DataSource::shared_ptr ds ) : + collectds(ds) + { + } + + void reset() {} + + bool evaluate() + { + // We may not call collectds->evaluate() inhere, because this actively tries a collect, + // and if it would succeed, we would proceed immediately, without giving the vertex node + // a chance to do something with the return value of the cmd. + return collectds->rvalue() != SendNotReady; + } + + virtual CmdCollectCondition* clone() const + { + return new CmdCollectCondition( collectds ); + } + virtual CmdCollectCondition* copy( + std::map< + const base::DataSourceBase*, + base::DataSourceBase*>& alreadyCloned) const + { + return new CmdCollectCondition ( collectds->copy(alreadyCloned) ); + } + }; +}} + +#endif diff --git a/rtt/scripting/CommandDataSource.hpp b/rtt/scripting/CommandDataSource.hpp index faef27f66..ed889cf27 100644 --- a/rtt/scripting/CommandDataSource.hpp +++ b/rtt/scripting/CommandDataSource.hpp @@ -57,10 +57,10 @@ namespace RTT void readArguments() { - _dsb->evaluate(); } bool execute() { + _dsb->evaluate(); return true; } @@ -90,10 +90,10 @@ namespace RTT void readArguments() { - _dsb->evaluate(); } bool execute() { + _dsb->evaluate(); return _dsb->value(); } void reset() { diff --git a/rtt/scripting/ExpressionParser.cpp b/rtt/scripting/ExpressionParser.cpp index 0614abf7c..8086ee651 100644 --- a/rtt/scripting/ExpressionParser.cpp +++ b/rtt/scripting/ExpressionParser.cpp @@ -48,7 +48,9 @@ #include "../types/Operators.hpp" #include "DataSourceCondition.hpp" #include "../internal/DataSourceCommand.hpp" +#include "CmdFunction.hpp" #include "../internal/GlobalService.hpp" +#include "CommandDataSource.hpp" #include "DataSourceTime.hpp" #include "../TaskContext.hpp" @@ -87,7 +89,8 @@ namespace RTT DataCallParser::DataCallParser( ExpressionParser& p, CommonParser& cp, TaskContext* c, ExecutionEngine* caller ) - : mcaller( caller ? caller : c->engine()), mis_send(false), commonparser(cp), expressionparser( p ), + : ret(), mhandle(), mcmdcnd(0), mobject(), mmethod(), + mcaller( caller ? caller : c->engine()), mcalltype(DEFAULT_CALLTYPE), commonparser(cp), expressionparser( p ), peerparser( c, cp, false ) // accept partial paths { BOOST_SPIRIT_DEBUG_RULE( datacall ); @@ -112,33 +115,37 @@ namespace RTT ( peerpath >> !object >> method[ boost::bind( &DataCallParser::seendataname, this ) ] >> !arguments)[ boost::bind( &DataCallParser::seendatacall, this ) ]; } - void DataCallParser::seensend() { - mis_send = true; - } - void DataCallParser::seenobjectname( iter_t begin, iter_t end ) { std::string name( begin, end ); mobject = name.substr(0, name.length() - 1); - }; + } void DataCallParser::seenmethodname( iter_t begin, iter_t end ) { std::string name( begin, end ); - if ( name == "send") { - mis_send = true; + if ( name == "send" ) { + mcalltype = CALLTYPE_SEND; + mmethod = mobject; + mobject.clear(); + } else if (name == "cmd" ) { + mcalltype = CALLTYPE_CMD; + mmethod = mobject; + mobject.clear(); + } else if (name == "call" ) { + mcalltype = CALLTYPE_CALL; mmethod = mobject; mobject.clear(); } else { - mis_send = false; + mcalltype = DEFAULT_CALLTYPE; mmethod = name; } // cout << "seenmethodname "<< mobject << "." << mmethod<getName(); // cout << "DCP saw method "<< mmethod <<" of object "<getName()<getDataSource() ); if (meth == "collect") ret = sha->getFactory()->produceCollect(args, new ValueDataSource(true) );// blocking - else + else // (meth == "collectIfDone") ret = sha->getFactory()->produceCollect(args, new ValueDataSource(false) );// non-blocking return; } } throw parse_exception_fatal_semantic_error( obj + "."+meth +": "+ obj +" is not a valid SendHandle object."); } - if (!mis_send) { + + unsigned int arity = ops->getCollectArity(meth); + switch(mcalltype) { + case DEFAULT_CALLTYPE: + case CALLTYPE_CALL: ret = ops->produce( meth, args, mcaller ); mhandle.reset(); - } else { + break; + case CALLTYPE_SEND: ret = ops->produceSend( meth, args, mcaller ); mhandle.reset( new SendHandleAlias( meth, ops->produceHandle(meth), ops->getPart(meth)) ); + break; + case CALLTYPE_CMD: + DataSourceBase::shared_ptr sendds = ops->produceSend( meth, args, mcaller ); + args.clear(); + args.push_back( sendds ); // store the produceSend DS for collecting: + for ( unsigned int i =0; i != arity; ++i) { + args.push_back( ops->getOperation(meth)->getCollectType( i + 1 )->buildValue() ); // this is only to satisfy produceCollect. We ignore the results... + } + + DataSource::shared_ptr collectds + = boost::dynamic_pointer_cast >( + ops->produceCollect( meth, args, new ValueDataSource(false) ) + ); // non-blocking, need extra condition + assert(collectds); + + ret = new ActionAliasDataSource(new CommandDataSource( sendds ), collectds.get() ); + mcmdcnd = new CmdCollectCondition( collectds ); // Replaces RTT 1.x completion condition. + break; } } catch( const wrong_number_of_args_exception& e ) @@ -266,7 +297,7 @@ namespace RTT { delete argparsers.top(); argparsers.pop(); - }; + } } ConstructorParser::ConstructorParser( ExpressionParser& p, CommonParser& cp) @@ -288,7 +319,7 @@ namespace RTT { delete argparsers.top(); argparsers.pop(); - }; + } } @@ -340,7 +371,8 @@ namespace RTT /** @endcond */ ExpressionParser::ExpressionParser( TaskContext* pc, ExecutionEngine* caller, CommonParser& cp ) - : datacallparser( *this, cp, pc, caller ), + : mcmdcnd(0), + datacallparser( *this, cp, pc, caller ), constrparser(*this, cp), commonparser( cp ), valueparser( pc, cp ), @@ -502,7 +534,7 @@ namespace RTT // ( str_p( "s" ) | "ms" | "us" | "ns" )[ // bind( &ExpressionParser::seentimeunit, this, _1, _2 ) ] ) | expression[bind(&ExpressionParser::seentimeexpr, this)]; - }; + } void ExpressionParser::inverttime() { @@ -542,7 +574,7 @@ namespace RTT default: std::string arg(begin, end); throw parse_exception_semantic_error("Expected time expression 's', 'ms', 'us' or 'ns' after integer value, got "+arg); - }; + } parsestack.push( new ConstantDataSource( total ) ); @@ -568,6 +600,7 @@ namespace RTT DataSourceBase::shared_ptr n( datacallparser.getParseResult() ); parsestack.push( n ); mhandle = datacallparser.getParseHandle(); + mcmdcnd = datacallparser.getParseCmdResult(); } void ExpressionParser::seenconstructor() @@ -595,6 +628,12 @@ namespace RTT return parsestack.top(); } + ConditionInterface* ExpressionParser::getCmdResult() + { + assert( !parsestack.empty() ); + return mcmdcnd; + } + boost::shared_ptr ExpressionParser::getHandle() { assert( !parsestack.empty() ); @@ -661,6 +700,15 @@ namespace RTT // cout << "Found !"<getName(); context->attributes()->removeAttribute(name); + // This aliasing may happen only once for each var SendHandle. Since 'arg1' is not assignable, a next assigning will fail. + if ( dynamic_cast*>(mhandle->getDataSource().get() ) ) { + // This goes quite far: we also wrap the SH DataSource in a collect such that evaluating it does not cause a reset()+send(), but merely returns the SendStatus: + context->attributes()->setValue( new SendHandleAlias( name, + mhandle->getFactory()->produceCollect(std::vector(1,arg1), new ValueDataSource(false)), + mhandle->getFactory() ) ); + parsestack.push( arg1 ); // effectively aliases RHS to lhs. Don't use the SendHandleAlias since it would not do the reset()+send(). + return; + } AttributeBase* var = mhandle->clone(); var->setName( name ); // fill in the final handle name. context->attributes()->setValue( var ); @@ -708,6 +756,7 @@ namespace RTT void ExpressionParser::dropResult() { + mcmdcnd = 0; parsestack.pop(); } } diff --git a/rtt/scripting/ExpressionParser.hpp b/rtt/scripting/ExpressionParser.hpp index bd6e14bda..4c16d2201 100644 --- a/rtt/scripting/ExpressionParser.hpp +++ b/rtt/scripting/ExpressionParser.hpp @@ -42,6 +42,7 @@ #include "CommonParser.hpp" #include "PeerParser.hpp" #include "ValueParser.hpp" +#include "SendHandleAlias.hpp" #include "../internal/DataSource.hpp" #include "../types/Operators.hpp" #include "../Time.hpp" @@ -65,11 +66,12 @@ namespace RTT { namespace scripting class DataCallParser { base::DataSourceBase::shared_ptr ret; - boost::shared_ptr mhandle; + boost::shared_ptr mhandle; + ConditionInterface* mcmdcnd; std::string mobject; std::string mmethod; ExecutionEngine* mcaller; - bool mis_send; + enum CallType { DEFAULT_CALLTYPE, CALLTYPE_CALL, CALLTYPE_SEND, CALLTYPE_CMD } mcalltype; rule_t datacall, arguments, peerpath, object, method; @@ -77,7 +79,7 @@ namespace RTT { namespace scripting void seenobjectname( iter_t begin, iter_t end ); void seendataname(); void seendatacall(); - void seensend(); + CommonParser& commonparser; ExpressionParser& expressionparser; PeerParser peerparser; @@ -95,7 +97,11 @@ namespace RTT { namespace scripting { return ret.get(); } - boost::shared_ptr getParseHandle() + ConditionInterface* getParseCmdResult() + { + return mcmdcnd; + } + boost::shared_ptr getParseHandle() { return mhandle; } @@ -157,7 +163,9 @@ namespace RTT { namespace scripting * Contains the last SendHandle encountered, Will also be dropped * by dropResult(). */ - boost::shared_ptr mhandle; + boost::shared_ptr mhandle; + + ConditionInterface* mcmdcnd; // the name that was parsed as the object to use a certain // data of.. @@ -201,6 +209,7 @@ namespace RTT { namespace scripting rule_t& parser(); base::DataSourceBase::shared_ptr getResult(); + ConditionInterface* getCmdResult(); /** * In case the parsed result returns a SendHandle, diff --git a/rtt/scripting/FunctionFactory.cpp b/rtt/scripting/FunctionFactory.cpp index 3dcba4dee..8ef94d6eb 100644 --- a/rtt/scripting/FunctionFactory.cpp +++ b/rtt/scripting/FunctionFactory.cpp @@ -43,6 +43,7 @@ #include "CommandComposite.hpp" #include "CommandBinary.hpp" #include "CallFunction.hpp" +#include "CmdFunction.hpp" #include "ConditionComposite.hpp" #include "TryCommand.hpp" #include @@ -61,6 +62,48 @@ namespace RTT { using namespace detail; + class CmdFunctionWrapper + : public DataSource + { + DataSource::shared_ptr alias; + public: + typedef boost::intrusive_ptr shared_ptr; + + CmdFunctionWrapper(DataSource* ds) + : alias(ds) + {} + + ~CmdFunctionWrapper() { } + + bool evaluate() const { + return alias->evaluate(); + } + + DataSource::result_t get() const + { + return alias->get(); + } + + DataSource::result_t value() const + { + return alias->value(); + } + + DataSource::const_reference_t rvalue() const + { + return alias->rvalue(); + } + + virtual void reset() { /* nop, don't reset ! */ } + + virtual CmdFunctionWrapper* clone() const { + return new CmdFunctionWrapper(alias.get()); + } + virtual CmdFunctionWrapper* copy( std::map& alreadyCloned ) const { + return new CmdFunctionWrapper(alias->copy(alreadyCloned) ); + } + }; + FunctionFactory::FunctionFactory(ProgramInterfacePtr pi, ExecutionEngine* procs) : func(pi), proc(procs) {} @@ -121,7 +164,14 @@ namespace RTT { const std::vector& args , ExecutionEngine* caller ) const { + bool issend = false; + return produceHelper(args, caller, issend); + } + DataSourceBase::shared_ptr FunctionFactory::produceHelper( + const std::vector& args + , ExecutionEngine* caller, bool issend + ) const { // check if correct number of args : boost::shared_ptr orig = func; std::vector origlist = orig->getArguments(); @@ -166,32 +216,48 @@ namespace RTT { // the args of the copy can now safely be removed (saves memory): //fcopy->clearArguments(); - // the command gets ownership of the new function : - // this command is a DataSourceBase... - AttributeBase* ar= fcopy->getResult(); if (!caller) caller = GlobalEngine::Instance(); - if (ar) - return ar->getDataSource()->getTypeInfo()->buildActionAlias( new CallFunction( icom, fcopy, proc, caller ), ar->getDataSource()).get(); - else // void case, returns result of runFunction (backwards compatibility). - return new DataSourceCommand( new CallFunction( icom, fcopy, proc, caller ) ); + if (issend == false) { + // the command gets ownership of the new function : + // this command is a DataSourceBase... + AttributeBase* ar= fcopy->getResult(); + if (ar) + return ar->getDataSource()->getTypeInfo()->buildActionAlias( new CallFunction( icom, fcopy, proc, caller ), ar->getDataSource()).get(); + else // void case, returns result of runFunction (backwards compatibility). + return new DataSourceCommand( new CallFunction( icom, fcopy, proc, caller ) ); + } else { + return new CmdFunction( icom, fcopy, proc, caller ); + } } base::DataSourceBase::shared_ptr FunctionFactory::produceHandle() const { - throw no_asynchronous_operation_exception("Send not yet implemented for scripting functions."); - return 0; + return new ValueDataSource(SendNotReady); } base::DataSourceBase::shared_ptr FunctionFactory::produceSend(const std::vector& args, ExecutionEngine* caller ) const { - throw no_asynchronous_operation_exception("Send not yet implemented for scripting functions."); - return 0; + return produceHelper(args, caller, true); } base::DataSourceBase::shared_ptr FunctionFactory::produceCollect(const std::vector& args, DataSource::shared_ptr blocking ) const { - if (args.size() != 2) { - log(Error) <<"Invalid number of arguments. Script functions can only collect the return value." <get() == true ) + throw no_asynchronous_operation_exception("Blocking call to collect() not yet implemented for scripting functions. Use collectIfDone()."); + + if (args.size() >= 1) { + if ( dynamic_cast (args[0].get()) != 0 ) { + // The CmdFunction : wrap it and return it + // wrapping is necessary because we don't want to propagate reset() + return new CmdFunctionWrapper( dynamic_cast(args[0].get()) ); + } else if ( dynamic_cast (args[0].get()) != 0 ) { + // Return argument. + return args[0]; + } else { + log(Error) <<"FunctionFactory: Please define your SendHandle with 'var SendHandle' for script functions." <& args + , ExecutionEngine* caller, bool issend + ) const; public: FunctionFactory(ProgramInterfacePtr func, ExecutionEngine* procs); diff --git a/rtt/scripting/ProgramGraphParser.cpp b/rtt/scripting/ProgramGraphParser.cpp index bb3d03a9b..ef7a0c71b 100644 --- a/rtt/scripting/ProgramGraphParser.cpp +++ b/rtt/scripting/ProgramGraphParser.cpp @@ -61,10 +61,10 @@ #include #include -#ifdef WIN32 - #ifdef NDEBUG - #pragma optimize( "", off) - #endif +#ifdef WIN32 + #ifdef NDEBUG + #pragma optimize( "", off) + #endif #endif namespace RTT @@ -865,13 +865,16 @@ namespace RTT void ProgramGraphParser::seenstatement() { // an expression/method call (former do). - DataSourceBase::shared_ptr expr = expressionparser.getResult().get(); + DataSourceBase::shared_ptr expr = expressionparser.getResult(); + ConditionInterface* cnd = expressionparser.getCmdResult(); expressionparser.dropResult(); DataSource* bexpr = dynamic_cast*>(expr.get()); if (bexpr) program_builder->setCommand( new CommandDataSourceBool( bexpr ) ); else program_builder->setCommand( new CommandDataSource( expr ) ); + if (cnd) + program_builder->addConditionEdge( cnd, program_builder->nextNode() ); if ( program_builder->buildEdges() == 0 ) program_builder->proceedToNext( new ConditionTrue(), mpositer.get_position().line - ln_offset ); else @@ -890,7 +893,9 @@ namespace RTT // some value changes generate a command, we need to add it to // the program. ActionInterface* ac = 0; + ConditionInterface* cond = 0; std::vector acv = valuechangeparser.assignCommands(); + std::vector conds = valuechangeparser.assignConditions(); // and not forget to reset().. valuechangeparser.clear(); if ( acv.size() == 1) { @@ -899,11 +904,23 @@ namespace RTT else if (acv.size() > 1) { ac = new CommandComposite(acv); } + if ( conds.size() ==1 ) { + cond = conds.front(); + } + else if ( conds.size() > 1) { + cond = conds.front(); + unsigned int i = 1; + while ( i != conds.size() ) { + cond = new ConditionBinaryCompositeAND(cond, conds[i] ); + ++i; + } + } if (ac) { program_builder->setCommand( ac ); - // Since a valuechange does not add edges, we use this variant - // to create one. - program_builder->proceedToNext( new ConditionTrue, mpositer.get_position().line - ln_offset ); + // check if one of the vars caused a condition: + if (cond == 0) + cond = new ConditionTrue; + program_builder->proceedToNext( cond, mpositer.get_position().line - ln_offset ); } } diff --git a/rtt/scripting/ScriptingService.cpp b/rtt/scripting/ScriptingService.cpp index a1ab2f11f..738547a6e 100644 --- a/rtt/scripting/ScriptingService.cpp +++ b/rtt/scripting/ScriptingService.cpp @@ -453,8 +453,8 @@ namespace RTT { addOperation("execute", &ScriptingService::execute, this).doc("Execute a line of code (DEPRECATED).").arg("Code", "A single statement."); // OperationCallers for loading programs - addOperation("loadPrograms", &ScriptingService::doLoadPrograms, this).doc("Load a program from a given file (DEPRECATED).").arg("Filename", "The filename of the script."); - addOperation("loadProgramText", &ScriptingService::doLoadProgramText, this).doc("Load a program from a string (DEPRECATED).").arg("Code", "A string containing one or more program scripts."); + addOperation("loadPrograms", &ScriptingService::doLoadPrograms, this).doc("Load a program from a given file.").arg("Filename", "The filename of the script."); + addOperation("loadProgramText", &ScriptingService::doLoadProgramText, this).doc("Load a program from a string.").arg("Code", "A string containing one or more program scripts."); addOperation("unloadProgram", &ScriptingService::doUnloadProgram, this).doc("Remove a loaded program.").arg("Name", "The name of the loaded Program"); // Query OperationCallers for programs @@ -465,8 +465,8 @@ namespace RTT { addOperation("getProgramText", &ScriptingService::getProgramText, this).doc("Get the script of a program.").arg("Name", "The Name of the loaded Program"); // OperationCallers for loading state machines - addOperation("loadStateMachines", &ScriptingService::doLoadStateMachines, this).doc("Load a state machine from a given file (DEPRECATED).").arg("Filename", "The filename of the script."); - addOperation("loadStateMachineText", &ScriptingService::doLoadStateMachineText, this).doc("Load a state machine from a string (DEPRECATED).").arg("Code", "A string containing one or more state machine scripts."); + addOperation("loadStateMachines", &ScriptingService::doLoadStateMachines, this).doc("Load a state machine from a given file.").arg("Filename", "The filename of the script."); + addOperation("loadStateMachineText", &ScriptingService::doLoadStateMachineText, this).doc("Load a state machine from a string.").arg("Code", "A string containing one or more state machine scripts."); addOperation("unloadStateMachine", &ScriptingService::doUnloadStateMachine, this).doc("Remove a loaded state machine.").arg("Name", "The name of the loaded State Machine"); // Query OperationCallers for state machines diff --git a/rtt/scripting/SendHandleAlias.cpp b/rtt/scripting/SendHandleAlias.cpp index 679524a82..f6f9e0bb5 100644 --- a/rtt/scripting/SendHandleAlias.cpp +++ b/rtt/scripting/SendHandleAlias.cpp @@ -37,6 +37,8 @@ #include "SendHandleAlias.hpp" +#include +using namespace std; namespace RTT { @@ -65,9 +67,12 @@ namespace RTT } SendHandleAlias* SendHandleAlias::copy(std::map< const base::DataSourceBase*, base::DataSourceBase*>& replacements, - bool) + bool inst) { - // instantiate does not apply. + // BIG NOTE: Instantiating a SendHandleAlias may/WILL happen too soon, not giving a chance + // to the arguments to instantiate during copy... Once instantiation is over, we + // may call copy, but we guess this won't even happen in our current application. + assert( (inst == false) && "SendHandleAlias may not be instantiated !" ); return new SendHandleAlias(mname, data->copy(replacements), fact); } diff --git a/rtt/scripting/StateMachineService.cpp b/rtt/scripting/StateMachineService.cpp index dd772a3eb..d844b2afd 100644 --- a/rtt/scripting/StateMachineService.cpp +++ b/rtt/scripting/StateMachineService.cpp @@ -43,6 +43,7 @@ #include "../FactoryExceptions.hpp" #include "../TaskContext.hpp" #include "../OperationCaller.hpp" +#include "SendHandleAlias.hpp" namespace RTT { @@ -99,6 +100,18 @@ namespace RTT StateMachineServicePtr tmp( new StateMachineService( newsc, this->mtc ) ); replacements[ _this.get() ] = tmp->_this.get(); // put 'newsc' in map + if (instantiate) { + // Remove any remaining SendHandleAlias attributes, since they are not allowed for an instantiate... + // See SendHandleAlias::copy() for more details. + for ( ConfigurationInterface::map_t::iterator it = values.begin(); it != values.end(); ) { + if (dynamic_cast(*it)) { + it = values.erase(it); + } else { + ++it; + } + } + } + ConfigurationInterface* dummy = ConfigurationInterface::copy( replacements, instantiate ); tmp->loadValues( dummy->getValues()); delete dummy; diff --git a/rtt/scripting/TryCommand.cpp b/rtt/scripting/TryCommand.cpp index 54e83eed4..9ac717fcc 100644 --- a/rtt/scripting/TryCommand.cpp +++ b/rtt/scripting/TryCommand.cpp @@ -136,11 +136,11 @@ namespace RTT } void EvalCommand::readArguments() { - _ds->evaluate(); } bool EvalCommand::execute() { - _cache->set( _ds->value() ); + _ds->evaluate(); + _cache->set( _ds->rvalue() ); return true; } diff --git a/rtt/scripting/ValueChangeParser.cpp b/rtt/scripting/ValueChangeParser.cpp index 82162b225..aff8ed072 100644 --- a/rtt/scripting/ValueChangeParser.cpp +++ b/rtt/scripting/ValueChangeParser.cpp @@ -291,12 +291,15 @@ namespace RTT if ( expressionparser.hasResult() ) { DataSourceBase::shared_ptr expr = expressionparser.getResult(); + ConditionInterface* cond = expressionparser.getCmdResult(); expressionparser.dropResult(); //assert( !expressionparser.hasResult() ); try { ActionInterface* ac = var->getDataSource()->updateAction( expr.get() ); assert(ac); assigncommands.push_back( ac ); + if (cond) + conditions.push_back(cond); } catch( const bad_assignment& ) { this->cleanup(); @@ -343,6 +346,7 @@ namespace RTT void ValueChangeParser::clear() { assigncommands.clear(); + conditions.clear(); definedvalues.clear(); diff --git a/rtt/scripting/ValueChangeParser.hpp b/rtt/scripting/ValueChangeParser.hpp index 5d368faa3..d1113cec1 100644 --- a/rtt/scripting/ValueChangeParser.hpp +++ b/rtt/scripting/ValueChangeParser.hpp @@ -62,6 +62,7 @@ namespace RTT { namespace scripting // all the AssignVariableCommand we've built.. // This list is cleared in cleanup(). std::vector assigncommands; + std::vector conditions; // the defined values... // This list is cleared in cleanup(). @@ -166,6 +167,11 @@ namespace RTT { namespace scripting return assigncommands; } + std::vector assignConditions() + { + return conditions; + } + base::AttributeBase* lastDefinedValue() { if ( definedvalues.empty() ) diff --git a/tests/operations_fixture.hpp b/tests/operations_fixture.hpp index 4ccf53969..6c75038de 100644 --- a/tests/operations_fixture.hpp +++ b/tests/operations_fixture.hpp @@ -71,7 +71,7 @@ class RTT_UNIT_API OperationsFixture { void printNumber(const std::string& what, int n) { cout << "print: " << what << n << endl; } bool fail() { - throw false; + throw std::runtime_error("OperationsFixture::fail() called."); return true; // should be ignored anyway. } @@ -80,7 +80,8 @@ class RTT_UNIT_API OperationsFixture { } bool assertBool(bool b) { - if (!b) throw b; + if (!b) + throw std::runtime_error("OperationsFixture::assertBool( b ) failed (arg was false)."); return b; } @@ -92,18 +93,19 @@ class RTT_UNIT_API OperationsFixture { { if (a != b) { cerr << "AssertEqual failed: a != b " << a << " != " << b << "." << endl; - throw b; + throw std::runtime_error("OperationsFixture::assertEqual( a, b ) failed (a != b)."); } return a == b; } bool assertMsg( bool b, const std::string& msg) { if ( b == false ) { cout << "Asserted :" << msg << endl; - throw b; + throw std::runtime_error("OperationsFixture::assertMsg( b, msg ) faild (b was false)."); } return true; // allow to continue to check other commands. } int increase() { return ++i;} + void resetI() { i = 0; } int getI() const { return i; } int i; diff --git a/tests/operations_fixture0.cpp b/tests/operations_fixture0.cpp index 5095e1871..6e119dee2 100644 --- a/tests/operations_fixture0.cpp +++ b/tests/operations_fixture0.cpp @@ -40,11 +40,12 @@ void OperationsFixture::createOperationCallerFactories0(TaskContext* target) to->addOperation("m0cr", &OperationsFixture::m0cr, this).doc("M0cr"); to->addOperation("vm0", &OperationsFixture::vm0, this).doc("VoidM0"); to->addOperation("m0", &OperationsFixture::m0, this).doc("M0"); - to->addOperation("m0except", &OperationsFixture::m0except, this, RTT::ClientThread ).doc("M0Except"); + to->addOperation("m0except", &OperationsFixture::m0except, this).doc("M0Except"); // OwnThread to->addOperation("o0r", &OperationsFixture::m0r, this, OwnThread).doc("M0r"); to->addOperation("o0cr", &OperationsFixture::m0cr, this, OwnThread).doc("M0cr"); to->addOperation("o0", &OperationsFixture::m0, this, OwnThread).doc("M0"); + to->addOperation("vo0", &OperationsFixture::vm0, this, OwnThread).doc("VoidM0"); to->addOperation("o0except", &OperationsFixture::m0except, this, OwnThread).doc("M0Except"); } diff --git a/tests/program_test.cpp b/tests/program_test.cpp index 91430035d..9ce873c01 100644 --- a/tests/program_test.cpp +++ b/tests/program_test.cpp @@ -47,25 +47,28 @@ class ProgramTest : public OperationsFixture ScriptingService::shared_ptr sa; int var_i; int const_i; + SendStatus tss; void doProgram( const std::string& prog, TaskContext*, bool test=true ); void finishProgram( TaskContext* , std::string ); void loopProgram( ProgramInterfacePtr ); ProgramTest() - : sa( ScriptingService::Create(tc) ) + : parser(tc->engine()), sa( ScriptingService::Create(tc) ) { tc->stop(); BOOST_REQUIRE( tc->setActivity(new SimulationActivity(0.01)) ); BOOST_REQUIRE( tc->start() ); tc->provides()->addService( sa ); tc->provides()->addAttribute("tvar_i", var_i); + tc->provides()->addAttribute("tss", tss); tc->provides()->addConstant("tconst_i", const_i); // ltc has a test object const_i = -1; var_i = -1; i = 0; + tss = SendNotReady; SimulationThread::Instance()->stop(); } @@ -480,32 +483,73 @@ BOOST_AUTO_TEST_CASE(testProgramCallFoo) + "set tvar_i = +2\n" + "do test.assert( tvar_i == +2 )\n" + "call foo()\n" + + "do test.assert( tvar_i == +4 ) \n" + "}"; this->doProgram( prog, tc ); - Attribute i = tc->provides()->getAttribute("tvar_i"); - BOOST_REQUIRE_EQUAL( 4, i.get() ); + BOOST_REQUIRE_EQUAL( 4, var_i ); this->finishProgram( tc, "x"); } -BOOST_AUTO_TEST_CASE(testProgramDoFoo) +BOOST_AUTO_TEST_CASE(testProgramSendFoo) { // see if modifying an attribute works. - string prog = string("export function foo {\n") - + " do test.assert( tvar_i == +2 ) \n" + string prog = string("export function foo(int arg) {\n") + + " do test.assert( tvar_i == arg ) \n" + " do test.assert( tvar_i != tconst_i ) \n" - + " set tvar_i = +4\n" - + " do test.assert( tvar_i == +4 ) \n" + + " set tvar_i = tvar_i+2\n" + + " do test.assert( tvar_i == arg + 2 ) \n" + "}\n" + "program x { \n" + "do test.assert( tvar_i == -1 ) \n" + "do test.assert( tvar_i == tconst_i ) \n" - + "set tvar_i = +2\n" - + "do test.assert( tvar_i == +2 )\n" - + "do foo()\n" + + "set tvar_i = +2\n" // 10 + + "var SendHandle sh\n" + + "sh = foo.send(tvar_i)\n" + + "while(sh.collectIfDone() != SendSuccess)\n" + + " yield\n" + + "do test.assert( sh.collectIfDone() == SendSuccess )\n" + + "do test.assert( tvar_i == +4 )\n" + + // test parallel send + + "var SendHandle sh1, sh2\n" + + "sh1 = foo.send(tvar_i)\n" + + "sh2 = foo.send(tvar_i + 2)\n" + + "while(sh2.collectIfDone() != SendSuccess)\n" + + " yield\n" + + "do test.assert( tvar_i == +8 )\n" + + "do test.assert( sh1.collectIfDone() == SendSuccess )\n" + + "do test.assert( sh2.collectIfDone() == SendSuccess )\n" + + "do test.assert( tvar_i == +8 )\n" + + "}"; + this->doProgram( prog, tc ); + BOOST_REQUIRE_EQUAL( 8, var_i ); + this->finishProgram( tc, "x"); +} + +BOOST_AUTO_TEST_CASE(testProgramCmdFoo) +{ + // see if modifying an attribute works. + string prog = string("export function foo(int arg) {\n") + + " do test.assert( tvar_i == arg ) \n" + + " do test.assert( tvar_i != tconst_i ) \n" + + " set tvar_i = tvar_i+2\n" + + " do test.assert( tvar_i == arg + 2 ) \n" + + "}\n" + + "program x { \n" + + "do test.assert( tvar_i == -1 ) \n" + + "do test.assert( tvar_i == tconst_i ) \n" + + "set tvar_i = +2\n" // 10 + + "while (tvar_i != +6) {\n" + + " tss = foo.cmd(tvar_i)\n" + + " do test.assert( tss == SendSuccess )\n" + + "}\n" + + "do test.assert( tvar_i == +6 )\n" + + "tss = foo.cmd(tvar_i)\n" + + "do test.assert( tvar_i == +8 )\n" + + "do test.assert( tss == SendSuccess )\n" + "}"; this->doProgram( prog, tc ); - Attribute i = tc->provides()->getAttribute("tvar_i"); - BOOST_REQUIRE_EQUAL( 4, i.get() ); + BOOST_REQUIRE_EQUAL( 8, var_i ); this->finishProgram( tc, "x"); } @@ -518,17 +562,23 @@ BOOST_AUTO_TEST_CASE(testSend) + "test.increaseCmd.send() \n" + "yield \n" + "test.assertEqual( test.i, 1 )\n" + + "yield \n" // make sure that increaseCmd is not evaluated twice! + + "test.assertEqual( test.i, 1 )\n" + + "var SendHandle sh\n" + "set sh = test.increaseCmd.send()\n" + + "test.assertEqual( test.i, 1 )\n" // not yet send + "var int r = 0\n" //+ "sh.collect(r)\n" // hangs + "while (sh.collectIfDone(r) != SendSuccess)\n" - + "yield \n" + + " yield \n" + "test.assertEqual( r , 2 )\n" + + "test.assertEqual( test.i, 2 )\n" + + "set sh = test.increaseCmd.send()\n" //+ "sh.collect(tvar_i)\n" // hangs + "while (sh.collectIfDone(tvar_i) != SendSuccess)\n" - + "yield \n" + + " yield \n" + "test.assertEqual( tvar_i, 3 )\n" // i is 3 but r isn't. + "}"; this->doProgram( prog, tc ); @@ -537,6 +587,31 @@ BOOST_AUTO_TEST_CASE(testSend) this->finishProgram( tc, "x"); } +BOOST_AUTO_TEST_CASE(testCmd) +{ + // see if modifying an attribute works. + string prog = string("") + + "program x { \n" + + "test.assertEqual( test.i, 0 )\n" + + "var SendStatus ss\n" + + "tss = test.increaseCmd.cmd() \n" + + "test.assert( tss == SendSuccess )\n" + + "test.assertEqual( test.i, 1 )\n" + + "ss = test.increaseCmd.cmd()\n" + + "test.assert( ss == SendSuccess )\n" + + "test.assertEqual( test.i , 2 )\n" + + + "tss = methods.vo0.cmd() \n" + + "test.assert( tss == SendSuccess )\n" + + "ss = methods.vo0.cmd()\n" + + "test.assert( ss == SendSuccess )\n" + + "}"; + this->doProgram( prog, tc ); + BOOST_REQUIRE( tss == SendSuccess ); + BOOST_CHECK_EQUAL( i, 2 ); + this->finishProgram( tc, "x"); +} + BOOST_AUTO_TEST_SUITE_END() void ProgramTest::doProgram( const std::string& prog, TaskContext* tc, bool test ) diff --git a/tests/state_test.cpp b/tests/state_test.cpp index f2ef37174..cbe40ceb2 100644 --- a/tests/state_test.cpp +++ b/tests/state_test.cpp @@ -70,6 +70,10 @@ class StateTest RTT::rt_string mrt_state; + int var_i; + int const_i; + SendStatus tss; + void log(const std::string& msg) { Logger::log(Logger::Info) << msg << endlog(); } @@ -127,6 +131,14 @@ class StateTest SimulationThread::Instance()->stop(); tc->addOperation("log", &StateTest::log, this); + + tc->provides()->addAttribute("tvar_i", var_i); + tc->provides()->addAttribute("tss", tss); + tc->provides()->addConstant("tconst_i", const_i); + + const_i = -1; + var_i = -1; + tss = SendNotReady; } ~StateTest(){ } @@ -850,6 +862,141 @@ BOOST_AUTO_TEST_CASE( testStateYieldbySend ) this->finishState( "x", tc); } +BOOST_AUTO_TEST_CASE( testStateYieldbyCmd ) +{ + // test yielding and checking .cmd syntax + string prog = string("StateMachine X {\n") + + " initial state INIT {\n" + + " var double d = 0.0\n" + + " run {\n" + + " test.assertEqual( test.i, 0 )\n" + + " var SendStatus ss\n" + + " ss = test.increaseCmd.cmd() \n" + + " test.assert( ss == SendSuccess )\n" + + " test.assertEqual( test.i, 1 )\n" + + " ss = test.increaseCmd.cmd()\n" + + " test.assert( ss == SendSuccess )\n" + + " test.assertEqual( test.i , 2 )\n" + + + " tss = methods.vo0.cmd() \n" // bug : does not evaluate conditions ! + + " test.assert( tss == SendSuccess )\n" + + " tss = methods.vo0.cmd()\n" + + " test.assert( tss == SendSuccess )\n" + + " }\n" + + " transitions {\n" + + " select FINI\n" + + " }\n" + + " }\n" + + " final state FINI {\n" // Success state. + + " entry { do test.assert(true); }\n" + + " }\n" + + " }\n" + + " RootMachine X x\n" // instantiate a non hierarchical SC + ; + this->doState("x", prog, tc ); + BOOST_CHECK( sa->getStateMachine( "x" )->inState("FINI") ); + this->finishState( "x", tc); +} + +BOOST_AUTO_TEST_CASE( testStateSendFunction ) +{ + // test yielding and checking .send syntax + string func = string("export function foo(int arg) {\n") + + " do test.assert( tvar_i == arg ) \n" + + " do test.assert( tvar_i != tconst_i ) \n" + + " set tvar_i = tvar_i+2\n" + + " do test.assert( tvar_i == arg + 2 ) \n" + + "}\n"; + string prog = string("StateMachine X {\n") + + " initial state INIT {\n" + + " run {\n" + + " tvar_i = 0\n" + + + " var SendHandle sh, sh2\n" + + " sh = foo.send(tvar_i) \n" + + + " sh\n" // tests accidental sh evaluation + + + " test.assert( sh.collectIfDone() == SendNotReady )\n" + + " test.assertEqual( tvar_i, 0 )\n" + + + " while ( sh.collectIfDone() == SendNotReady) \n" + + " yield\n" + + " test.assert( sh.collectIfDone() == SendSuccess )\n" + + " test.assertEqual( tvar_i, 2 )\n" + + + " sh2 = foo.send(tvar_i) \n" + + " test.assert( sh2.collectIfDone() == SendNotReady )\n" + + " test.assertEqual( tvar_i, 2 )\n" + + " while ( sh2.collectIfDone() == SendNotReady) \n" + + " yield\n" + + " test.assert( sh2.collectIfDone() == SendSuccess )\n" + + " test.assertEqual( tvar_i , 4 )\n" + + + " }\n" + + " transitions {\n" + + " select FINI\n" + + " }\n" + + " }\n" + + " final state FINI {\n" // Success state. + + " entry { do test.assert(true); }\n" + + " }\n" + + " }\n" + + " RootMachine X x\n" // instantiate a non hierarchical SC + ; + BOOST_REQUIRE( sa->loadPrograms(func, "func.ops", false) ); + this->doState("x", prog, tc, true, 25 ); + BOOST_CHECK( sa->getStateMachine( "x" )->inState("FINI") ); + this->finishState( "x", tc); +} + +BOOST_AUTO_TEST_CASE( testStateCmdFunction ) +{ + // test yielding and checking .cmd syntax + string func = string("export function foo(int arg) {\n") + + " do test.assert( tvar_i == arg ) \n" + + " do test.assert( tvar_i != tconst_i ) \n" + + " set tvar_i = tvar_i+2\n" + + " do test.assert( tvar_i == arg + 2 ) \n" + + "}\n"; + string prog = string("StateMachine X {\n") + + " initial state INIT {\n" + + " run {\n" + + " var SendStatus ss\n" + + " tvar_i = 0\n" + + + " ss = foo.cmd(tvar_i)\n" + + " test.assert( ss == SendSuccess )\n" + + " test.assertEqual( tvar_i, 2 )\n" + + + " ss = foo.cmd(tvar_i)\n" + + " test.assert( ss == SendSuccess )\n" + + " test.assertEqual( tvar_i , 4 )\n" + + + " tss = foo.cmd(tvar_i)\n" + + " test.assert( tss == SendSuccess )\n" + + " tss = foo.cmd(tvar_i)\n" + + " test.assert( tss == SendSuccess )\n" + + " test.assertEqual( tvar_i , 8 )\n" + + + " }\n" + + " transitions {\n" + + " select FINI\n" + + " }\n" + + " }\n" + + " final state FINI {\n" // Success state. + + " entry { do test.assert(true); }\n" + + " }\n" + + " }\n" + + " RootMachine X x\n" // instantiate a non hierarchical SC + ; + BOOST_REQUIRE( sa->loadPrograms(func, "func.ops", false) ); + this->doState("x", prog, tc, true, 25 ); + BOOST_CHECK( sa->getStateMachine( "x" )->inState("FINI") ); + this->finishState( "x", tc); +} + + BOOST_AUTO_TEST_CASE( testStateGlobalTransitions) { // test processing of transition statements.