转载说明: 感谢简书原作者play_robot的分享!
著作权归原作者所有,如有侵权,请联系我删除,谢谢!
原文地址: https://www.jianshu.com/p/af9adf450dad

文章目录

  • 1. MessageManager 工作原理
  • 2. MessageManager 源码分析
  • 3. spinOnce()源码分析
  • 4. spin()源码分析

1. MessageManager 工作原理

MessageManager通过它的通信连接接收simple message。而后基于收到的message类型调用相应的回调函数,回调函数则执行相应的操作,以及根据需要作出消息应答。

MessageManager有两种工作模式: spin()spinOnce()

spin的执行是阻塞式的,而spinOnce是执行一次单独的操作。 因此,在spinOnce模式下,程序可以同时干其它事情,但是要确保执行spinOnce的频率足够高,这样不至于丢失通信数据。

2. MessageManager 源码分析

namespace industrial
{namespace message_manager
{class MessageManager
{public:MessageManager();~MessageManager();bool init(industrial::smpl_msg_connection::SmplMsgConnection* connection);bool init(industrial::smpl_msg_connection::SmplMsgConnection* connection,industrial::comms_fault_handler::CommsFaultHandler* fault_handler);void spinOnce();void spin();bool add(industrial::message_handler::MessageHandler* handler, bool allow_replace = false);unsigned int getNumHandlers(){return this->num_handlers_;}unsigned int getMaxNumHandlers(){return this->MAX_NUM_HANDLERS;}industrial::comms_fault_handler::CommsFaultHandler* getCommsFaultHandler(){return this->comms_hndlr_;}void setCommsFaultHandler(industrial::comms_fault_handler::CommsFaultHandler* handler){this->comms_hndlr_ = handler;}private:static const unsigned int MAX_NUM_HANDLERS = 64;industrial::message_handler::MessageHandler* handlers_[MAX_NUM_HANDLERS];industrial::smpl_msg_connection::SmplMsgConnection* connection_;industrial::ping_handler::PingHandler ping_hndlr_;industrial::simple_comms_fault_handler::SimpleCommsFaultHandler def_comms_hndlr_;industrial::comms_fault_handler::CommsFaultHandler* comms_hndlr_;unsigned int num_handlers_;industrial::message_handler::MessageHandler* getHandler(int msg_type);int getHandlerIdx(int msg_type);industrial::simple_comms_fault_handler::SimpleCommsFaultHandler& getDefaultCommsFaultHandler(){return this->def_comms_hndlr_;}industrial::ping_handler::PingHandler& getPingHandler(){return this->ping_hndlr_;};void setConnection(industrial::smpl_msg_connection::SmplMsgConnection* connection){this->connection_ = connection;};industrial::smpl_msg_connection::SmplMsgConnection* getConnection(){return this->connection_;};void setNumHandlers(unsigned int num_handlers){this->num_handlers_ = num_handlers;};};} // namespace industrial
} // namespace message_manager

先来看一下它的私有成员:

类型 变量符号 含义
int MAX_NUM_HANDLERS 消息处理器的最大数目
int num_handlers_ 消息处理器的实际数目
MessageHandler* handlers_[MAX_NUM_HANDLERS] 存放消息处理器的指针数组
SmplMsgConnection* connection_ 通信使用的连接
PingHandler ping_hndlr_ ping消息处理器
SimpleCommsFaultHandler def_comms_hndlr_ 默认的通信错误处理器
CommsFaultHandler* comms_hndlr_ 用户指定的通信错误处理器

在使用MessageManager时,外部需要先初始化好connection,然后传入init以初始化MessageManager使用的连接和错误处理器,此外还将初始化ping_hndlr_对象,并调用add将ping_hndlr_存放到handlers_数组中,保存的目的就在于当遇到ping message时,MessageManager就会调用PingHandler来处理该消息。

参考关于ros消息发布器和订阅器的教程, 消息发布器在一个while循环内一直循环发送“hello world”到话题(topic)chatter上。消息订阅器一旦知道chatter上面有data,就会将这data作为参数传入callback函数中,但是此时还没有执行callback函数,而是把callback函数放到了一个回调函数队列中。所以当发布器不断发送data到chatter上面时,就会有相应的callback函数进入队列中,它们函数名一样,只是实参不一样。


3. spinOnce()源码分析

下面分析spinOnce代码:

void MessageManager::spinOnce()
{SimpleMessage msg;MessageHandler* handler = NULL;if(!this->getConnection()->isConnected()){this->getCommsFaultHandler()->connectionFailCB();}if (this->getConnection()->receiveMsg(msg)){LOG_COMM("Message received");handler = this->getHandler(msg.getMessageType());if (NULL != handler){LOG_DEBUG("Executing handler callback for message type: %d", handler->getMsgType());handler->callback(msg);}else{if (CommTypes::SERVICE_REQUEST == msg.getCommType()){simple_message::SimpleMessage fail;fail.init(msg.getMessageType(), CommTypes::SERVICE_REPLY, ReplyTypes::FAILURE);this->getConnection()->sendMsg(fail);LOG_WARN("Unhandled message type encounters, sending failure reply");}LOG_ERROR("Message callback for message type: %d, not executed", msg.getMessageType());}}else{LOG_ERROR("Failed to receive incoming message");this->getCommsFaultHandler()->sendFailCB();}
}
  • 进入spinOnce后,首先会检查是否处于连接状态,如果连接断开则触发通信错误处理器的连接失败回调函数connectionFailCB。
  • 接着将尝试接收一条SimpleMessage消息,如果接收到消息,则根据消息类型寻找能处理该消息的处理器handler,找到后则触发处理的回调函数callback对接收到的消息进行处理。
  • 当spinOnce函数被调用时,spinOnce就会调用回调函数队列中第一个callback函数,此时callback函数才被执行,然后等到下次spinOnce函数又被调用时,回调函数队列中第二个callback函数就会被调用,以此类推。
  • 所以,这会有一个问题。因为回调函数队列的长度是有限的,如果发布器发送数据的速度太快,spinOnce函数调用的频率太少,就会导致队列溢出,一些callback函数就会被挤掉,导致没被执行到。

4. spin()源码分析

对于spin方法,它是基于spinOnce实现的,进入该方法后,程序将进入内部的死循环,持续调用spinOnce:

void MessageManager::spin()
{LOG_INFO("Entering message manager spin loop");
#ifdef ROSwhile (ros::ok())
#elsewhile (true)
#endif{this->spinOnce();// Throttle loop speed if waiting for a re-connectionif (!this->getConnection()->isConnected())mySleep(5);}
}

参考roswiki: Writing a Simple Publisher and Subscriber (C++),摘抄了一段关于spin()以及spinOnce()的描述:

ros::spin() enters a loop, calling message callbacks as fast as possible. Don’t worry though, if there’s nothing for it to do it won’t use much CPU.
ros::spin() will exit once ros::ok() returns false, which means ros::shutdown() has been called, either by the default Ctrl-C handler, the master telling us to shutdown, or it being called manually.

简述下来就是,当无事可做时,ros::spin()只占据CPU很少的资源。只要回调函数队列里面有callback函数在,它就会马上去执行callback函数。如果没有的话,它就会阻塞,不会占用CPU。
ros::ok()返回false时,ros::spin()结束并退出,这就意味着ros::shutdown()被调用,或者用户在终端执行了ctrl + c命令,master节点告诉我们要shutdown。


MessageManager就分析到这里,后面再举例分析在更上层的代码中是如何使用它的。

注: 可以参考以下网站,对比学习

  1. industrial::message_manager::MessageManager
  2. docs.ros.org - simple_message

[转] ROS-I simple_message 源码分析:MessageManager相关推荐

  1. 5-LVI-SAM源码分析_imageProjection初步分析

    2021SC@SDUSC LVI-SAM源码分析_imageProjection.cpp初步分析 文章目录 LVI-SAM源码分析_imageProjection.cpp初步分析 一.点云结构体 二. ...

  2. 【ROS-Navigation】Costmap2d代价地图源码分析——ObstacleLayer障碍物层

    在学习ROS-Navigation源码过程中,记录自己的理解,本文为分层代价地图中障碍物层源码的学习笔记,针对obstacle_layer.h和obstacle_layer.cpp源码文件,分析障碍物 ...

  3. Apollo 2.0 框架及源码分析(一) | 软硬件框架

    原文地址:https://zhuanlan.zhihu.com/p/33059132 前言 如引言中介绍的,这篇软硬件框架多为现有消息的整合加一些个人的想法.关于 Apollo 介绍的文章已经有许多, ...

  4. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  5. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  6. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  7. SpringBoot-web开发(一): 静态资源的导入(源码分析)

    目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...

  8. Yolov3Yolov4网络结构与源码分析

    Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...

  9. ViewGroup的Touch事件分发(源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,View的touch事件分发相对比较简单,可参考 View的Touch事件分发(一.初步了解) View的Touch事 ...

最新文章

  1. 智办事2.0,第一个提出以“事情”为中心的企业管理方法落地软件
  2. python类中方法的执行顺序-浅谈Python的方法解析顺序(MRO)
  3. SVN用户验证,调错
  4. SpringCloud 应用在 Kubernetes 上的最佳实践 — 部署篇(工具部署)
  5. OpenJudge 2739 计算对数
  6. OO第二单元作业总结
  7. 程序员界年度人口普查:6成以上开发者日工作超9小时,且从不运动
  8. 虚拟化部署之Windows 7中远程管理Hyper-V
  9. HigLabo.Mapper,用表达式树在10天内创建世界上最快的对象映射器
  10. 已知若干点求圆心_【求精干货】高中数学知识点总结归纳高一学生必须掌握
  11. windows2008开机占用多少内存_如何提升电脑开机速度?
  12. win10系统怎么把语言栏弄回去
  13. linux 蓝牙驱动
  14. Windows运行机理——主程序—WinMain
  15. 写 git commit message 的错误姿势 —— whatthecommit.com 到底说了些什么
  16. Ardupilot代码学习笔记
  17. 思科交换机指示灯全解
  18. TeamCenter怎样删除已发布物料
  19. 淘宝API接口系列,获取购买到的商品订单列表,卖出的商品订单列表,订单详情,订单物流,买家信息,收货地址列表,买家token
  20. python爬虫入门案例day10:珠宝图片

热门文章

  1. dedecms 文章回收站 记得及时清理
  2. 用标准 GHOST镜像xpe系统(EWF保护模式为RAM)时,写保护丢失问题的解决方法
  3. [系统安全] 三十一.恶意代码检测(1)恶意代码攻击溯源及恶意样本分析
  4. [知识图谱实战篇] 一.数据抓取之Python3抓取JSON格式的电影实体
  5. C# 系统应用之ListView控件 (三).添加ContextMenuStrip右键菜单打开删除文件
  6. iOS之深入解析CocoaPods的插件机制和如何加载插件整合开发工具
  7. 【数据结构与算法】之深入解析“课程表”的求解思路与算法示例
  8. python获取计算机IP、mac地址、计算机名
  9. Django 使用 HttpResponse 返回 json 字符串显示 Unicode 编码
  10. Linux停止后台运行Django项目