根據@ffl 老師的建議做了一些修改,給messenger的緩存添加了互斥鎖保證線程安全並對調了ReceiveMessage和PostMessage函數名
1. xenomai任務與qt-ui之間的通信
目前所做的工作是將xenomai-api使用c++進行封裝並在qt的ui中進行顯示,在實際編程過程中發現xenomai任務與qtgui難以進行有效的通信,原因如下:
- qtgui必須運行在主線程,在xenomai任務中直接修改gui容易導致線程不安全,程序在運行時會報錯;
- qtgui的ui對象為私有,強行改成公有在xenomai任務中調用又會引發一系列問題,包括上述線程問題。
為了解決上述問題,老師建議使用xddp,但是太麻煩,於是創建了一個信使單例用於xenomai任務和qtgui之間信息的傳遞,應該也可以進行xenomai任務之間的信息傳遞以取代xddp,由於時間倉促暫時沒有測試。
源碼如下:
1 //messenger.h 2 #ifndef MESSENGER_H 3 #define MESSENGER_H 4 5 #include "userheaders.h" 6 7 typedef struct _Message //封裝的消息結構體 8 { 9 int type; //用於標明消息的類型 10 void* message; //void指針攜帶需要傳輸的消息內容,可以是一個結構體 11 }Message; 12 13 class Messenger:public QObject 14 { 15 Q_OBJECT 16 17 public: 18 19 static Messenger* GetInstance(); 20 21 void PostMessage(Message msg); //推送消息 22 23 Message ReceiveMessage(int type); //接收消息 24 25 void Initialize(); //初始化 26 27 void Destroy(); //銷毀單例 28 29 private: 30 31 Messenger(); 32 33 map<int,pthread_mutex_t> m_messageTempMutex; //互斥鎖集合,與緩存隊列一一對應 34 35 map<int,queue<Message>> m_messageTemp; //數據緩存,根據數據類型不同分為不同的緩存隊列 36 37 static Messenger* m_pInstance; 38 39 // ~TaskManager(){}; 40 signals: 41 42 void sig_newMessageIn(int type); //有新消息時發出信號 43 }; 44 45 #endif // MESSENGER_H
1 //messenger.cpp 2 #include "messenger.h" 3 4 Messenger* Messenger::m_pInstance = nullptr; 5 6 Messenger::Messenger() 7 { 8 } 9 10 void Messenger::PostMessage(Message msg) 11 { 12 map<int,queue<Message>>::iterator it = m_messageTemp.find(msg.type); 13 14 if(it==m_messageTemp.end()) 15 { 16 queue<Message> newMessageQueue; 17 newMessageQueue.push(msg); 18 pthread_mutex_t newMutex; 19 pthread_mutex_init(&newMutex,NULL); 20 m_messageTemp.insert(pair<int,queue<Message>>{msg.type,newMessageQueue}); 21 m_messageTempMutex.insert(pair<int,pthread_mutex_t>{msg.type,newMutex}); 22 } 23 else 24 { 25 pthread_mutex_lock(&m_messageTempMutex[msg.type]); 26 it->second.push(msg); 27 pthread_mutex_unlock(&m_messageTempMutex[msg.type]); 28 } 29 30 emit sig_newMessageIn(msg.type); 31 } 32 33 Message Messenger::ReceiveMessage(int type) 34 { 35 Message msg{0,nullptr}; 36 map<int,queue<Message>>::iterator it = m_messageTemp.find(type); 37 if(!(it==m_messageTemp.end())) 38 { 39 if(!(it->second.empty())) 40 { 41 pthread_mutex_lock(&m_messageTempMutex[type]); 42 msg=it->second.front(); 43 it->second.pop(); 44 pthread_mutex_unlock(&m_messageTempMutex[type]); 45 } 46 } 47 return msg; 48 } 49 50 void Messenger::Initialize() 51 { 52 53 } 54 55 void Messenger::Destroy() 56 { 57 delete m_pInstance; 58 } 59 60 Messenger* Messenger::GetInstance() 61 { 62 if(m_pInstance==nullptr) 63 { 64 m_pInstance = new Messenger(); 65 } 66 return m_pInstance; 67 }
- 設計思路:實際上受到了路由器原理的啟發,本質上這個單例的工作方式就是存儲轉發:
ReceiveMessage函數在收到消息以后首先查看在緩存組中有沒有此消息類型對應的消息緩存,如果沒有就創建一個,實際上就是路由器創建路由表的方法;將不同類型的消息分組存儲,在存儲完畢后發出一個有新消息到來的信號,將希望接收這個信號的函數聲明為槽,與這個信號連接即可,樣例如下,我希望ui管理器中的update函數進行消息的響應:
1 connect(Messenger::GetInstance(), 2 SIGNAL(sig_newMessageIn(int)), 3 this, 4 SLOT(update(int)));
只需這樣做即可,每次有新消息被信使存儲后都會通知update來接收消息,在update函數中可以這樣做:
1 void GuiManager::update(int messagetype) 2 { 3 Message msg=Messenger::GetInstance()->ReceiveMessage(messagetype); 4 5 switch (msg.type) 6 { 7 case type_task_trajectory_generator: 8 ui->textBrowser->insertPlainText(QString::number(((tAxisSetpoint)msg.message)->Velocity,10)); 9 break; 10 case type_task_command_sender: 11 ui->textBrowser->insertPlainText(QString::number(((tPosCmd)msg.message)->Acceleration,10)); 12 break; 13 default: 14 ui->textBrowser->insertPlainText("null"); 15 break; 16 } 17 }
根據不同的消息類型分別進行響應即可,寫法有點像win32的消息機制,只不過沒有GetMessage,用了信使的ReceiveMessage:)
使用強制類型轉換從void*中取出攜帶的信息即可,十分方便,這里將老師的示例程序中的tAxisSetpoint和tPosCmd改成了結構體指針。
在xenomai的task函數中這樣發送消息:
1 Message msg; 2 msg.type = type_task_trajectory_generator; 3 msg.message = axis1_setpoint; 4 5 //bla bla bla 6 7 Messenger::GetInstance()->PostMessage(msg);
創建全局的結構體指針變量時需要進行內存分配。
tPosCmd new_cmd = (tPosCmd)malloc(sizeof(_tPosCmd_));
tAxisSetpoint axis1_setpoint = (tAxisSetpoint)malloc(sizeof(_tAxisSetpoint_));
后續待更新。。。。。。代碼整理后再傳GitHub,接下來需要做作業要求的軌跡了= =。。打算用QImage直接畫在ui上所以需要花點時間,希望老師通融一下