日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2024/4/18 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [转] ROS-I simple_message 源码分析:MessageManager 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載說明: 感謝簡書原作者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 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就分析到這里,后面再舉例分析在更上層的代碼中是如何使用它的。

注: 可以參考以下網站,對比學習

  • industrial::message_manager::MessageManager
  • docs.ros.org - simple_message
  • 總結

    以上是生活随笔為你收集整理的[转] ROS-I simple_message 源码分析:MessageManager的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。