Orocos OperationCaller 解析
来源:互联网 发布:淘宝刷皇冠多少钱 编辑:程序博客网 时间:2024/06/18 18:38
在 Orocos 中 OperatoinCaller 表示一个可以调用其他模块的函数的对象, 如果一个模块添加了一个 Operation,则该 Operation 表示的函数可以被其他模块的 OperationCaller 调用。
在连接 ( connectService ) 调用端(OperationCaller)和被调用端(Operation)之后,该 OperatoinCaller 就能够执行其他模块对应的函数。这里涉及到两个对象,ServiceRequester 和 Serivce,前者 拥有一个 OperatoinCaller 的 map, 后者拥有一个 Operation 的 map。
另外,一个 OperatoinCaller 可以在本模块的 ExecutionEngine 中执行,也可以在被调端的 ExecutionEngine 中执行。
其中一个关键的类为 OperationCallerBase
,因为他分别被 Operation 和 OperationCaller 继承,充当两边的接口类,并且在两边都有实现,该类可以分成两个部分 :
template<class F> // 这个F是函数原型,例如: bool(int)struct OperationCallerBase : public internal::InvokerBase<F>, public OperationCallerInterface{ typedef boost::shared_ptr<OperationCallerBase<F> > shared_ptr; virtual ~OperationCallerBase() {} virtual OperationCallerBase<F>* cloneI(ExecutionEngine* caller) const = 0;};
第一个部分:其中子类 OperationCallerInterface 中有两个指针,分别保存着自己的和另外模块的 ExecutionEngine:
struct RTT_API OperationCallerInterface: public DisposableInterface{ ...protected: ExecutionEngine* myengine; ExecutionEngine* caller;}
第二个部分:子类 InvokerBase 则保存着被调用的函数原型(这样在匹配 Operation 和 OperationCaller 类的时候可以知道函数签名是否相同——dynamic_cast,并且可以定义统一的调用形式——call() 函数):
template<int, class F>struct InvokerBaseImpl;/** * This is the base class that defines the interface * of all invocable method implementations. * Any invocable method implementation must inherit * from this class such that it can be used transparantly * by the OperationCaller, Operation and SendHandle containers. */template<class F>struct InvokerBase : public InvokerBaseImpl<boost::function_traits<F>::arity, F>{};template<class F>struct InvokerBaseImpl<0,F>{ typedef typename boost::function_traits<F>::result_type result_type; typedef typename boost::function_traits<F>::result_type result_reference; virtual ~InvokerBaseImpl() {} virtual SendHandle<F> send() = 0; virtual result_type call() = 0;};template<class F>struct InvokerBaseImpl<1,F>{ typedef typename boost::function_traits<F>::result_type result_type; typedef typename boost::function<F>::arg1_type arg1_type; virtual ~InvokerBaseImpl() {} virtual result_type call(arg1_type a1) = 0; virtual SendHandle<F> send(arg1_type a1) = 0;};...
关于如何关联 Operation 和 OperationCaller:
Operation(被调用端) 和 OperationCaller(调用端) 之间共享一个智能指针(internal::LocalOperationCaller<Signature>::shared_ptr impl
):
// 在 Operation 端:impl = boost::make_shared<internal::LocalOperationCaller<Signature> >( boost::function<Signature>(), this->mowner, null_caller, ClientThread)// 在 OperationCaller 端:boost::dynamic_pointer_cast< base::OperationCallerBase<Signature> >(impl)// 上面这个地方是关键,如果Operation端和OperationCaller端定义的函数签名不同,dynamic_pointer_cast会得到一个空指针,并报告函数签名不同。
该智能指针指向一个可调用的对象 ( LocalOperationCaller,该对象中有个成员变量 boost::function ),这个 LocalOperationCaller
类才是orocos所有底层的调用的实现类。
如何被调用:
写到这里,到了选择如何调用这个 boost::function 的时候了。有两个选择,如果是在调用端(Caller)执行,则只需要直接调用即可;但是如果在被调用端(Operation)的线程中执行,则需要保存参数,保存函数指针,等到线程被执行时再进行函数的调用。简要如下:
class LocalOperationCaller ...LocalOperationCallerImpl ...template<class T1>result_type call_impl(T1 a1){ SendHandle<Signature> h; if ( this->isSend() ) { // true 表示在Operation的线程中执行, false表示在 Caller 的线程中执行 // 如果在 Operation 的线程中执行,则需要保存参数,将该函数添加到 ExecutionEngine 的函数指针 Queue 中, 然后到了 ExecutionEngine 的执行点再执行 // 见下述 RStore 和 BindStorage 类 h = send_impl<T1>(a1); if ( h.collect() == SendSuccess ) return h.ret(a1); else throw SendFailure; } else{ // 如果在 Caller 的线程中执行就简单了,直接调用即可. if ( this->mmeth ) return this->mmeth(a1); else return NA<result_type>::na(); } return NA<result_type>::na();}
如果是在被调用端的线程中执行,则需要处理参数,返回值,添加额外信息以查询是否被执行过,是否有错误发生等等。
调用的过程大概是这样子的:
- 以自己为模板,新建(
new
)一个LocalOperationCaller
对象,并赋值给 shared_ptr:shared_ptr cl = this->cloneRT();
- 保存调用函数的参数 :
cl->store( a1, a2, ... );
- 将该 shared_ptr 的指针添加到执行该函数的线程类的 Queue 中(该线程会在执行时从Queue 中 pop 函数指针并执行! see link ExecutionEngine 实现)
- 等待该函数被线程调用(时间不确定,因此需要wait,通过信号量和 Mutex 来实现互锁等待 (see link) 其效果就是调用端的线程被挂起,等待): this->caller->waitForMessages(boost::bind(&RStoreType::isExecuted,boost::ref(this->retv)) );
其中 waitForMessages 的函数可简要表示如下:
{
if ( pred() ) // pred 即第四步中 boost::bind 返回的可调用对象
return;
// only to be called from the thread not executing step().
os::MutexLock lock(msg_lock);
while (!pred()) { // the mutex guards that processMessages can not run between !pred and the wait().
msg_cond.wait(msg_lock); // unlock & 阻塞,并且只有 pred() 返回true才能被唤醒。
} - 返回结果,并销毁在第一部
new
出来的对象。
调用的返回值被封装成 RStore
对象(这样就可以判断该函数是否已被执行过,是否在调用的时候出错——try catch…),见下述 RStore
类和 BindStorage
类(其中 BindStorage 类拥有一个 RStore 类的成员变量 retv):
// 返回值对象template<>struct RStore<void> { bool executed; // 判断对应的函数是否已被调用过 bool error; //判断在调用过程中是否出错 RStore() : executed(false), error(false) {} void checkError() const { if(error) throw std::runtime_error("Unable to complete the operation call. The called operation has thrown an exception"); } bool isError() const { return error; } bool isExecuted() const { // 就是第四部中 waitForMessages 中调用的函数,判断是否已经被执行过了 return executed; } template<class F> void exec(F f) { error = false; try{ f(); // 最终调用函数的地方 } catch (std::exception& e) { log(Error) << "Exception raised while executing an operation : " << e.what() << endlog(); error = true; } catch (...) { log(Error) << "Unknown exception raised while executing an operation." << endlog(); error = true; } executed = true; } void result() { checkError(); return; }};// ...其他类型的返回值对象的定义 继承自 RStore<void> ... 如://template<class T>// struct RStore : public RStore<void> {// T arg; // 实际返回值, 调用过程为 arg = f();// ... //}template<class ToBind>struct BindStorage // 就是这个类,保存着调用的实体 : public BindStorageImpl<boost::function_traits<ToBind>::arity, ToBind>{};template<class ToBind>struct BindStorageImpl<1, ToBind>{ typedef typename boost::function_traits<ToBind>::result_type result_type; typedef typename boost::function_traits<ToBind>::arg1_type arg1_type; typedef RStore<result_type> RStoreType; // RStore 类 // stores the original function pointer, supplied by the user. boost::function<ToBind> mmeth; // 被调用的函数对象 // Store the argument. mutable AStore<arg1_type> a1; // 参数对象 1,参数也需要保存,因为有些函数是传递引用的,参数也可能会被修改。 mutable RStore<result_type> retv; // 返回值对象 // the list of all our storage. bf::vector< RStore<result_type>&, AStore<arg1_type>& > vStore;#ifdef ORO_SIGNALLING_OPERATIONS typename Signal<ToBind>::shared_ptr msig;#endif BindStorageImpl() : vStore(retv,a1) {} BindStorageImpl(const BindStorageImpl& orig) : mmeth(orig.mmeth), vStore(retv,a1)#ifdef ORO_SIGNALLING_OPERATIONS , msig(orig.msig)#endif {} void store(arg1_type t1) { a1(t1); } // 保存函数的各个参数 void exec() {#ifdef ORO_SIGNALLING_OPERATIONS if (msig) (*msig)(a1.get());#endif if (mmeth) retv.exec( boost::bind(mmeth, boost::ref(a1.get()) ) ); // 调用函数 else retv.executed = true; }// ...234567个参数的定义形式...
总结:
总结下来就是将所有的东西都对象化;参数,返回值、函数等都是一个个对象
在没有 connectService 之前,OperationCaller 为空,在 connectService 之后会调用setImplementation(获得一个智能指针),并创建一个新的 OperationCaller, 然后让自身等于这个新的 OperationCaller (*this=tmp), 现在这个新的 OperationCaller 代表着一个可以调用其他模块函数的对象:
OperationCaller& operator=(boost::shared_ptr<base::DisposableInterface> implementation) // 关键!该智能指针指向 Operation 的成员: LocalOperationCaller, 此处关联 Operation 和 OperationCalle 两个对象{ if (this->impl && this->impl == implementation) return *this; OperationCaller<Signature> tmp(implementation, mcaller); *this = tmp; // 更新自己为带 impl return *this;}
而调用这个 OperationCaller 会执行如下过程:
result_type OperationCaller::operator()(){ // impl为一个智能指针:boost::shared_ptr< base::OperationCallerBase<SignatureT> // 该智能指针在 setImplementation 之后非空,否则为空 if (impl) return impl->call(); // 所以最后又回到了调用 LocalOperationCaller 的 call 函数,然后判断是否在被调用端的线程中执行,详见 “如何被调用” 段落。 if ( this->mmeth ) return this->mmeth(arg1, arg2 ...); else return NA<result_type>::na(); return NA<result_type>::na(); // 否则返回一个默认的返回值}
Orocos Operation & OperationCaller的继承关系图:
综上所述,关键变量为一个 shared_ptr — impl
,其指向的是一个 boost::function 的对象的wrapper(LocalOperationCaller), 传递这个 shared_ptr 即传递了这个函数的调用实体,因此 OperationCaller 端的 operator()
调用的是 imp->call()
这个函数,最终完成对具体对象的成员函数的调用。
除了用 OperationCaller 调用 另一个模块的 Operation 之外,也可以直接获取 Service 的 OperationInterfacePart 指针。
在 rtt/Service.hpp
文件中,addOperation 的时候同时创建 OperationInterfacePartFused
类,该类继承自 OperationInterfacePart
。直接调用 Service::getPart 函数即可获取对应的 OperationInterfacePart——即对应的 Operation。
使用的关键技术: boost::fusion::invoke, 见文件 rtt/rtt/internal/FusedFunctorDataSource.hpp
的 evaluate()
调用。
see link: http://www.boost.org/doc/libs/1_63_0/libs/fusion/doc/html/fusion/functional/invocation/functions/invoke.html
- Orocos OperationCaller 解析
- typeinfo dynamic_cast & 模板编程 & orocos OperationCaller 类的设计
- Orocos Activity&ExecutionEngine 解析
- Orocos DataPort 解析: orocos lock free data object
- OROCOS 网上注释文档
- orocos 类型系统分析
- 机器人开源项目:orocos
- orocos Logger 类的设计
- 开源机器人控制软件OROCOS
- Orocos Real-Time Toolkit 2.6.0
- ROS节点与OROCOS组件通信--安装
- orocos 模块的构造 和 析构 顺序
- orocos xenomai dlopen 内存权限问题:
- 开源机器人控制软件OROCOS
- ROS节点与OROCOS组件通信--构建组件
- Orocos ExecutionEngine 对函数Operation调用的实现
- Orocos 无锁的 Muliti Writer Single Reader Queue
- orocos 的数据表示(模板template): DataObject (还有一条主线为 DataSource)
- Java-内存结构
- android与服务端的的http请求数据交互
- 【swift基础】04字符串
- mxnet
- 350. Intersection of Two Arrays II
- Orocos OperationCaller 解析
- java按行读取文件,可做模板(简单易懂),大家一起学习
- [leetcode] 231. Power of Two
- Linux下文件描述符
- 关于csdn上的博客搬家的问题
- 【swift基础】05数组
- 007_Android Studio 2.1.0 导入Android Studio 项目 步骤详解(附详细截图)
- 贝叶斯学习、MAP、ML
- 第一行代码读书笔记——数据存储全方案,持久化技术