[转] ROS-I simple_message 源码分析:MessageManager

2023-05-16

转载说明: 感谢简书原作者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

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

类型变量符号含义
intMAX_NUM_HANDLERS消息处理器的最大数目
intnum_handlers_消息处理器的实际数目
MessageHandler*handlers_[MAX_NUM_HANDLERS]存放消息处理器的指针数组
SmplMsgConnection*connection_通信使用的连接
PingHandlerping_hndlr_ping消息处理器
SimpleCommsFaultHandlerdef_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 ROS
  while (ros::ok())
#else
  while (true)
#endif
  {
    this->spinOnce();

    // Throttle loop speed if waiting for a re-connection
    if (!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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[转] ROS-I simple_message 源码分析:MessageManager 的相关文章

随机推荐

  • 【SpringMVC学习笔记2】-【SpringMVC创建:pom.xml文件配置、web.xml文件配置、创建controller控制器、创建Tomcat服务器】

    SpringMVC学习笔记2 二 SpringMVC创建1 pom xml文件配置2 web xml文件配置3 创建controller控制器4 创建Tomcat服务器 二 SpringMVC创建 1 pom xml文件配置 span cl
  • Tomcat9的安装和配置

    安装前需要先安装 Java 环境 jdk1 8安装 xff1a https blog csdn net weixin 42109012 article details 94388518 1 下载tomcat9 官网地址 http tomca
  • java方法设置超时时间

    span class token keyword public span span class token keyword static span span class token keyword void span span class
  • Java中的集合类有哪些?

    Java中的集合类有哪些 xff1f Java的整个集合框架中 xff0c 主要分为List Set Queue Stack Map等五种数据结构 其中 xff0c 前四种数据结构都是单一元素的集合 xff0c 而最后的Map则是以KV键值
  • Day01. mybatis框架

    Day01 mybatis框架 课程计划 xff1a 1 MyBatis快速入门 2 MyBatis对数据库中数据的增删改查操作 3 占位符的应用 4 动态SQL的应用 6 MyBatis的Mapper接口开发 1 MyBatis简介 了解
  • 求两数组的和

    给定一个整数数组 nums 和一个整数目标值 target xff0c 请你在该数组中找出 和为目标值 target 的那 两个 整数 xff0c 并返回它们的数组下标 你可以假设每种输入只会对应一个答案 但是 xff0c 数组中同一个元素
  • 初学“RESTful“(RESTful增删改查基础案例) 主要在于PUT/DELETE请求

    1 RESTful 一种风格 1 1RESTful简介 REST Representational State Transfer xff0c 表现层资源状态转移 资源 一切皆资源 资源是一种看待服务器的方式 即 将服务器看作是由很多离散的资
  • servlet打war包

    一 打war包 xff1a 类似于 jar war zip rip war 可以将开发好的web应用的所有文件和目录达成一个war包 1 对于传输而言 xff0c 可以将web应用的所有文件和目录打成一个war包 便于传输 2 打成war包
  • [转]ROS2 源码解析与实践 - Node

    转载说明 原文链接https floodshao github io 2020 03 06 ros2 源码解析与实践 Node 感谢原作者分享 xff01 如有侵权 xff0c 请联系我删除 xff0c 谢谢 xff01 文章目录 1 No
  • 注解配置SpringMVC --使用配置类和注解代替web.xml 和SpringMVC的配置文件

    1 创建初始化类 代替web xml 在Servlet3 0环境中 xff0c 容器会在类路径中查找实现javax servlet ServletContainerInitializer接口的类 xff0c 如果找到的话就用它来配置Serv
  • SSM整合(一)

    每一个框架在SSM中担任的角色 例如 SpringMVC 是表述层框架 用来处理我们浏览器发送到服务器的请求 并且将数据反应到浏览器 Mybatis是一个持久层框架 帮助我们来连接数据库 访问数据库 以及操作数据库 Spring是一个整合型
  • SSM整合(二)

    接上文 2 准备工作 创建Maven Module导入依赖pom xml文件 span class token prolog lt xml version 61 34 1 0 34 encoding 61 34 UTF 8 34 gt sp
  • 算法: 递归方法

    算法基础课 查找与排序 1 递归 查找与排序的排序 1递归的设计经验 找重复 子问题 找重复的变化率 gt 参数 找参数变化趋势 gt 设计出口 2 练习策略 循环改递归 经典递归 大量练习 总结规律 掌握套路 找感觉 挑战高难度 什么是递
  • Spring Cloud Gateway不能使用外置tomcat部署的问题

    继上一边博客写了spring cloud gateway的入门之后 xff0c 打算部署到服务器上 xff0c 却发现将网关打包成war包 xff0c 然后上传到外置的tomcat服务器上 xff0c 启动的时候 xff0c 回到这个错 x
  • 关于“error C2065 : 未声明的标识符”的解决方法

    关于 error C2065 未声明的标识符 的解决方法 1 查看是否已经在头文件中对其定义和声明 xff1b 2 查看一下函数或变量所在的源文件其是否已经包含了需要用到的头文件 xff1b 3 查看函数前方是否有所属的名称空间 xff08
  • 问题记录:springboot添加拦截器,过滤器;解决拦截器不生效及生效后无法调用controller 方法

    1 添加拦截器 package com example saina interceptor import org springframework web servlet HandlerInterceptor import javax ser
  • 解决WIN10的cmd无法使用的问题

    终于解决WIN10的cmd无法使用的问题啦 xff01 xff01 xff01 每次想要查个本机ip啥的都会出现下面这种 34 xxxx不是内部或外部命令 xff0c 也不是可运行的程序 34 今天终于解决了 xff01 xff01 xff
  • 线程池的简单运用Executors.newFixedThreadPool

    span class token comment https blog csdn net weixin 40271838 article details 79998327 通过运行结果 xff0c 可以看到我们创建了一个固定数量为2的Fix
  • Egg TypeScript 项目 npm start 启动项目 报错问题

    egg ts 项目npm start 报错问题 1 错误信息 页面上Internal Server Error real status 500 控制台报错如下 span class token punctuation span egg sc
  • [转] ROS-I simple_message 源码分析:MessageManager

    转载说明 感谢简书原作者play robot的分享 xff01 著作权归原作者所有 xff0c 如有侵权 xff0c 请联系我删除 xff0c 谢谢 xff01 原文地址 https www jianshu com p af9adf450d