KY-RTI分布仿真技术:第五章 Qt程序设计

第五章 Qt程序设计

       本章讲述了如何基于Qt Creator设计控制台程序和图形界面程序。控制台程序相当于4.3节的聊天程序;图形界面程序相当于4.4节的时间管理程序。图形界面程序近似于真实仿真项目,讲述了如何设计仿真项目,如何周期性地推进仿真,如何通过Qt临界区和pthread信号量实现主线程和回调线程之间的同步等功能。Qt本身不是一种编程语言,但其拥有自己的类型定义、图形框架和丰富的函数库,类似Visual Studio C++,本章专门对其进行说明。

5.1 控制台程序

       本节说明如何开发一个Qt控制台程序,其结果表明将基于KY-RTI开发的GNU C++程序移植到Qt非常方便。

5.1.1需求分析

       本项目需要基于Qt Creator开发一个相当于4.3节的Qt聊天程序。

5.1.2项目设计

       基于Qt Creator先实现一个Qt程序框架,然后把4.3节的GNU C++聊天程序代码移植到Qt程序。

5.1.3项目开发

       下面以银河麒麟操作系统中的Qt Creator为例。

       第1步:启动Qt Creator,选择“New Project”创建一个Qt项目。

                                                       图5.1 启动Qt Creator

       第2步:选择“Qt Console Application”创建一个控制台项目。点击“Choose”继续。

                                                      图5.2 创建控制台项目

       第3步:设置项目名称为“QtChatConsole”、保存目录为“/home/lbq”,点击“Next”继续,直至项目生成。

                                                      图5.3 设计项目名和目录

       图5.4是项目生成后的界面。到目前为止,本项目有1个QtChatConsole.pro工程文件和1个main.cpp源代码文件。

                                                      图5.4 缺省生成的Qt控制台项目

       第4步:添加HLA回调文件。用户应根据自己的目录适当修改。

       (1)将RTI-1.3NGv6/Linux-x86_64-opt-mt/apps/chat目录下的HwFederateAmbassador.cpp、HwFederateAmbassador.hh两个文件拷贝到该工程目录下。这两个文件是4.3节聊天程序的源文件。

         cd /home/lbq/RTI-1.3NGv6/Linux-x86_64-opt-mt/apps/chat

         cp HwFederateAmbassador.cpp  HwFederateAmbassador.hh /home/lbq/QtChatConsole/

       (2)在图5.4中,选择根节点“QtChatConsole”,按右键,添加已有文件。将HwFederateAmbassador.cpp、HwFederateAmbassador.hh添加到本项目。

       第5步:修改main.cpp。

       将RTI-1.3NGv6/Linux-x86_64-opt-mt/apps/chat目录下的Chat.cpp代码粘贴到main.cpp,并做简单修改,区别只在于第1、123-126这几行语句。

                                                      表5.1 main.cpp

  1. #include
  2. #include "HwFederateAmbassador.hh"
  3. #include
  4. #include
  5. #include
  6. using namespace std;
  7. RTI::InteractionClassHandle     hChatClass;
  8. RTI::ParameterHandle             hChatName;
  9. RTI::ParameterHandle             hChatSentence;
  10. int hw_main(int argc, char *argv[])
  11. {
  12.     const char *federationExecutionName = "chat";
  13.     const char *FEDfile = "chat.fed";
  14.     char federateName[50];
  15.     cout << "Please input your name: ";
  16.     cin >> federateName;
  17.     try {
  18.         RTI::RTIambassador       rti;
  19.         HwFederateAmbassador     fedAmb;
  20.         RTI::FederateHandle      federateId;
  21.         try {
  22.             rti.createFederationExecution(federationExecutionName, FEDfile);
  23.         }
  24.         catch ( RTI::FederationExecutionAlreadyExists& e ) {
  25.             //According to the HLA standard, only the first federate can call this service succesfully.
  26.             //cerr << "FED_HW: Note: Federation execution already exists." << e << endl;
  27.         } catch ( RTI::Exception& e ) {
  28.             cerr << "FED_HW: ERROR:" << e << endl;
  29.             return -1;
  30.         }
  31.         try {
  32.             federateId = rti.joinFederationExecution(federateName, federationExecutionName, &fedAmb);
  33.         } catch (RTI::FederateAlreadyExecutionMember& e) {
  34.             cerr << "FED_HW: ERROR: " << argv[1]
  35.                  << " already exists in the Federation Execution "
  36.                  << federationExecutionName << "." << endl;
  37.             cerr << e << endl;
  38.             return -1;
  39.         } catch (RTI::FederationExecutionDoesNotExist&) {
  40.             cerr << "FED_HW: ERROR: Federation Execution "
  41.                  << "does not exists."<< endl;
  42.             return -1;
  43.         } catch ( RTI::Exception& e ) {
  44.             cerr << "FED_HW: ERROR:" << e << endl;
  45.             return -1;
  46.         }
  47.         ///
  48.         hChatClass = rti.getInteractionClassHandle("chat");
  49.         hChatName = rti.getParameterHandle("name", hChatClass);
  50.         hChatSentence = rti.getParameterHandle("sentence", hChatClass);
  51.         //如果向外发送,则需要公布
  52.         rti.publishInteractionClass(hChatClass);
  53.         //如果需要接收,则必须订购
  54.         rti.subscribeInteractionClass(hChatClass);
  55.         string szSentence;
  56.         cin.ignore();
  57.         while (0 != strcmp(szSentence.c_str(), "exit")) {
  58.             cout << "Please input a sentence: ";
  59.             getline(cin, szSentence);
  60.             RTI::ParameterHandleValuePairSet* pParams = NULL;
  61.             long numParams(2);
  62.             pParams = RTI::ParameterSetFactory::create (numParams);
  63.             pParams->add(hChatName,(char*)federateName, strlen(federateName)+1);
  64.             pParams->add(hChatSentence,(char*)szSentence.c_str(), szSentence.size());
  65.             try {
  66.                 rti.sendInteraction(hChatClass, *pParams, "");
  67.             } catch(...) {
  68.                 cerr << "Error: send interaction" << endl;
  69.             }
  70.             pParams->empty();
  71.             delete pParams;   // Deallocate the memory
  72.         }
  73.         try {
  74.     rti.resignFederationExecution( RTI::DELETE_OBJECTS_AND_RELEASE_ATTRIBUTES );
  75.         } catch ( RTI::Exception& e ) {
  76.             cerr << "FED_HW: ERROR:" << e << endl;
  77.             return -1;
  78.         }
  79.         try {
  80.             rti.destroyFederationExecution( federationExecutionName );
  81.         } catch ( RTI::FederatesCurrentlyJoined& /* e */ ) {
  82.             cerr << "FED_HW: FederatesCurrentlyJoined" << endl;
  83.             return 0;
  84.         } catch ( RTI::FederationExecutionDoesNotExist& /* e */) {
  85.             cerr << "FED_HW: FederationExecutionDoesNotExist" << endl;
  86.             return 0;
  87.         } catch ( RTI::Exception& e ) {
  88.             cerr << "FED_HW: ERROR:" << e << endl;
  89.             return -1;
  90.         }
  91.     } catch (RTI::ConcurrentAccessAttempted& e) {
  92.         cerr << e << endl;
  93.         return -1;
  94.     } catch ( RTI::Exception& e ) {
  95.         cerr << "FED_HW: ERROR:" << e << endl;
  96.         return -1;
  97.     }
  98.     return 0;
  99. }
  100. int main(int argc, char *argv[])
  101. {
  102.     QCoreApplication a(argc, argv);
  103.     hw_main(argc, argv);
  104.     return a.exec();
  105. }

5.1.4编译运行

5.1.4.1编译

       编译之前,需要修改工程文件。双击“QtChatConsole.pro”,打开该文件。在文件末尾添加如下内容:

INCLUDEPATH += /home/lbq/RTI-1.3NGv6/Linux-x86_64-opt-mt/include

LIBS += -L/home/lbq/RTI-1.3NGv6/Linux-x86_64-opt-mt/lib -lRTI-NG -lfedtime -lMid -lMidUtil -lbz2mid -lpthread

DEFINES += RTI_USES_STD_FSTREAM

       通过下列两种编译方式,生成可执行程序QtChatConsole。

       (1)由Qt Creator构建(Build)。Qt Creator在构建项目时,会将生成的可执行程序和临时文件缺省保存到/home/lbq/build-QtChatConsole-Desktop-Debug目录,而不是源代码目录。

       (2)在源代码目录手动构建,执行如下命令。

       qmake

       make

5.1.4.2测试运行

       运行程序:

       (1)启动KY-RTI。

       (2)在图5.4中,点击2次运行按钮,启动2个界面;或者启动2个命令行界面,运行程序。如下图所示。

       问题:左图liu发送一个“hello”,但是右图zhang却没有收到。

                                                      图5.5 打开tick开关后运行控制台程序

       原因:当程序运行时,会缺省生成RTI.rid文件。该文件中打开了tick开关,然而本程序并没有调用tick服务来接收回调,因此无法收到应答。

       解决:将RTI.rid文件中的“;; UsingTickSwitch On”改为“;; UsingTickSwitch Off”。若没有RTI.rid,则运行程序后会自动生成该文件。

       重新启动程序后,运行正常。

                                                      图5.6 关闭tick开关后运行控制台程序

5.2 图形程序

       本节说明如何从项目需求开始,从头设计和开发一个Qt图形界面的HLA时间管理程序,功能相当于4.4节的GNU C++时间管理程序。整个项目分为4个阶段:需求分析、项目设计、代码设计、编译运行。其中,需求分析阶段与HLA/RTI关系不大,甚至整个项目的设计框架与HLA/RTI关系也不大。仿真应用的需求千千万,Qt的设计模式也多样化,不同的仿真应用可采用不同的设计模式;HLA/RTI服务于仿真应用,用于系统中的不同仿真成员之间的数据通信。HLA/RTI为仿真应用提供支持,而不是仿真应用反过来受HLA/RTI的制约,希望用户通过本程序的设计过程能够体会到这一点。现有的一些HLA/RTI代码自动生成工具实际上就是给用户设计系统制定了一个约束框架,不管什么类型的仿真项目都装到一个套子里,使得用户设计程序缺乏较强的灵活性,也不利于基于HLA/RTI移植已有的非HLA/RTI项目。

5.2.1需求分析

       本仿真项目的名称为“TimeManagementExample”,当然也可以叫做像“星球大战”、“世界末日”之类的响亮名字。名称规定了不是“TimeManagementExample”的程序不属于本项目。对HLA/RTI程序来说,联邦名称为“TimeManagementExample”,不是该名字的仿真成员不属于本项目。

       每个仿真成员拥有3架飞机,但其中只有1架飞机会起飞,飞机的x、y坐标为随机数(不考虑合理性),飞机每隔1秒发布自己的二维态势信息。

       要求采用图形界面显示仿真结果。

5.2.2项目设计

       本项目所有仿真成员的功能相同,因此只需要开发一个Qt程序即可。在多次启动该可执行程序后,多个仿真成员之间通过KY-RTI进行数据通信。本项目的联邦名称为“TimeManagementExample”,仿真成员之间的通信数据通过tracer.fed进行定义,包括一个plane对象类和xPos、yPos两个属性。另外,仿真周期为1秒,每个仿真成员周期性地发布飞机起飞后的位置信息,并接收其他飞机的位置信息。

       Qt图形程序包含2个图形界面。一个图形界面负责当程序启动运行时,输入仿真成员名称;另一个图形界面用来接收其他仿真成员的飞机位置信息;由于接收的位置信息会累积很多,所以当超过1000条信息时清空1次(在实际项目中可保存到数据库)。

       时间间隔采用Qt定时器来设计,定时器每隔1秒中断1次。

                                                      图5.7 仿真成员的2个图形界面示意图

5.2.3代码设计

       第1步:启动Qt Creator,选择“New Project”创建一个Qt项目。

                                                      图5.8 启动Qt Creator

       第2步:选择“Qt Widgets Application”创建一个图形程序。点击“Choose”继续。

                                                      图5.9 创建图形界面程序

       第3步:设置项目名称“QtTimeManagement”和保存的目录“/home/lbq”,点击“Next”继续,中间过程选择默认选项,直至项目生成。图5.10是项目生成后的界面。

                                                      图5.10 默认生成的图形程序界面

       程序运行后的图形界面如下图所示。该界面为主界面,可作为“项目设计”阶段确定的界面2,后面需要再设计界面1来输入仿真成员名称,另外还需要完善主界面。

                                                      图5.11 运行生成的图形界面程序

       第4步:创建界面1,用于输入仿真成员名。

       (1)右键点击图5.10中的“Forms”,新建一个窗体。后面过程可参考下列几幅图,类名设置为NameDialog。

                                                      图5.12 选择窗体类型

                                                      图5.13 选择对话框模板

                                                      图5.14 设置类名

       (2)为新建的NameDialog窗体添加QLabel、QLineEdit、QPushButton共3个控件,设置QLabel为“请输入仿真成员名称”并调整字体大小。编辑框的变量名缺省设置为lineEdit。

                                                      图5.15 设置界面1中的控件

       (3)为OK按钮添加Click事件。与界面1相关的namedialog.h和namedialog.cpp就设计完成,分别如表5.2和表5.3所示。

表5.2 namedialog.h

  1. #ifndef NAMEDIALOG_H
  2. #define NAMEDIALOG_H
  3. #include
  4. namespace Ui
  5. {
  6. class NameDialog;
  7. }
  8. class NameDialog : public QDialog
  9. {
  10.     Q_OBJECT
  11. public:
  12.     explicit NameDialog(QWidget *parent = 0);
  13.     ~NameDialog();
  14. private slots:
  15.     void on_okButton_clicked();
  16. private:
  17.     Ui::NameDialog *ui;
  18. };
  19. #endif // NAMEDIALOG_H

                                                      表5.3 namedialog.cpp

  1. #include "namedialog.h"
  2. #include "ui_namedialog.h"
  3. QString federateName = "";
  4. NameDialog::NameDialog(QWidget *parent) :
  5.     QDialog(parent),
  6.     ui(new Ui::NameDialog)
  7. {
  8.     ui->setupUi(this);
  9. }
  10. NameDialog::~NameDialog()
  11. {
  12.     delete ui;
  13. }
  14. void NameDialog::on_okButton_clicked()
  15. {
  16.     federateName = ui->lineEdit->text();
  17.     if(federateName.size() != 0) {
  18.         this->close();
  19.     }
  20. }

       第5步:设计界面2。

       (1)删除MainWindow主界面窗体中的菜单、工具条、状态栏,添加1个QLabel和1个QListWidget控件,分别用于显示仿真成员名和显示接收到的飞机态势信息。

                                                      图5.16 设置界面2中的控件

       (2)添加定时器。到目前为止,整个程序的代码框架就已经完成,该框架本身与HLA/RTI并没有太大关系。运行程序结果如下图所示。左图为界面1,右图为界面2,右图中的数据为程序设计的假想数据。

                                                      图5.17 基于假想数据的程序运行结果

       程序由5个源文件组成,除了表5.2和表5.3表示的namedialog.h、namedialog.cpp外,还有3个文件main.cpp、mainwindow.h、mainwindow.cpp,参见表5.4、表5.5、表5.6。

                                                      表5.4 main.cpp 

  1. #include "mainwindow.h"
  2. #include
  3. int main(int argc, char *argv[])
  4. {
  5.     QApplication a(argc, argv);
  6.     MainWindow w;
  7.     w.show();
  8.     return a.exec();
  9. }

                                                      表5.5 mainwindow.h

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #include
  4. #include
  5. #include
  6. namespace Ui
  7. {
  8. class MainWindow;
  9. }
  10. class MainWindow : public QMainWindow
  11. {
  12.     Q_OBJECT
  13. public:
  14.     explicit MainWindow(QWidget *parent = 0);
  15.     ~MainWindow();
  16. private slots:
  17.     void onTimerOut();
  18. private:
  19.     Ui::MainWindow *ui;
  20.     QStringList m_receivedMessages; //保存接收到的飞机位置信息
  21.     QTimer *timer;
  22. };
  23. #endif // MAINWINDOW_H

                                                      表5.6 mainwindow.cpp

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include "namedialog.h"
  4. extern QString federateName; //仿真成员名称
  5. MainWindow::MainWindow(QWidget *parent) :
  6.     QMainWindow(parent),
  7.     ui(new Ui::MainWindow)
  8. {
  9.     ui->setupUi(this);
  10.     m_receivedMessages.clear();
  11.     while (federateName.size() == 0) { //启动界面1输入仿真成员名称
  12.         NameDialog n;
  13.         n.exec();
  14.     }
  15.     //设置仿真成员名称
  16.     ui->label->setText(federateName);
  17.     ui->label->setAlignment(Qt::AlignCenter);
  18.     //添加定时器
  19.     timer = new QTimer();
  20.     timer->setInterval(1000);
  21.     timer->start();
  22.     connect(timer, SIGNAL(timeout()), this, SLOT(onTimerOut()));
  23. }
  24. MainWindow::~MainWindow()
  25. {
  26.     delete ui;
  27. }
  28. //定时处理
  29. void MainWindow::onTimerOut()
  30. {
  31.     if(ui->listWidget->count() > 1000) {
  32.         ui->listWidget->clear();
  33.     }
  34.     QString imaginaryReceivedMsg = "myhandle  100  200  f15"; //假想的接收到的飞机位置信息
  35.     m_receivedMessages.append(imaginaryReceivedMsg );
  36.     for(int i=0; i<m_receivedMessages.size(); i++) {
  37.         ui->listWidget->addItem(new QListWidgetItem(m_receivedMessages[i]));
  38.     }
  39.     m_receivedMessages.clear();
  40. }

       第6步:以上步骤构成的系统是静态的,当启动多次时,每个仿真成员都是独立运行,仿真成员之间不能进行数据通信。因此,需要加入HLA/RTI代码将仿真成员变成动态的,实现相互间的通信。在已有的仿真系统中加入HLA/RTI代码,基本方法是:

      (1)删除已有程序中的通信代码(例如TCP/IP代码)。对本例而言,没有这部分内容。

      (2)在程序的初始化部分添加创建联邦执行、加入联邦执行、公布和订购服务、注册对象示例等;如果采用时间管理,则还要设置相关的时间管理标识(是否为受限成员、管控成员)。

       对于本例而言,初始化部分为MainDialog类的构造函数;因此,在该构造函数中完成这些工作,需要添加的代码可参考4.4节时间管理示例的TimeManagement.cpp。

      (3)MainWindow::onTimerOut()函数每秒执行1次,在一个仿真周期内主要处理2件事。一是该仿真周期内收到回调后应该采取的动作;二是将自己下一步的态势发布出去。对于本例而言,一是将收到的其他飞机的态势信息显示出来;而是调用HLA的updateAttributeValues服务更新飞机的下一步位置,并将仿真时间请求推进到下一步。当然,依照仿真模型不同,如果每个仿真周期只更新自己的当前态势信息,也未尝不可;但这样的消息在HLA中应定义为RO(Receive Order)消息而不是TSO(Time Stamp Order)消息。

     (4)添加回调文件。

       (a)将RTI-1.3NGv6/Linux-x86_64-opt-mt/apps/time-notick目录下的HwFederateAmbassador.cpp、HwFederateAmbassador.hh两个文件拷贝到该工程目录下。

  cd /home/lbq/RTI-1.3NGv6/Linux-x86_64-opt-mt/apps/time-notick

  cp HwFederateAmbassador.cpp  HwFederateAmbassador.hh /home/lbq/QtTimeManagement/

       (b)在图5.4中,选择根节点“QtTimeManagement”,按右键,添加已有文件。将HwFederateAmbassador.cpp、HwFederateAmbassador.hh添加到本项目。

       至此,整个代码设计完成。整个程序包括namedialog.h、mainwindow.h、HwFederateAmbassador.h 3个.h文件,以及main.cpp、namedialog.cpp、mainwindow.cpp、HwFederateAmbassador.cpp 4个.cpp文件。表5.7、表5.8、表5.9分别是mainwindow.h、mainwindow.cpp、HwFederateAmbassador.cpp的程序代码。用户可自行比较表5.5与表5.7、表5.6与表5.8之间的区别,从中可以发现添加HLA代码前后所做的工作。

       代码说明:

       (1)mainwindow.cpp和HwFederateAmbassador.cpp分别位于主线程和回调线程中,对共享变量需要做互斥控制;

       (2)设置全局变量g_receivedMessages。回调线程将收到的其他飞机态势信息保存在此变量中;

       (3)设置Qt临界区g_mutex。在两个线程使用包括g_receivedMessages在内的全局变量时,应通过临界区加锁和解锁;本例演示了Qt临界区的用法;

       (4)设置pthread信号量g_granted,当主线程需要接收来自回调线程的同意时,则主线程等待1个信号量;如果回调线程满足主线程的请求,则释放1个信号量。本例演示了pthread信号量的用法。

       mainwindow.h代码说明:

21行:定义变量rti,通过该变量调用HLA服务;

22行:定义变量fedAmb,通过该变量接收HLA回调服务。

                                                      表5.7 mainwindow.h

  1. #ifndef MAINWINDOW_H
  2. #define MAINWINDOW_H
  3. #include
  4. #include
  5. #include
  6. #include "HwFederateAmbassador.hh"
  7. #include
  8. #include
  9. #include //for usleep
  10. #include //for rand
  11. #include
  12. namespace Ui
  13. {
  14. class MainWindow;
  15. }
  16. static    RTI::RTIambassador    rti;
  17. static    HwFederateAmbassador  fedAmb;
  18. class MainWindow : public QMainWindow
  19. {
  20.     Q_OBJECT
  21. public:
  22.     explicit MainWindow(QWidget *parent = 0);
  23.     ~MainWindow();
  24. private slots:
  25.     void onTimerOut();
  26. private:
  27.     Ui::MainWindow *ui;
  28.     QTimer *timer;
  29.     //RTI
  30.     RTIfedTime              lookahead;
  31.     const char              *federationExecutionName;
  32.     RTIfedTime              targetTime;
  33.     RTIfedTime              intervalTime;
  34.     int                       xPos, yPos;
  35.     const char              *tag;
  36.     int                       step;
  37. };
  38. #endif // MAINWINDOW_H

       mainwindow.cpp代码说明:

       (1)变量定义

9-11行:定义对象句柄和2个属性句柄,在仿真过程中主线程和回调线程都不会修改它们,因此不需要对它们访问不需要设置临界区;

14行:定义用于保存回调消息的字符串列表变量,每个回调消息是一个字符串;

15行:定义pthread信号量,Qt还可以使用QSemaphore来定义并具有更好的可移植性;

16行:定义Qt类型的临界区,用于互斥访问全局变量。

       (2)在构造函数中初始化

28-31行:启动界面1输入仿真成员名称;

34行:初始化信号量;

37-38行:将界面1输入的仿真成员名称显示在界面2中;

49-61行:创建联邦执行;

63-83行:加入联邦执行;

87-89行:获取对象句柄和2个属性句柄;此时回调线程不会对它们进行操作,故不需要临界区保护;

97行:公布对象类属性,只有公布之后才能够向RTI发送二维态势信息,即xPos和yPos;

98行:订购交互类属性,只有订购之后才能够从RTI收到二维态势信息;

103-108行:注册3架飞机;

118行:将仿真成员设置为时间管理受限的;

119行:通过信号量来等待RTI同意将该仿真成员设置为时间管理受限成员;

121行:将仿真成员设置为时间管控成员;

122行:通过信号量来等待RTI同意将该仿真成员设置为时间管控成员;

124行:打开异步消息开关;

133行:设置仿真的逻辑时间间隔为1秒,与定时器的1秒间隔对应;即逻辑时间的1等于物理时间的1秒;

139-143行:定义定时器。

       (3)在析构函数中清理仿真现场

148行:该行为注释行,表示仿真成员在结束时可以不调用'resignFederationExecution'和 'destroyFederationExecution'服务,RTI服务器会自动执行这两个服务。这种情形会被KY-RTI的监控器发现并为仿真成员执行这两个服务。

149-179行:主动执行'resignFederationExecution'和 'destroyFederationExecution'服务。

       (4)在定时器函数中实现仿真

188-190行:如果列表控件中超过1000条记录,则清空控件;真实场景中可写入日记文件或数据库等;

192-197行:将其他飞机的态势信息显示到列表控件中,全局变量用临界区访问;

200行:仿真步长加1;

203-204行:飞机的二维坐标取随机数;

206-220行:将飞机下一时刻的位置信息打包

223行:设置消息时戳;

224行:设置下一仿真时间;

228行:发送飞机在下一时刻的二维态势信息(如果采用RO消息也可以发送当前时刻的态势,依仿

真模型而定);

245行:将仿真请求推进到下一步;

246行:等待RTI同意该仿真成员推进到下一步。

                                                      表5.8 mainwindow.cpp

  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3. #include "namedialog.h"
  4. #include
  5. #include
  6. #include
  7. #include
  8. RTI::ObjectHandle          g_hInstance1, g_hInstance2, g_hInstance3;
  9. RTI::AttributeHandle      g_hxPos;
  10. RTI::AttributeHandle      g_hyPos;
  11. RTIfedTime                  g_currentTime = 0.0;
  12. QStringList                 g_receivedMessages; //保存接收到的飞机信息
  13. sem_t                        g_granted; //pthread信号量, Qt还可以使用QSemaphore
  14. QMutex                       g_mutex;   //临界区
  15. extern QString federateName; //仿真成员名称
  16. MainWindow::MainWindow(QWidget *parent) :
  17.     QMainWindow(parent),
  18.     ui(new Ui::MainWindow)
  19. {
  20.     ui->setupUi(this);
  21.     g_receivedMessages.clear();
  22.     while (federateName.size() == 0) { //启动界面1输入仿真成员名称
  23.         NameDialog n;
  24.         n.exec();
  25.     }
  26.     //初始化信号量
  27.     sem_init(&g_granted, 0, 0);
  28.     //设置仿真成员名称
  29.     ui->label->setText(federateName);
  30.     ui->label->setAlignment(Qt::AlignCenter);
  31.     //RTI初始化-----------------------------------------------------------
  32.     federationExecutionName = "TimeManagementExample";
  33.     const char *FEDfile = "tracer.fed";
  34.     lookahead = 1.0;
  35.     try {
  36.         RTI::FederateHandle      federateId;
  37.         try {
  38.             rti.createFederationExecution(federationExecutionName, FEDfile);
  39.         }
  40.         catch ( RTI::FederationExecutionAlreadyExists& e ) {
  41.             //According to the HLA standard, only the first federate can call this service succesfully.
  42.             //cerr << "FED_HW: Note: Federation execution already exists." << e << endl;
  43.         } catch ( RTI::Exception& e ) {
  44.             QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), "Cannot connect to the RTI server, please check RTI.rid.");
  45.             box.setStandardButtons (QMessageBox::Ok);
  46.             box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  47.             box.exec ();
  48.             exit(1);
  49.         }
  50.         try {
  51.             federateId = rti.joinFederationExecution(federateName.toLatin1().data(),   
                                                  
    federationExecutionName, &fedAmb);
  52.         } catch (RTI::FederateAlreadyExecutionMember& e) {
  53.             QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
  54.             box.setStandardButtons (QMessageBox::Ok);
  55.             box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  56.             box.exec ();
  57.             exit(1);
  58.         } catch (RTI::FederationExecutionDoesNotExist& e) {
  59.             QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
  60.             box.setStandardButtons (QMessageBox::Ok);
  61.             box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  62.             box.exec ();
  63.             exit(1);
  64.         } catch ( RTI::Exception& e ) {
  65.             QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
  66.             box.setStandardButtons (QMessageBox::Ok);
  67.             box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  68.             box.exec ();
  69.             exit(1);
  70.         }
  71.         /
  72.         RTI::ObjectClassHandle hPlaneClass = rti.getObjectClassHandle("plane");
  73.         g_hxPos = rti.getAttributeHandle("xPos", hPlaneClass);
  74.         g_hyPos = rti.getAttributeHandle("yPos", hPlaneClass);
  75.         RTI::AttributeHandleSet *theAttributes;
  76.         theAttributes = RTI::AttributeHandleSetFactory::create(2);
  77.         theAttributes->add(g_hxPos);
  78.         theAttributes->add(g_hyPos);
  79.         rti.publishObjectClass(hPlaneClass, *theAttributes);
  80.         rti.subscribeObjectClassAttributes(hPlaneClass, *theAttributes);
  81.         theAttributes->empty();
  82.         delete theAttributes;
  83.         //register one plane
  84.         g_hInstance1 = rti.registerObjectInstance(hPlaneClass);
  85.         //register 2nd plane
  86.         g_hInstance2 = rti.registerObjectInstance(hPlaneClass);
  87.         //register 3rd plane
  88.         g_hInstance3 = rti.registerObjectInstance(hPlaneClass);
  89.     } catch ( RTI::Exception& e ) {
  90.         QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
  91.         box.setStandardButtons (QMessageBox::Ok);
  92.         box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  93.         box.exec ();
  94.         exit(1);
  95.     }
  96.     try {
  97.         rti.enableTimeConstrained();
  98.         sem_wait(&g_granted); //等待信号量,不使用tick
  99.         rti.enableTimeRegulation((RTIfedTime)0.0, lookahead);
  100.         sem_wait(&g_granted); //等待信号量,不使用tick
  101.         rti.enableAsynchronousDelivery();
  102.     } catch ( RTI::Exception& e ) {
  103.         QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
  104.         box.setStandardButtons (QMessageBox::Ok);
  105.         box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  106.         box.exec ();
  107.         exit(1);
  108.     }
  109.     intervalTime = 1.0;
  110.     tag = "Qt";
  111.     step = 0;
  112.     //------------------------------------------RTI初始化完成
  113.     //添加定时器
  114.     timer = new QTimer();
  115.     timer->setInterval(1000); //1 second
  116.     timer->start();
  117.     connect(timer, SIGNAL(timeout()), this, SLOT(onTimerOut()));
  118. }
  119. MainWindow::~MainWindow()
  120. {
  121.     //After the program exits, the RTI will automatically calls 'resignFederationExecution' and 'destroyFederationExecution'. Of course, you can write them for yourself.
  122.     try {
  123.         rti.resignFederationExecution( RTI::DELETE_OBJECTS_AND_RELEASE_ATTRIBUTES );
  124.     } catch ( RTI::Exception& e ) {
  125.         QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
  126.         box.setStandardButtons (QMessageBox::Ok);
  127.         box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  128.         box.exec ();
  129.         exit(1);
  130.     }
  131.     try {
  132.         rti.destroyFederationExecution( federationExecutionName );
  133.     } catch ( RTI::FederatesCurrentlyJoined& e) {
  134.         QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
  135.         box.setStandardButtons (QMessageBox::Ok);
  136.         box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  137.         box.exec ();
  138.         exit(1);
  139.     } catch ( RTI::FederationExecutionDoesNotExist& e) {
  140.         QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
  141.         box.setStandardButtons (QMessageBox::Ok);
  142.         box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  143.         box.exec ();
  144.         exit(1);
  145.     } catch ( RTI::Exception& e ) {
  146.         QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
  147.         box.setStandardButtons (QMessageBox::Ok);
  148.         box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  149.         box.exec ();
  150.         exit(1);
  151.     }
  152.     delete ui;
  153. }
  154. //定时处理
  155. void MainWindow::onTimerOut()
  156. {
  157.     //1.先处理本步已有工作
  158.     if(ui->listWidget->count() > 1000) {
  159.         ui->listWidget->clear();
  160.     }
  161.     g_mutex.lock();
  162.     for(int i=0; i<g_receivedMessages.size(); i++) {
  163.         ui->listWidget->addItem(new QListWidgetItem(g_receivedMessages[i]));
  164.     }
  165.     g_receivedMessages.clear();
  166.     g_mutex.unlock();
  167.     //2.发送下一步的飞机位置信息,并将仿真时间推进到下一步
  168.     step++;
  169.     qDebug() << "Step: " << step;
  170.     xPos=rand();
  171.     yPos=rand();
  172.     RTI::AttributeHandleValuePairSet* pAttrs = NULL;
  173.     pAttrs = RTI::AttributeSetFactory::create (2);
  174.     /* 如果两个仿真成员都是C++程序,则使用下面两条语句即可 */
  175.     //pAttrs->add(g_hxPos, (char*)&xPos, sizeof(int));
  176.     //pAttrs->add(g_hyPos, (char*)&yPos, sizeof(int));
  177.     /* 如果两个仿真成员采用不同语言编程,则应转为字符串再发送 */
  178.     char s[20];
  179.     //´\0´为字符串结束符,有的平台上sprintf会自动添加
  180.     sprintf(s, "%d\0", xPos);
  181.     pAttrs->add(g_hxPos, (char*)s, strlen(s));
  182.     sprintf(s, "%d\0", yPos);
  183.     pAttrs->add(g_hyPos, (char*)s, strlen(s));
  184.     g_mutex.lock();
  185.     RTIfedTime  timestamp = g_currentTime + lookahead; //消息时戳
  186.     targetTime = g_currentTime + intervalTime;   //下一步仿真时间
  187.     g_mutex.unlock();
  188.     try {
  189.         rti.updateAttributeValues(g_hInstance1, *pAttrs, timestamp, tag);
  190.     } catch(...) {
  191.         QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), "Error: updateAttributeValues");
  192.         box.setStandardButtons (QMessageBox::Ok);
  193.         box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  194.         box.exec ();
  195.         exit(1); //qDebug() may be better and not exit.
  196.     }
  197.     pAttrs->empty();
  198.     delete pAttrs;
  199.     //-----------------------------------------------------------------
  200.     try {
  201.         qDebug() << "This federate will advance to " << targetTime.getTime();
  202.         rti.timeAdvanceRequest(targetTime);
  203.         sem_wait(&g_granted); //等待信号量,不使用tick
  204.         qDebug() << "The federate has advanced to " << targetTime.getTime();
  205.     } catch ( RTI::Exception& e ) {
  206.         QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
  207.         box.setStandardButtons (QMessageBox::Ok);
  208.         box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  209.         box.exec ();
  210.     }
  211. }

       HwFederateAmbassador.cpp代码说明:

19-29行:将发现的飞机输出到终端;

31-99行:处理收到的飞机态势信息;

101-115行:RTI同意将仿真成员设置为时间管控成员;

117-131行:RTI同意将仿真成员设置为时间管理受限的成员;

133-147行:RTI同意仿真成员推进到下一步。

                                                      表5.9 HwFederateAmbassador.cpp

  1. #include "fedtime.hh"
  2. #include "HwFederateAmbassador.hh"
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include
  8. #include
  9. extern RTI::ObjectHandle         g_hInstance1, g_hInstance2, g_hInstance3;
  10. extern RTI::AttributeHandle     g_hxPos;
  11. extern RTI::AttributeHandle     g_hyPos;
  12. extern RTIfedTime                 g_currentTime;
  13. extern QStringList                g_receivedMessages;//保存接收到的飞机位置信息
  14. extern sem_t                       g_granted;           //pthread信号量
  15. extern QMutex                      g_mutex;             //临界区
  16. void HwFederateAmbassador::discoverObjectInstance (
  17.     RTI::ObjectHandle          theObject,        // supplied C1
  18.     RTI::ObjectClassHandle     theObjectClass, // supplied C1
  19.     const char *          theObjectName)  // supplied C4
  20. throw (
  21.     RTI::CouldNotDiscover,
  22.     RTI::ObjectClassNotKnown,
  23.     RTI::FederateInternalError)
  24. {
  25.     qDebug() << "discoverObjectInstance: " << theObject << "," << theObjectClass << "," << theObjectName;
  26. }
  27. void HwFederateAmbassador::reflectAttributeValues (
  28.     RTI::ObjectHandle                 theObject,     // supplied C1
  29.     const RTI::AttributeHandleValuePairSet& theAttributes, // supplied C4
  30.     const RTI::FedTime&                     theTime,       // supplied C1
  31.     const char                             *theTag,        // supplied C4
  32.     RTI::EventRetractionHandle        theHandle)     // supplied C1
  33. throw (
  34.     RTI::ObjectNotKnown,
  35.     RTI::AttributeNotKnown,
  36.     RTI::FederateOwnsAttributes,
  37.     RTI::InvalidFederationTime,
  38.     RTI::FederateInternalError)
  39. {
  40.     //call the next service.
  41.     reflectAttributeValues(theObject, theAttributes, theTag);
  42. }
  43. void HwFederateAmbassador::reflectAttributeValues (
  44.     RTI::ObjectHandle                 theObject,     // supplied C1
  45.     const RTI::AttributeHandleValuePairSet& theAttributes, // supplied C4
  46.     const char                             *theTag)        // supplied C4
  47. throw (
  48.     RTI::ObjectNotKnown,
  49.     RTI::AttributeNotKnown,
  50.     RTI::FederateOwnsAttributes,
  51.     RTI::FederateInternalError)
  52. {
  53.     RTI::AttributeHandle attrHandle;
  54.     RTI::ULong           valueLength;
  55.     QString msg = "";
  56.     int xPos = 0;
  57.     int yPos = 0;
  58.     char str[20];
  59.     //不需要对g_hxPosg_hyPos加锁,主线程和回调线程都是读操作
  60.     for (int i = 0; i < theAttributes.size(); i++) {
  61.         attrHandle = theAttributes.getHandle( i );
  62.         if(attrHandle == g_hxPos) {
  63.             /* 如果两个仿真成员都是C++程序,则使用下面语句即可 */
  64.             //theAttributes.getValue(i, (char*)&xPos, valueLength);
  65.             /* 如果两个仿真成员采用不同语言编程,则发送方应使用字符串发送,接收方应将接收到的字符串转为整数 */
  66.             theAttributes.getValue(i, (char*)str, valueLength);
  67.             xPos = atoi(str);
  68.         } else if(attrHandle == g_hyPos) {
  69.             /* 如果两个仿真成员都是C++程序,则使用下面语句即可 */
  70.             //theAttributes.getValue(i, (char*)&yPos, valueLength);
  71.             /* 如果两个仿真成员采用不同语言编程,则发送方应使用字符串发送,接收方应将接收到的字符串转为整数 */
  72.             theAttributes.getValue(i, (char*)str, valueLength);
  73.             yPos = atoi(str);
  74.         } else {
  75.             QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), "Receive wrong parameter handle.");
  76.             box.setStandardButtons (QMessageBox::Ok);
  77.             box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
  78.             box.exec ();
  79.         }
  80.     }
  81.     msg = QString::number(theObject)+"    "+QString::number(xPos)+"    "+QString::number(yPos)+"    "+theTag;
  82.     g_mutex.lock();
  83.     g_receivedMessages.append(msg);
  84.     g_mutex.unlock();
  85. }
  86. void HwFederateAmbassador::timeRegulationEnabled (
  87.     const  RTI::FedTime& theFederateTime) // supplied C4
  88. throw (
  89.     RTI::InvalidFederationTime,
  90.     RTI::EnableTimeRegulationWasNotPending,
  91.     RTI::FederateInternalError)
  92. {
  93.     g_mutex.lock();
  94.     g_currentTime = theFederateTime;
  95.     g_mutex.unlock();
  96.     sem_post(&g_granted); //释放信号量
  97.     qDebug() << "timeRegulationEnabled: " << ((RTIfedTime)theFederateTime).getTime();
  98. }
  99. void HwFederateAmbassador::timeConstrainedEnabled (
  100.     const RTI::FedTime& theFederateTime) // supplied C4
  101. throw (
  102.     RTI::InvalidFederationTime,
  103.     RTI::EnableTimeConstrainedWasNotPending,
  104.     RTI::FederateInternalError)
  105. {
  106.     g_mutex.lock();
  107.     g_currentTime = theFederateTime;
  108.     g_mutex.unlock();
  109.     sem_post(&g_granted); //释放信号量
  110.     qDebug() << "timeRegulationEnabled: " << ((RTIfedTime)theFederateTime).getTime();
  111. }
  112. void HwFederateAmbassador::timeAdvanceGrant (
  113.     const RTI::FedTime& theTime) // supplied C4
  114. throw (
  115.     RTI::InvalidFederationTime,
  116.     RTI::TimeAdvanceWasNotInProgress,
  117.     RTI::FederateInternalError)
  118. {
  119.     g_mutex.lock();
  120.     g_currentTime = theTime;
  121.     g_mutex.unlock();
  122.     sem_post(&g_granted); //释放信号量
  123.     qDebug() << "timeAdvanceGrant: " << ((RTIfedTime)theTime).getTime();
  124. }

5.2.4编译运行

       编译方法参照5.1.4.1执行。测试运行按如下步骤操作。

       第1步:修改RTI.rid,关闭tick开关。

       因为本程序没有使用tick服务,所以需要关闭tick开关。从其他地方拷贝1个RTI.rid文件;或者启动运行QtTimeManagement,然后再关闭程序,则程序在运行时会自动生成该文件。将RTI.rid文件中的“;; UsingTickSwitch On”改为“;; UsingTickSwitch Off”。本人的QtTimeManagement源代码目录为/home/lbq/QtTimeManagement,使用Qt Creator运行程序生成的RTI.rid文件所在目录为/home/lbq/build-QtTimeManagement-Desktop-Debug。

       第2步:启动KY-RTI。注意,KY-RTI的IP地址和端口号要与RTI.rid一致。

       第3步:运行程序,启动1个仿真成员。仿真成员名称设置为“第101飞行大队”,可以看到没有显示任何飞机信息。按照HLA标准,一个仿真成员只能收到其他仿真成员发布的信息,无法收到自己发布的信息。想要显示自己的信息,直接在程序中添加本地信息即可。

                                                      图5.18 启动1个仿真成员的运行结果

        第4步:再次运行程序,启动第2个仿真成员。仿真成员名称设置为“第102飞行大队”,可以看到此时2个仿真成员都正确地显示了对方的飞机信息,包括飞机的对象句柄、xPos、yPos和tag。

                                                      图5.19 启动2个仿真成员的运行结果

 KY-RTI的Linux、Windows版本和源码请联系作者:walt_lbq@163.com

KY-RTI分布仿真技术:前 言

KY-RTI分布仿真技术:第一章 简介

KY-RTI分布仿真技术:第二章 系统安装

KY-RTI分布仿真技术:第三章 KY-OMT对象模型模板工具

KY-RTI分布仿真技术:第四章 C++程序设计

KY-RTI分布仿真技术:第五章 Qt程序设计

KY-RTI分布仿真技术:第六章 Java程序设计

KY-RTI分布仿真技术:第七章 Visual C++程序设计

KY-RTI分布仿真技术:第八章 Visual C#程序设计

KY-RTI分布仿真技术:第九章 综合演示

KY-RTI分布仿真技术:第十章 Python程序设计

KY-RTI分布仿真技术:附录1 分组聊天(HLA数据分发管理的应用

KY-RTI分布仿真技术:附录2 大联邦(构建1000个成员的HLA/RTI仿真系统)

KY-RTI分布仿真技术:附录3 国产化(操作系统+CPUs)

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
`rti::core::QosProviderParams` 是 Real-Time Innovations(RTI)公司提供的 C++ API 中的一个类,用于设置和配置 Quality of Service(QoS)提供者的参数。 QoS 是一种机制,用于在实时系统中管理和控制数据传输的可靠性、实时性和带宽等属性。RTI 的 QoSProviderParams 类允许用户在使用 RTI Connext DDS 实时数据分发中间件时,通过编程方式配置和定制 QoS 参数。 以下是一个示例,演示如何使用 `rti::core::QosProviderParams` 类: ```cpp #include <iostream> #include <rti/core/QosProviderParams.hpp> int main() { rti::core::QosProviderParams qos_params; // 设置 QoS 参数 qos_params.set_auto_enable(true); qos_params.set_qos_profile("MyQosProfile"); // 获取 QoS 参数 bool auto_enable = qos_params.auto_enable(); std::string qos_profile = qos_params.qos_profile(); std::cout << "Auto enable: " << auto_enable << std::endl; std::cout << "QoS profile: " << qos_profile << std::endl; return 0; } ``` 在上面的示例中,我们创建了一个 `rti::core::QosProviderParams` 对象 `qos_params`,并使用 `set_auto_enable()` 和 `set_qos_profile()` 方法分别设置了自动启用和 QoS 配置文件的参数。然后,我们使用 `auto_enable()` 和 `qos_profile()` 方法获取了设置的值,并将其打印到控制台上。 这只是 `rti::core::QosProviderParams` 类的简单示例,你可以根据实际需求使用其他方法和属性来配置和访问 QoS 参数。 希望这个简单的解释对你有所帮助!如果你有任何进一步的问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值