[转] 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先來看一下它的私有成員:
| 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就分析到這里,后面再舉例分析在更上層的代碼中是如何使用它的。
注: 可以參考以下網站,對比學習
總結
以上是生活随笔為你收集整理的[转] ROS-I simple_message 源码分析:MessageManager的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [转]ROS2 源码解析与实践 - No
- 下一篇: [转]vim基础入门、语法速探、快捷键与