本章讲述如何在Linux操作系统上设计GNU C++程序。演示了2个程序:聊天程序chat和时间管理程序time。chat使用HLA的交互类进行通信,没有采用tick服务;time使用HLA的对象类进行通信,采用tick服务;并说明了如何简单修改就可以变成一个不采用tick服务的程序。
4.1 多线程设计模式
RTI由Central RTI Component(CRC)和Local RTI Component(LRC)组成。在KY-RTI中,可以把CRC简单地理解为RTI服务器,LRC理解为libRTI-NG库。
基于KY-RTI的C++仿真程序可以仅包含2个.cpp文件,如下图所示。一个负责请求RTI;另一个负责从RTI接收回调。Main.cpp通过调用HLA服务把请求传给LRC,再由LRC传给CRC;CRC将回调传给LRC,再由LRC传给HwFederateAmbassador.cpp。HwFederateAmbassador.cpp是DMSO RTI习惯采用的文件名,专门用来接收RTI的回调数据。在KY-RTI的各类程序设计语言中,接收回调的文件名都采用了HwFederateAmbassador这个专有名称。
![](https://i-blog.csdnimg.cn/blog_migrate/abc2e129bfdf4e7fa1d36bce3d346203.png)
图4.1 仿真成员采用多线程技术实现
在KY-RTI编程模式中,Main.cpp和HwFederateAmbassador.cpp分别位于两个独立的线程中;当然,如果在同一个线程中,既要向RTI发送请求,又要从RTI接收请求,容易造成死锁。如果两个线程采用全局变量来共享数据,则同时读写一个共享变量时会引起冲突,因此应加锁或采用临界区来进行互斥访问。
4.2 多语言间的数据传输
以C++和Java语言为例,两种程序相互之间传输数据时涉及到主机字节序与网络字节序两个概念。
网络字节序:不同计算机体系结构的存储机制可能不同,通信双方交流的信息单元(比特、字节、字、双字等)应该按照统一的规则进行发送和接收才行。如果不达成一致的理解, 通信双方将无法进行正确的通信。网络字节序规定采用big-endian规则,通信双方需要把数据按照big-endian编码之后再通过网络传输。
主机字节序就是CPU的字节序。x86主机的字节序为little-endian。因此,在x86主机上传输数据的时候要把数据从little-endian转换为big-endian编码后,再通过网络发送出去。
下面举一个例子说明big-endian与little-endian的区别:
int size = 0x01020304;
size的类型为int,int有4个字节,其中01为最高位的字节,04为最低位的字节。那么在内存(或文件)中,该值的存储顺序为:
内存(或文件)地址:0x00000001(高位) 0x00000002 0x00000003 0x00000004(低位)
big-endian : 01 02 03 04
little-endian : 04 03 02 01
如图4.2所示,如果两个程序都是C++程序,则它们在通信时不进行编码转换也能正常通信。
![](https://i-blog.csdnimg.cn/blog_migrate/90867a2636946e8d3d5be073d8b075f6.png)
图4.2 C++程序之间不进行网络编码能正常通信
Java虚拟机采用big-endian,而运行在Java虚拟机中的Java程序的主机字节序也为big-endian。如图4.3所示,如果C++程序与Java程序进行通信,不进行编码转换就不能正确通信。
![](https://i-blog.csdnimg.cn/blog_migrate/3aa9282414f33ed3fb282f2abc138e7a.png)
图4.3 C++程序与Java程序之间不进行网络编码则通信异常
为简化用户编程逻辑,基于KY-RTI设计的两个仿真程序可以不进行网络编码就能正常通信,但有1个简单要求。如图4.4所示,如果C++把123.45这个浮点数变成字符串'1'、'2'、'3'、'.'、'4'、'5',则C++不需要编码,Java程序也能够正确地接收。
![](https://i-blog.csdnimg.cn/blog_migrate/27f2ad213e1d23c465be7a6b30600bca.png)
图4.4 C++程序与Java程序在KY-RTI中的通信方式
下面几行代码是从4.4节时间管理程序中摘来的几条语句,略有改动,用户可能会经常用到。
(1)如果两个仿真成员都是C++程序,则使用下面语句对整型属性xPos进行打包和解包。
打包:
int xPos;
pAttrs->add(g_hxPos, (char*)&xPos, sizeof(int));
解包:
int xPos;
theAttributes.getValue(i, (char*)&xPos, valueLength);
(2)如果两个仿真成员采用不同语言编程,则应将整型属性xPos转为字符串。
也就是说,发送方应使用字符串发送,接收方应将接收到的字符串转为整数。
打包:
char s[20];
sprintf(s, "%d\0", xPos);
pAttrs->add(g_hxPos, (char*)s, strlen(s));
解包:
int xPos;
char str[20];
theAttributes.getValue(i, (char*)str, valueLength);
xPos = atoi(str);
本项目需要实现一个类似微信群或者QQ群聊天功能的GNU C++程序,每个人发送的消息都能够被群里的其他人看到。
每条聊天信息应包含2个内容:聊天者昵称、聊天的一句话,这样接收者就会知道是谁在发言。“聊天者昵称”用name表示,“聊天的一句话”用sentence表示,两个都是字符串类型。因为HLA是面向对象的,发送的数据要么采用对象类,要么采用交互类。本项目可采用交互类,将name和sentence封装到一个名叫“chat”的交互类中,如下列代码所示。
class chat { //交互类
string name; //参数
string sentence; //参数
}
下面采用KY-OMT创建fed文件,相应的chat.fed文件已经在3.3.3中创建完成,将该文件保存到KY-RTI的bin目录。
本项目对时间没有特别要求,不需要采用HLA时间管理机制。当RTI收到聊天信息时就立即发送给其他人,不需要调用tick服务。
该程序比较简单,一个Chat.cpp和HwFederateAmbassador.cpp就可以实现。前者通过调用创建联邦执行、加入联邦执行、公布和订购交互类、发送交互,仿真完成时退出联邦;后者用来接收RTI的回调消息。
Chat.cpp代码说明:
8-10行:定义交互类及其参数句柄变量;
27-36行:创建联邦执行;
38-53行:加入联邦执行;
57-59行:获取交互类及其参数句柄;
62行:公布交互类,只有公布之后才能够向RTI发送交互;
64行:订购交互类,只有订购之后才能够从RTI收到其他人的聊天内容;
68-87行:循环操作,每次输入一句话,并调用sendInteraction服务发送给RTI;当用户输入“exit”时则退出执行;
89-94行:退出联邦执行,不再参加仿真;
96-107行:销毁联邦。如果是最后一个仿真成员执行该操作,则整个仿真结束。
表4.1 C++聊天示例:Chat.cpp
- #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)
- {
- return hw_main(argc, argv);
- }
|
HwFederateAmbassador.cpp代码说明:
10-24行:由于不处理时间参数,因此如果接收到这种类型的receiveInteraction交互,则直接调用不带时间参数的服务来统一处理;
26-63行:处理接收到的聊天信息并输出。
表4.2 C++聊天示例:HwFederateAmbassador.cpp
- #include "fedtime.hh"
- #include "HwFederateAmbassador.hh"
- #include <iostream>
- using namespace std;
- extern RTI::InteractionClassHandle hChatClass;
- extern RTI::ParameterHandle hChatName;
- extern RTI::ParameterHandle hChatSentence;
- void HwFederateAmbassador::receiveInteraction (
- RTI::InteractionClassHandle theInteraction, // supplied C1
- const RTI::ParameterHandleValuePairSet& theParameters, // supplied C4
- const RTI::FedTime& theTime, // supplied C4
- const char *theTag, // supplied C4
- RTI::EventRetractionHandle theHandle) // supplied C1
- throw (
- RTI::InteractionClassNotKnown,
- RTI::InteractionParameterNotKnown,
- RTI::InvalidFederationTime,
- RTI::FederateInternalError)
- {
- //call the next service.
- this->receiveInteraction( theInteraction, theParameters, theTag );
- }
- void HwFederateAmbassador::receiveInteraction (
- RTI::InteractionClassHandle theInteraction, // supplied C1
- const RTI::ParameterHandleValuePairSet& theParameters, // supplied C4
- const char *theTag) // supplied C4
- throw (
- RTI::InteractionClassNotKnown,
- RTI::InteractionParameterNotKnown,
- RTI::FederateInternalError)
- {
- RTI::ParameterHandle paraHandle;
- RTI::ULong valueLength;
- //Usage of char[] and string
- char name[256]; //name of sender
- string sentence; //sentence of sender
- for ( int i = 0; i < theParameters.size(); i++ ) {
- paraHandle = theParameters.getHandle( i );
- if(paraHandle == hChatName) {
- theParameters.getValue(i, (char*)name, valueLength);
- /*If name is a double number, you can do this way.
- double name;
- theParameters.getValue(i, (char*)&name, valueLength);
- */
- } else if(paraHandle == hChatSentence) {
- sentence.resize(theParameters.getValueLength(i));
- theParameters.getValue(i, (char*)sentence.c_str(), valueLength);
- } else {
- cout << "Receive wrong parameter handle." << endl;
- }
- }
- cout << endl << name << ": " << sentence << endl;
- }
|
编译GNU C++程序,通常要先建一个Makefile文件。在Makefile文件中指定了所使用的编译器、编译命令、编译选项、连接的库和库目录等。
表4.3是编译聊天程序的Makefile文件,对于其他程序而言,格式都一样。在该Makefile中,只要关心第10、11、13行即可。其中,第10、11行指明了本程序有两个.cpp文件;第13行则指明了最终生成的可执行程序名。
表4.3 Makefile
- C++FLAGS = -DRTI_USES_STD_FSTREAM -DREENTRANT -D_RH72_GCC302 \
- -DRTI_HAS_THREADS -DPOSIX_PTHREAD_SEMANTICS -g -O3
- LDFLAGS += -g -O3
- LIBS = -lRTI-NG -lfedtime -lpthread
- C++ = g++
- OBJS = Chat.o \
- HwFederateAmbassador.o
- EXECUTABLE = chat
- RTI_ROOT_DIR = ${RTI_HOME}/${RTI_BUILD_TYPE}
- RTI_INC_DIR = ${RTI_ROOT_DIR}/include
- RTI_LIB_DIR = ${RTI_ROOT_DIR}/lib
- INC_PATH = -I${RTI_INC_DIR} -I.
- LIB_PATH = -L${RTI_LIB_DIR}
- # Build targets
- %.o : %.cpp
- @echo
- @echo Compiling $< ...
- @echo
- ${C++} -c ${C++FLAGS} ${INC_PATH} $< -o $@
- default: ${EXECUTABLE}
- ${EXECUTABLE}: ${OBJS}
- @echo
- @echo Linking $@ ...
- @echo
- ${C++} ${LDFLAGS} ${OBJS} -o $@ ${LIB_PATH} ${LIBS}
- clean:
- rm -rf *.o core *~ .depend Templates.DB ${EXECUTABLE}
|
执行下列命令生成可执行程序chat。
make
测试项目:在银河麒麟操作系统上运行2个GNU C++仿真成员,测试KY-RTI通信功能。
测试步骤:
第1步:修改RTI.rid,关闭tick开关。
因为本程序没有使用tick服务,所以需要关闭tick开关。
查看当前目录下是否存在RTI.rid,若没有则运行chat,则会自动生成该文件。将RTI.rid文件中的“;; UsingTickSwitch On”改为“;; UsingTickSwitch Off”。
第2步:启动KY-RTI。注意,KY-RTI的IP地址和端口号要与RTI.rid一致。
第3步:如图4.5和图4.6所示,开启两个终端,运行2个仿真成员,开始仿真。运行命令:
./chat
测试结果表明:聊天功能正常,KY-RTI支持中英文传输。
![](https://i-blog.csdnimg.cn/blog_migrate/328091d9802ac60de41a975fd0493e57.png)
图4.5 GNU C++聊天者1
![](https://i-blog.csdnimg.cn/blog_migrate/f431cbb9f2ec4dc9207ab80154172836.png)
图4.6 GNU C++聊天者2
4.4 时间管理程序
本仿真项目的名称为“TimeManagementExample”,当然也可以叫做“空中雄鹰”、“飞行表演”之类的响亮名字。名称规定了不是“TimeManagementExample”的程序不属于本项目。对HLA/RTI程序来说,联邦名称为“TimeManagementExample”,不是该名字的仿真成员不属于本仿真。
每个仿真成员拥有3架飞机,但其中只有1架飞机会起飞,飞机的x、y坐标为随机数(不考虑合理性),飞机会每隔1秒发布自己的位置信息。
飞机每隔1秒发布自己的位置信息,意味着该仿真应采用时间管理服务,仿真步长为1。
飞机发送的是自己的x、y二维态势信息,用一个对象类plane来封装,两个属性为xPos和yPos,类型为整型;但不管什么类型,在RTI中都是作为字符串来传送。如下列代码所示。
class plane { //对象类
int xPos; //属性
int yPos; //属性
}
下面采用KY-OMT创建fed文件,相应的tracer.fed文件已经在3.3.2中创建完成,将该文件保存到KY-RTI的bin目录。
该程序包括TimeManagement.cpp和HwFederateAmbassador.cpp两个实现文件。前者通过调用创建联邦执行、加入联邦执行、公布和订购对象类、注册对象实例、周期性地发送二维态势信息和推进仿真,仿真完成时退出联邦;后者用来接收RTI的二维态势信息,并处理相关的时间管理服务等。
TimeManagement.cpp代码说明:
10-17行:定义全局变量,由两个线程共享;
21行:联邦名称定义为“TimeManagementExample”;
22行:fed文件定义为“tracer.fed”;
35-44行:创建联邦执行;
46-61行:加入联邦执行;
65-67行:获取对象类及其属性句柄;
75行:公布对象类属性,只有公布之后才能够向RTI发送二维态势信息,即xPos和yPos;
76行:订购交互类属性,只有订购之后才能够从RTI收到二维态势信息;
81-86行:注册3架飞机;
93行:将仿真成员设置为时间管理受限的;
94-102行:等待RTI同意将该仿真成员设置为时间管理受限的(注意,由于Word自动对齐,100行之后会比之前多一个缩进,所以while循环的‘{}’没有对齐);
104行:将仿真成员设置为时间管控成员;
105-113行:等待RTI同意将该仿真成员设置为时间管控成员;
115行:打开异步消息开关;
122行:仿真周期设置为1秒;这里的逻辑时间1对应物理时间的1秒(假设设置为2,则1个逻辑时间单位对应物理时间的0.5秒,2个逻辑时间单位对应仿真周期1秒);
127-180行:每隔1秒循环推进仿真,直到中断退出仿真;
150行:发送飞机在下一时刻的二维态势信息(如果采用RO消息也可以发送当前时刻的态势,依仿
真模型而定);
163行:将仿真请求推进到下一步;
164-172行:等待RTI同意该仿真成员推进到下一步;
183-188行:退出联邦执行,不再参加仿真;
190-201行:销毁联邦。如果是最后一个仿真成员执行该操作,则整个仿真结束。
表4.4 C++时间管理示例:TimeManagement.cpp
- #include "HwFederateAmbassador.hh"
- #include <RTI.hh>
- #include <fedtime.hh>
- #include <unistd.h> //for usleep
- #include <stdlib.h> //for rand
- #include <iostream>
- using namespace std;
- RTI::ObjectHandle g_hInstance1, g_hInstance2, g_hInstance3;
- RTI::AttributeHandle g_hxPos;
- RTI::AttributeHandle g_hyPos;
- RTIfedTime g_currentTime = 0.0;
- bool g_bConstrained = false;
- bool g_bRegulation = false;
- bool g_granted = false;
- int hw_main(int argc, char *argv[])
- {
- const char *federationExecutionName = "TimeManagementExample";
- const char *FEDfile = "tracer.fed";
- char federateName[50];
- cout << "Please input the federate name: ";
- cin >> federateName;
- RTI::RTIambassador rti;
- HwFederateAmbassador fedAmb;
- RTIfedTime 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 ) {
- 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;
- }
- ///
- 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 ) {
- cerr << "FED_HW: ERROR:" << e << endl;
- return -1;
- }
- try {
- rti.enableTimeConstrained();
- while(!g_bConstrained) {
- //use tick
- // RTI.rid: ';; UsingTickSwitch On'
- rti.tick(0.001, 1.0);
- //don't use tick
- // RTI.rid: ';; UsingTickSwitch Off'
- //usleep(1000); //1 millisecond
- }
- rti.enableTimeRegulation((RTIfedTime)0.0, lookahead);
- while(!g_bRegulation) {
- //use tick
- // RTI.rid: ';; UsingTickSwitch On'
- rti.tick(0.001, 1.0);
- //don't use tick
- // RTI.rid: ';; UsingTickSwitch Off'
- //usleep(1000); //1 millisecond
- }
- rti.enableAsynchronousDelivery();
- } catch ( RTI::Exception& e ) {
- cerr << "FED_HW: ERROR:" << e << endl;
- return -1;
- }
- try {
- RTIfedTime intervalTime = 1.0;
- int xPos, yPos;
- const char *tag = "C++";
- int step = 0;
- while(1) {
- step++;
- cout << "Step: " << step << endl;
- 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];
- 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));
- try {
- rti.updateAttributeValues(g_hInstance1, *pAttrs, g_currentTime + lookahead, tag);
- } catch(...) {
- cerr << "Error: send interaction" << endl;
- }
- pAttrs->empty();
- delete pAttrs;
- //-----------------------------------------------------------
- RTIfedTime targetTime = g_currentTime + intervalTime;
- cout << "This federate will advance to " << targetTime << endl;
- try {
- rti.timeAdvanceRequest(targetTime);
- while(!g_granted) {
- //use tick
- // RTI.rid: ';; UsingTickSwitch On'
- rti.tick(0.001, 1.0);
- //don't use tick
- // RTI.rid: ';; UsingTickSwitch Off'
- //usleep(1000); //1 millisecond
- }
- g_granted = false;
- cout << "The federate has advanced to " << g_currentTime << endl << endl;
- } catch ( RTI::Exception& e ) {
- cerr << "FED_HW: ERROR:" << e << endl;
- return -1;
- }
- }
- //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 ) {
- 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)
- {
- return hw_main(argc, argv);
- }
|
HwFederateAmbassador.cpp代码说明:
7-14行:定义全局变量,由两个线程共享;
16-26行:将发现的飞机输出到终端;
28-89行:将收到的飞机态势信息输出到终端;
91-101行:RTI同意将仿真成员设置为时间管控成员;
103-113行:RTI同意将仿真成员设置为时间管理受限的成员;
115-125行:RTI同意仿真成员推进到下一步。
表4.5 C++时间管理示例:HwFederateAmbassador.cpp
- #include "fedtime.hh"
- #include "HwFederateAmbassador.hh"
- #include <stdlib.h> //atoi
- #include <iostream>
- using namespace std;
- extern RTI::ObjectHandle g_hInstance1, g_hInstance2, g_hInstance3;
- extern RTI::AttributeHandle g_hxPos;
- extern RTI::AttributeHandle g_hyPos;
- extern RTIfedTime g_currentTime;
- extern bool g_bConstrained;
- extern bool g_bRegulation;
- extern bool g_granted;
- 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)
- {
- cout << "discoverObjectInstance: " << theObject << "," << theObjectClass << "," << theObjectName << endl;
- }
- 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)
- {
- cout << "reflectAttributeValues: " << theObject << endl;
- RTI::AttributeHandle attrHandle;
- RTI::ULong valueLength;
- int value;
- char str[20];
- for (int i = 0; i < theAttributes.size(); i++) {
- attrHandle = theAttributes.getHandle( i );
- if(attrHandle == g_hxPos) {
- /* 如果两个仿真成员都是C++程序,则使用下面语句即可 */
- //theAttributes.getValue(i, (char*)&value, valueLength);
- /* 如果两个仿真成员采用不同语言编程,则发送方应使用字符串发送,接收方应将接收到的字符串转为整数 */
- theAttributes.getValue(i, (char*)str, valueLength);
- value = atoi(str);
- } else if(attrHandle == g_hyPos) {
- /* 如果两个仿真成员都是C++程序,则使用下面语句即可 */
- //theAttributes.getValue(i, (char*)&value, valueLength);
- /* 如果两个仿真成员采用不同语言编程,则发送方应使用字符串发送,接收方应将接收到的字符串转为整数 */
- theAttributes.getValue(i, (char*)str, valueLength);
- value = atoi(str);
- } else {
- cout << "Receive wrong parameter handle." << endl;
- }
- cout << " <" << attrHandle << "," << value << ">" << endl;
- }
- cout << " tag:" << theTag << endl;
- }
- void HwFederateAmbassador::timeRegulationEnabled (
- const RTI::FedTime& theFederateTime) // supplied C4
- throw (
- RTI::InvalidFederationTime,
- RTI::EnableTimeRegulationWasNotPending,
- RTI::FederateInternalError)
- {
- g_currentTime = theFederateTime;
- g_bRegulation = true;
- cout << "timeRegulationEnabled: " << theFederateTime << endl;
- }
- void HwFederateAmbassador::timeConstrainedEnabled (
- const RTI::FedTime& theFederateTime) // supplied C4
- throw (
- RTI::InvalidFederationTime,
- RTI::EnableTimeConstrainedWasNotPending,
- RTI::FederateInternalError)
- {
- g_currentTime = theFederateTime;
- g_bConstrained = true;
- cout << "timeRegulationEnabled: " << theFederateTime << endl;
- }
- void HwFederateAmbassador::timeAdvanceGrant (
- const RTI::FedTime& theTime) // supplied C4
- throw (
- RTI::InvalidFederationTime,
- RTI::TimeAdvanceWasNotInProgress,
- RTI::FederateInternalError)
- {
- g_currentTime = theTime;
- g_granted = true;
- cout << "timeAdvanceGrant: " << theTime << endl;
- }
|
在编译时要创建一个Makefile文件,参考4.3.4.1节把第10行的“Chat”改成“TimeManagement”,第13行的“chat”改成“timemanagement”即可。
执行下列命令生成可执行程序timemanagement。
make
测试项目:在银河麒麟操作系统上运行2个GNU C++仿真成员,两个仿真成员启动后尽可能快地向前推进;测试KY-RTI的HLA基本服务功能,特别是时间管理同步功能。
测试步骤:
第1步:修改RTI.rid,打开tick开关。
本程序使用了tick服务,因此需要打开tick开关。查看当前目录下是否有RTI.rid,若没有则运行程序后会在当前目录下自动生成RTI.rid。将RTI.rid文件中的“;; UsingTickSwitch Off”改为“;; UsingTickSwitch On”。
第2步:启动KY-RTI。注意,KY-RTI的IP地址和端口号要与RTI.rid一致。
第3步:开启两个终端,运行2个仿真成员,开始仿真。运行程序命令:
./time
测试结果表明:KY-RTI运行效率高,时间管理同步功能强。
测试说明:
(1)图4.7是KY-RTI的运行界面。可以看到,联邦名称为“TimeManagementExample”,两个名为“Air01”、“Air02”的仿真成员先后加入仿真;当运行一段时间之后,两个仿真成员被Ctrl+C中断执行,KY-RTI的监控器发现了二者的异常退出。当两个仿真成员都退出后,KY-RTI认为整个仿真结束,联邦被摧毁,KY-RTI清理现场,准备开始下一次仿真。
(2)图4.8是仿真成员“Air01”被Ctrl+C中断执行时的界面。之前,它能收到仿真成员“Air02”发送的二维态势信息。
(3)图4.9是仿真成员“Air02”被Ctrl+C中断执行时的界面。之前,只显示它自己的运行信息,没有收到其他仿真成员的二维态势信息。
(4)当仿真成员“Air01”启动后,在显示众多信息的情况下,瞬间运行到上万步;再启动仿真成员“Air02”,两者同步推进;图4.8显示的仿真时间为40412,图4.9显示的仿真时间为51420,相差10000多,其实就是一瞬间运行了上万步。
![](https://i-blog.csdnimg.cn/blog_migrate/8dc7e69f4e8fff34b73c6f8864405526.png)
图4.7 KY-RTI运行界面
![](https://i-blog.csdnimg.cn/blog_migrate/449043b498de62abf58004155d4d3a19.png)
图4.8 使用时间管理服务的仿真成员Air01
![](https://i-blog.csdnimg.cn/blog_migrate/3d534788806cd156dba7fe399aa048fb.png)
图4.9 使用时间管理服务的仿真成员Air02
TimeManagement.cpp使用了tick服务编程方法。下列代码在TimeManagement.cpp中一共出现了3处,使用tick服务来获取RTI的回调消息,如果没有收到,则循环调用tick服务,直到收到回调消息。
然而,如果把下面代码中的tick语句注释掉,换成后面的usleep语句,程序同样能够正常运行,于是程序就变成非tick服务方式。
表4.6 tick/usleep代码切换
- //use tick
- // RTI.rid: ';; UsingTickSwitch On'
- rti.tick(0.001, 1.0);
- //don't use tick
- // RTI.rid: ';; UsingTickSwitch Off'
- //usleep(1000); //1 millisecond
|
对本程序而言,这两种编程方法并没有本质区别,甚至非tick服务方式效率更高。但这两种方式在编程时还是有一些细节要注意,以表4.7中TimeManagement.cpp中的下列代码段为例,如果把第12行代码放到第1行与第2行之间,变成表4.8,那么对于tick服务来说,不会有任何问题;但改成usleep则可能while循环会一直等不到g_granted标识设置为真。
表4.7 tick/usleep代码切换不会导致问题
- rti.timeAdvanceRequest(targetTime);
- while(!g_granted) {
- //use tick
- // RTI.rid: ';; UsingTickSwitch On'
- rti.tick(0.001, 1.0);
- //don't use tick
- // RTI.rid: ';; UsingTickSwitch Off'
- //usleep(1000); //1 millisecond
- }
- g_granted = false;
|
因为主线程和回调线程是2个不同的线程,在表4.8中,当执行完第1行语句之后,RTI就可以给仿真成员发回调消息,回调线程收到回调消息后将全局变量g_granted变成true;当主线程执行第2条语句之后,g_granted变成false,则主线程再执行while语句会循环等待,因为回调服务已经处理过了,再也不会有新的回调服务来将g_granted变成true。
这里所说的问题不是KY-RTI特有的问题,而是不采用tick服务的仿真系统可能普遍存在的问题,譬如采用IEEE1516标准的RTI来开发仿真系统,一般都不使用tick服务(IEEE1516标准中称之为evokeCallback)。
表4.8 tick/usleep代码切换潜在问题
- rti.timeAdvanceRequest(targetTime);
- g_granted = false;
- while(!g_granted) {
- //use tick
- // RTI.rid: ';; UsingTickSwitch On'
- rti.tick(0.001, 1.0);
- //don't use tick
- // RTI.rid: ';; UsingTickSwitch Off'
- //usleep(1000); //1 millisecond
- }
|
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)