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

第五章 Qt程序设计

       本章讲述了如何基于Qt Creator设计控制台程序和图形界面程序。控制台程序相当于4.3节的聊天程序;图形界面程序相当于4.4节的时间管理程序。图形界面程序近似于真实仿真项目,讲述了如何设计仿真项目,如何周期性地推进仿真,如何通过Qt临界区和pthread信号量实现主线程和回调线程之间的同步等功能。

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

 

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.  
  4. #include <QDialog>
  5.  
  6. namespace Ui
  7. {
  8. class NameDialog;
  9. }
  10.  
  11. class NameDialog : public QDialog
  12. {
  13.     Q_OBJECT
  14.  
  15. public:
  16.     explicit NameDialog(QWidget *parent = 0);
  17.     ~NameDialog();
  18.  
  19. private slots:
  20.     void on_okButton_clicked();
  21.  
  22. private:
  23.     Ui::NameDialog *ui;
  24. };
  25.  
  26. #endif // NAMEDIALOG_H

                                                      表5.3 namedialog.cpp

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

       第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 <QApplication>
  3.  
  4. int main(int argc, char *argv[])
  5. {
  6.     QApplication a(argc, argv);
  7.  
  8.     MainWindow w;
  9.     w.show();
  10.  
  11.     return a.exec();
  12. }

                                                      表5.5 mainwindow.h

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

                                                      表5.6 mainwindow.cpp

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

       第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代码说明:

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

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

                                                      表5.7 mainwindow.h

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

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

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个仿真成员的运行结果

麒麟RTI软件KY-RTI的Linux、Windows版本和源码请联系作者:[email protected]

 

麒麟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分布仿真技术:第九章 综合演示

猜你喜欢

转载自blog.csdn.net/sillysunny/article/details/84198014