实时控制软件第三次作业(编程作业)-更新中

根据@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上所以需要花点时间,希望老师通融一下

原文地址:https://www.cnblogs.com/leafwaltz/p/6169463.html