本章讲述了如何基于Qt Creator设计控制台程序和图形界面程序。控制台程序相当于4.3节的聊天程序;图形界面程序相当于4.4节的时间管理程序。图形界面程序近似于真实仿真项目,讲述了如何设计仿真项目,如何周期性地推进仿真,如何通过Qt临界区和pthread信号量实现主线程和回调线程之间的同步等功能。Qt本身不是一种编程语言,但其拥有自己的类型定义、图形框架和丰富的函数库,类似Visual Studio C++,本章专门对其进行说明。
本节说明如何开发一个Qt控制台程序,其结果表明将基于KY-RTI开发的GNU C++程序移植到Qt非常方便。
本项目需要基于Qt Creator开发一个相当于4.3节的Qt聊天程序。
基于Qt Creator先实现一个Qt程序框架,然后把4.3节的GNU C++聊天程序代码移植到Qt程序。
下面以银河麒麟操作系统中的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
- #include <QCoreApplication>
- #include "HwFederateAmbassador.hh"
- #include <RTI.hh>
- #include <fedtime.hh>
- #include <iostream>
- using namespace std;
- RTI::InteractionClassHandle hChatClass;
- RTI::ParameterHandle hChatName;
- RTI::ParameterHandle hChatSentence;
- int hw_main(int argc, char *argv[])
- {
- const char *federationExecutionName = "chat";
- const char *FEDfile = "chat.fed";
- char federateName[50];
- cout << "Please input your name: ";
- cin >> federateName;
- try {
- RTI::RTIambassador rti;
- HwFederateAmbassador fedAmb;
- RTI::FederateHandle federateId;
- try {
- rti.createFederationExecution(federationExecutionName, FEDfile);
- }
- catch ( RTI::FederationExecutionAlreadyExists& e ) {
- //According to the HLA standard, only the first federate can call this service succesfully.
- //cerr << "FED_HW: Note: Federation execution already exists." << e << endl;
- } catch ( RTI::Exception& e ) {
- cerr << "FED_HW: ERROR:" << e << endl;
- return -1;
- }
- try {
- federateId = rti.joinFederationExecution(federateName, federationExecutionName, &fedAmb);
- } catch (RTI::FederateAlreadyExecutionMember& e) {
- cerr << "FED_HW: ERROR: " << argv[1]
- << " already exists in the Federation Execution "
- << federationExecutionName << "." << endl;
- cerr << e << endl;
- return -1;
- } catch (RTI::FederationExecutionDoesNotExist&) {
- cerr << "FED_HW: ERROR: Federation Execution "
- << "does not exists."<< endl;
- return -1;
- } catch ( RTI::Exception& e ) {
- cerr << "FED_HW: ERROR:" << e << endl;
- return -1;
- }
- ///
- hChatClass = rti.getInteractionClassHandle("chat");
- hChatName = rti.getParameterHandle("name", hChatClass);
- hChatSentence = rti.getParameterHandle("sentence", hChatClass);
- //如果向外发送,则需要公布
- rti.publishInteractionClass(hChatClass);
- //如果需要接收,则必须订购
- rti.subscribeInteractionClass(hChatClass);
- string szSentence;
- cin.ignore();
- while (0 != strcmp(szSentence.c_str(), "exit")) {
- cout << "Please input a sentence: ";
- getline(cin, szSentence);
- RTI::ParameterHandleValuePairSet* pParams = NULL;
- long numParams(2);
- pParams = RTI::ParameterSetFactory::create (numParams);
- pParams->add(hChatName,(char*)federateName, strlen(federateName)+1);
- pParams->add(hChatSentence,(char*)szSentence.c_str(), szSentence.size());
- try {
- rti.sendInteraction(hChatClass, *pParams, "");
- } catch(...) {
- cerr << "Error: send interaction" << endl;
- }
- pParams->empty();
- delete pParams; // Deallocate the memory
- }
- try {
- rti.resignFederationExecution( RTI::DELETE_OBJECTS_AND_RELEASE_ATTRIBUTES );
- } catch ( RTI::Exception& e ) {
- cerr << "FED_HW: ERROR:" << e << endl;
- return -1;
- }
- try {
- rti.destroyFederationExecution( federationExecutionName );
- } catch ( RTI::FederatesCurrentlyJoined& /* e */ ) {
- cerr << "FED_HW: FederatesCurrentlyJoined" << endl;
- return 0;
- } catch ( RTI::FederationExecutionDoesNotExist& /* e */) {
- cerr << "FED_HW: FederationExecutionDoesNotExist" << endl;
- return 0;
- } catch ( RTI::Exception& e ) {
- cerr << "FED_HW: ERROR:" << e << endl;
- return -1;
- }
- } catch (RTI::ConcurrentAccessAttempted& e) {
- cerr << e << endl;
- return -1;
- } catch ( RTI::Exception& e ) {
- cerr << "FED_HW: ERROR:" << e << endl;
- return -1;
- }
- return 0;
- }
- int main(int argc, char *argv[])
- {
- QCoreApplication a(argc, argv);
- hw_main(argc, argv);
- return a.exec();
- }
|
编译之前,需要修改工程文件。双击“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
运行程序:
(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开关后运行控制台程序
本节说明如何从项目需求开始,从头设计和开发一个Qt图形界面的HLA时间管理程序,功能相当于4.4节的GNU C++时间管理程序。整个项目分为4个阶段:需求分析、项目设计、代码设计、编译运行。其中,需求分析阶段与HLA/RTI关系不大,甚至整个项目的设计框架与HLA/RTI关系也不大。仿真应用的需求千千万,Qt的设计模式也多样化,不同的仿真应用可采用不同的设计模式;HLA/RTI服务于仿真应用,用于系统中的不同仿真成员之间的数据通信。HLA/RTI为仿真应用提供支持,而不是仿真应用反过来受HLA/RTI的制约,希望用户通过本程序的设计过程能够体会到这一点。现有的一些HLA/RTI代码自动生成工具实际上就是给用户设计系统制定了一个约束框架,不管什么类型的仿真项目都装到一个套子里,使得用户设计程序缺乏较强的灵活性,也不利于基于HLA/RTI移植已有的非HLA/RTI项目。
本仿真项目的名称为“TimeManagementExample”,当然也可以叫做像“星球大战”、“世界末日”之类的响亮名字。名称规定了不是“TimeManagementExample”的程序不属于本项目。对HLA/RTI程序来说,联邦名称为“TimeManagementExample”,不是该名字的仿真成员不属于本项目。
每个仿真成员拥有3架飞机,但其中只有1架飞机会起飞,飞机的x、y坐标为随机数(不考虑合理性),飞机每隔1秒发布自己的二维态势信息。
要求采用图形界面显示仿真结果。
本项目所有仿真成员的功能相同,因此只需要开发一个Qt程序即可。在多次启动该可执行程序后,多个仿真成员之间通过KY-RTI进行数据通信。本项目的联邦名称为“TimeManagementExample”,仿真成员之间的通信数据通过tracer.fed进行定义,包括一个plane对象类和xPos、yPos两个属性。另外,仿真周期为1秒,每个仿真成员周期性地发布飞机起飞后的位置信息,并接收其他飞机的位置信息。
Qt图形程序包含2个图形界面。一个图形界面负责当程序启动运行时,输入仿真成员名称;另一个图形界面用来接收其他仿真成员的飞机位置信息;由于接收的位置信息会累积很多,所以当超过1000条信息时清空1次(在实际项目中可保存到数据库)。
时间间隔采用Qt定时器来设计,定时器每隔1秒中断1次。
图5.7 仿真成员的2个图形界面示意图
第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
- #ifndef NAMEDIALOG_H
- #define NAMEDIALOG_H
- #include <QDialog>
- namespace Ui
- {
- class NameDialog;
- }
- class NameDialog : public QDialog
- {
- Q_OBJECT
- public:
- explicit NameDialog(QWidget *parent = 0);
- ~NameDialog();
- private slots:
- void on_okButton_clicked();
- private:
- Ui::NameDialog *ui;
- };
- #endif // NAMEDIALOG_H
|
表5.3 namedialog.cpp
- #include "namedialog.h"
- #include "ui_namedialog.h"
- QString federateName = "";
- NameDialog::NameDialog(QWidget *parent) :
- QDialog(parent),
- ui(new Ui::NameDialog)
- {
- ui->setupUi(this);
- }
- NameDialog::~NameDialog()
- {
- delete ui;
- }
- void NameDialog::on_okButton_clicked()
- {
- federateName = ui->lineEdit->text();
- if(federateName.size() != 0) {
- this->close();
- }
- }
|
第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
- #include "mainwindow.h"
- #include <QApplication>
- int main(int argc, char *argv[])
- {
- QApplication a(argc, argv);
- MainWindow w;
- w.show();
- return a.exec();
- }
|
表5.5 mainwindow.h
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
- #include <QMainWindow>
- #include <QStringList>
- #include <QTimer>
- namespace Ui
- {
- class MainWindow;
- }
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
- public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
- private slots:
- void onTimerOut();
- private:
- Ui::MainWindow *ui;
- QStringList m_receivedMessages; //保存接收到的飞机位置信息
- QTimer *timer;
- };
- #endif // MAINWINDOW_H
|
表5.6 mainwindow.cpp
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- #include "namedialog.h"
- extern QString federateName; //仿真成员名称
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- m_receivedMessages.clear();
- while (federateName.size() == 0) { //启动界面1输入仿真成员名称
- NameDialog n;
- n.exec();
- }
- //设置仿真成员名称
- ui->label->setText(federateName);
- ui->label->setAlignment(Qt::AlignCenter);
- //添加定时器
- timer = new QTimer();
- timer->setInterval(1000);
- timer->start();
- connect(timer, SIGNAL(timeout()), this, SLOT(onTimerOut()));
- }
- MainWindow::~MainWindow()
- {
- delete ui;
- }
- //定时处理
- void MainWindow::onTimerOut()
- {
- if(ui->listWidget->count() > 1000) {
- ui->listWidget->clear();
- }
- QString imaginaryReceivedMsg = "myhandle 100 200 f15"; //假想的接收到的飞机位置信息
- m_receivedMessages.append(imaginaryReceivedMsg );
- for(int i=0; i<m_receivedMessages.size(); i++) {
- ui->listWidget->addItem(new QListWidgetItem(m_receivedMessages[i]));
- }
- m_receivedMessages.clear();
- }
|
第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
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
- #include <QMainWindow>
- #include <QStringList>
- #include <QTimer>
- #include "HwFederateAmbassador.hh"
- #include <RTI.hh>
- #include <fedtime.hh>
- #include <unistd.h> //for usleep
- #include <stdlib.h> //for rand
- #include <iostream>
- namespace Ui
- {
- class MainWindow;
- }
- static RTI::RTIambassador rti;
- static HwFederateAmbassador fedAmb;
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
- public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
- private slots:
- void onTimerOut();
- private:
- Ui::MainWindow *ui;
- QTimer *timer;
- //RTI
- RTIfedTime lookahead;
- const char *federationExecutionName;
- RTIfedTime targetTime;
- RTIfedTime intervalTime;
- int xPos, yPos;
- const char *tag;
- int step;
- };
- #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
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- #include "namedialog.h"
- #include <QMutex>
- #include <QMessageBox>
- #include <QDebug>
- #include <semaphore.h>
- RTI::ObjectHandle g_hInstance1, g_hInstance2, g_hInstance3;
- RTI::AttributeHandle g_hxPos;
- RTI::AttributeHandle g_hyPos;
- RTIfedTime g_currentTime = 0.0;
- QStringList g_receivedMessages; //保存接收到的飞机信息
- sem_t g_granted; //pthread信号量, Qt还可以使用QSemaphore
- QMutex g_mutex; //临界区
- extern QString federateName; //仿真成员名称
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- g_receivedMessages.clear();
- while (federateName.size() == 0) { //启动界面1输入仿真成员名称
- NameDialog n;
- n.exec();
- }
- //初始化信号量
- sem_init(&g_granted, 0, 0);
- //设置仿真成员名称
- ui->label->setText(federateName);
- ui->label->setAlignment(Qt::AlignCenter);
- //RTI初始化-----------------------------------------------------------
- federationExecutionName = "TimeManagementExample";
- const char *FEDfile = "tracer.fed";
- lookahead = 1.0;
- try {
- RTI::FederateHandle federateId;
- try {
- rti.createFederationExecution(federationExecutionName, FEDfile);
- }
- catch ( RTI::FederationExecutionAlreadyExists& e ) {
- //According to the HLA standard, only the first federate can call this service succesfully.
- //cerr << "FED_HW: Note: Federation execution already exists." << e << endl;
- } catch ( RTI::Exception& e ) {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), "Cannot connect to the RTI server, please check RTI.rid.");
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- exit(1);
- }
- try {
- federateId = rti.joinFederationExecution(federateName.toLatin1().data(),
federationExecutionName, &fedAmb); - } catch (RTI::FederateAlreadyExecutionMember& e) {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- exit(1);
- } catch (RTI::FederationExecutionDoesNotExist& e) {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- exit(1);
- } catch ( RTI::Exception& e ) {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- exit(1);
- }
- /
- RTI::ObjectClassHandle hPlaneClass = rti.getObjectClassHandle("plane");
- g_hxPos = rti.getAttributeHandle("xPos", hPlaneClass);
- g_hyPos = rti.getAttributeHandle("yPos", hPlaneClass);
- RTI::AttributeHandleSet *theAttributes;
- theAttributes = RTI::AttributeHandleSetFactory::create(2);
- theAttributes->add(g_hxPos);
- theAttributes->add(g_hyPos);
- rti.publishObjectClass(hPlaneClass, *theAttributes);
- rti.subscribeObjectClassAttributes(hPlaneClass, *theAttributes);
- theAttributes->empty();
- delete theAttributes;
- //register one plane
- g_hInstance1 = rti.registerObjectInstance(hPlaneClass);
- //register 2nd plane
- g_hInstance2 = rti.registerObjectInstance(hPlaneClass);
- //register 3rd plane
- g_hInstance3 = rti.registerObjectInstance(hPlaneClass);
- } catch ( RTI::Exception& e ) {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- exit(1);
- }
- try {
- rti.enableTimeConstrained();
- sem_wait(&g_granted); //等待信号量,不使用tick
- rti.enableTimeRegulation((RTIfedTime)0.0, lookahead);
- sem_wait(&g_granted); //等待信号量,不使用tick
- rti.enableAsynchronousDelivery();
- } catch ( RTI::Exception& e ) {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- exit(1);
- }
- intervalTime = 1.0;
- tag = "Qt";
- step = 0;
- //------------------------------------------RTI初始化完成
- //添加定时器
- timer = new QTimer();
- timer->setInterval(1000); //1 second
- timer->start();
- connect(timer, SIGNAL(timeout()), this, SLOT(onTimerOut()));
- }
- MainWindow::~MainWindow()
- {
- //After the program exits, the RTI will automatically calls 'resignFederationExecution' and 'destroyFederationExecution'. Of course, you can write them for yourself.
- try {
- rti.resignFederationExecution( RTI::DELETE_OBJECTS_AND_RELEASE_ATTRIBUTES );
- } catch ( RTI::Exception& e ) {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- exit(1);
- }
- try {
- rti.destroyFederationExecution( federationExecutionName );
- } catch ( RTI::FederatesCurrentlyJoined& e) {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- exit(1);
- } catch ( RTI::FederationExecutionDoesNotExist& e) {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- exit(1);
- } catch ( RTI::Exception& e ) {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- exit(1);
- }
- delete ui;
- }
- //定时处理
- void MainWindow::onTimerOut()
- {
- //1.先处理本步已有工作
- if(ui->listWidget->count() > 1000) {
- ui->listWidget->clear();
- }
- g_mutex.lock();
- for(int i=0; i<g_receivedMessages.size(); i++) {
- ui->listWidget->addItem(new QListWidgetItem(g_receivedMessages[i]));
- }
- g_receivedMessages.clear();
- g_mutex.unlock();
- //2.发送下一步的飞机位置信息,并将仿真时间推进到下一步
- step++;
- qDebug() << "Step: " << step;
- xPos=rand();
- yPos=rand();
- RTI::AttributeHandleValuePairSet* pAttrs = NULL;
- pAttrs = RTI::AttributeSetFactory::create (2);
- /* 如果两个仿真成员都是C++程序,则使用下面两条语句即可 */
- //pAttrs->add(g_hxPos, (char*)&xPos, sizeof(int));
- //pAttrs->add(g_hyPos, (char*)&yPos, sizeof(int));
- /* 如果两个仿真成员采用不同语言编程,则应转为字符串再发送 */
- char s[20];
- //´\0´为字符串结束符,有的平台上sprintf会自动添加
- sprintf(s, "%d\0", xPos);
- pAttrs->add(g_hxPos, (char*)s, strlen(s));
- sprintf(s, "%d\0", yPos);
- pAttrs->add(g_hyPos, (char*)s, strlen(s));
- g_mutex.lock();
- RTIfedTime timestamp = g_currentTime + lookahead; //消息时戳
- targetTime = g_currentTime + intervalTime; //下一步仿真时间
- g_mutex.unlock();
- try {
- rti.updateAttributeValues(g_hInstance1, *pAttrs, timestamp, tag);
- } catch(...) {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), "Error: updateAttributeValues");
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- exit(1); //qDebug() may be better and not exit.
- }
- pAttrs->empty();
- delete pAttrs;
- //-----------------------------------------------------------------
- try {
- qDebug() << "This federate will advance to " << targetTime.getTime();
- rti.timeAdvanceRequest(targetTime);
- sem_wait(&g_granted); //等待信号量,不使用tick
- qDebug() << "The federate has advanced to " << targetTime.getTime();
- } catch ( RTI::Exception& e ) {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), e._reason);
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- }
- }
|
HwFederateAmbassador.cpp代码说明:
19-29行:将发现的飞机输出到终端;
31-99行:处理收到的飞机态势信息;
101-115行:RTI同意将仿真成员设置为时间管控成员;
117-131行:RTI同意将仿真成员设置为时间管理受限的成员;
133-147行:RTI同意仿真成员推进到下一步。
表5.9 HwFederateAmbassador.cpp
- #include "fedtime.hh"
- #include "HwFederateAmbassador.hh"
- #include <iostream>
- #include <QStringList>
- #include <QMutex>
- #include <QDebug>
- #include <QMessageBox>
- #include <semaphore.h>
- extern RTI::ObjectHandle g_hInstance1, g_hInstance2, g_hInstance3;
- extern RTI::AttributeHandle g_hxPos;
- extern RTI::AttributeHandle g_hyPos;
- extern RTIfedTime g_currentTime;
- extern QStringList g_receivedMessages;//保存接收到的飞机位置信息
- extern sem_t g_granted; //pthread信号量
- extern QMutex g_mutex; //临界区
- void HwFederateAmbassador::discoverObjectInstance (
- RTI::ObjectHandle theObject, // supplied C1
- RTI::ObjectClassHandle theObjectClass, // supplied C1
- const char * theObjectName) // supplied C4
- throw (
- RTI::CouldNotDiscover,
- RTI::ObjectClassNotKnown,
- RTI::FederateInternalError)
- {
- qDebug() << "discoverObjectInstance: " << theObject << "," << theObjectClass << "," << theObjectName;
- }
- void HwFederateAmbassador::reflectAttributeValues (
- RTI::ObjectHandle theObject, // supplied C1
- const RTI::AttributeHandleValuePairSet& theAttributes, // supplied C4
- const RTI::FedTime& theTime, // supplied C1
- const char *theTag, // supplied C4
- RTI::EventRetractionHandle theHandle) // supplied C1
- throw (
- RTI::ObjectNotKnown,
- RTI::AttributeNotKnown,
- RTI::FederateOwnsAttributes,
- RTI::InvalidFederationTime,
- RTI::FederateInternalError)
- {
- //call the next service.
- reflectAttributeValues(theObject, theAttributes, theTag);
- }
- void HwFederateAmbassador::reflectAttributeValues (
- RTI::ObjectHandle theObject, // supplied C1
- const RTI::AttributeHandleValuePairSet& theAttributes, // supplied C4
- const char *theTag) // supplied C4
- throw (
- RTI::ObjectNotKnown,
- RTI::AttributeNotKnown,
- RTI::FederateOwnsAttributes,
- RTI::FederateInternalError)
- {
- RTI::AttributeHandle attrHandle;
- RTI::ULong valueLength;
- QString msg = "";
- int xPos = 0;
- int yPos = 0;
- char str[20];
- //不需要对g_hxPos和g_hyPos加锁,主线程和回调线程都是读操作
- for (int i = 0; i < theAttributes.size(); i++) {
- attrHandle = theAttributes.getHandle( i );
- if(attrHandle == g_hxPos) {
- /* 如果两个仿真成员都是C++程序,则使用下面语句即可 */
- //theAttributes.getValue(i, (char*)&xPos, valueLength);
- /* 如果两个仿真成员采用不同语言编程,则发送方应使用字符串发送,接收方应将接收到的字符串转为整数 */
- theAttributes.getValue(i, (char*)str, valueLength);
- xPos = atoi(str);
- } else if(attrHandle == g_hyPos) {
- /* 如果两个仿真成员都是C++程序,则使用下面语句即可 */
- //theAttributes.getValue(i, (char*)&yPos, valueLength);
- /* 如果两个仿真成员采用不同语言编程,则发送方应使用字符串发送,接收方应将接收到的字符串转为整数 */
- theAttributes.getValue(i, (char*)str, valueLength);
- yPos = atoi(str);
- } else {
- QMessageBox box(QMessageBox::Critical, QObject::tr("Error"), "Receive wrong parameter handle.");
- box.setStandardButtons (QMessageBox::Ok);
- box.setButtonText (QMessageBox::Ok,QObject::tr("Ok"));
- box.exec ();
- }
- }
- msg = QString::number(theObject)+" "+QString::number(xPos)+" "+QString::number(yPos)+" "+theTag;
- g_mutex.lock();
- g_receivedMessages.append(msg);
- g_mutex.unlock();
- }
- void HwFederateAmbassador::timeRegulationEnabled (
- const RTI::FedTime& theFederateTime) // supplied C4
- throw (
- RTI::InvalidFederationTime,
- RTI::EnableTimeRegulationWasNotPending,
- RTI::FederateInternalError)
- {
- g_mutex.lock();
- g_currentTime = theFederateTime;
- g_mutex.unlock();
- sem_post(&g_granted); //释放信号量
- qDebug() << "timeRegulationEnabled: " << ((RTIfedTime)theFederateTime).getTime();
- }
- void HwFederateAmbassador::timeConstrainedEnabled (
- const RTI::FedTime& theFederateTime) // supplied C4
- throw (
- RTI::InvalidFederationTime,
- RTI::EnableTimeConstrainedWasNotPending,
- RTI::FederateInternalError)
- {
- g_mutex.lock();
- g_currentTime = theFederateTime;
- g_mutex.unlock();
- sem_post(&g_granted); //释放信号量
- qDebug() << "timeRegulationEnabled: " << ((RTIfedTime)theFederateTime).getTime();
- }
- void HwFederateAmbassador::timeAdvanceGrant (
- const RTI::FedTime& theTime) // supplied C4
- throw (
- RTI::InvalidFederationTime,
- RTI::TimeAdvanceWasNotInProgress,
- RTI::FederateInternalError)
- {
- g_mutex.lock();
- g_currentTime = theTime;
- g_mutex.unlock();
- sem_post(&g_granted); //释放信号量
- qDebug() << "timeAdvanceGrant: " << ((RTIfedTime)theTime).getTime();
- }
|
编译方法参照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)