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

2023-05-16

第四章 C++程序设计

       本章讲述如何在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这个专有名称。

                                                              图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++程序,则它们在通信时不进行编码转换也能正常通信。

                                                              图4.2 C++程序之间不进行网络编码能正常通信

       Java虚拟机采用big-endian,而运行在Java虚拟机中的Java程序的主机字节序也为big-endian。如图4.3所示,如果C++程序与Java程序进行通信,不进行编码转换就不能正确通信。

                                                              图4.3 C++程序与Java程序之间不进行网络编码则通信异常

       为简化用户编程逻辑,基于KY-RTI设计的两个仿真程序可以不进行网络编码就能正常通信,但有1个简单要求。如图4.4所示,如果C++把123.45这个浮点数变成字符串'1'、'2'、'3'、'.'、'4'、'5',则C++不需要编码,Java程序也能够正确地接收。

                                                              图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);

4.3 聊天程序

4.3.1需求分析

       本项目需要实现一个类似微信群或者QQ群聊天功能的GNU C++程序,每个人发送的消息都能够被群里的其他人看到。

4.3.2项目设计

       每条聊天信息应包含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服务。

4.3.3代码设计

       该程序比较简单,一个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

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

       HwFederateAmbassador.cpp代码说明:

10-24行:由于不处理时间参数,因此如果接收到这种类型的receiveInteraction交互,则直接调用不带时间参数的服务来统一处理;

26-63行:处理接收到的聊天信息并输出。

                                                              表4.2  C++聊天示例:HwFederateAmbassador.cpp

  1. #include "fedtime.hh"
  2. #include "HwFederateAmbassador.hh"
  3. #include <iostream>
  4. using namespace std;
  5. extern RTI::InteractionClassHandle  hChatClass;
  6. extern RTI::ParameterHandle     hChatName;
  7. extern RTI::ParameterHandle     hChatSentence;
  8. void HwFederateAmbassador::receiveInteraction (
  9.     RTI::InteractionClassHandle       theInteraction,         // supplied C1
  10.     const RTI::ParameterHandleValuePairSet& theParameters,  // supplied C4
  11.     const RTI::FedTime&                     theTime,             // supplied C4
  12.     const char                                *theTag,             // supplied C4
  13.     RTI::EventRetractionHandle             theHandle)          // supplied C1
  14. throw (
  15.     RTI::InteractionClassNotKnown,
  16.     RTI::InteractionParameterNotKnown,
  17.     RTI::InvalidFederationTime,
  18.     RTI::FederateInternalError)
  19. {
  20.     //call the next service.
  21.     this->receiveInteraction( theInteraction, theParameters, theTag );
  22. }
  23. void HwFederateAmbassador::receiveInteraction (
  24.     RTI::InteractionClassHandle       theInteraction, // supplied C1
  25.     const RTI::ParameterHandleValuePairSet& theParameters,  // supplied C4
  26.     const char                             *theTag)         // supplied C4
  27. throw (
  28.     RTI::InteractionClassNotKnown,
  29.     RTI::InteractionParameterNotKnown,
  30.     RTI::FederateInternalError)
  31. {
  32.     RTI::ParameterHandle paraHandle;
  33.     RTI::ULong           valueLength;
  34.     //Usage of char[] and string
  35.     char name[256];   //name of sender
  36.     string sentence;  //sentence of sender
  37.     for ( int i = 0; i < theParameters.size(); i++ ) {
  38.         paraHandle = theParameters.getHandle( i );
  39.         if(paraHandle == hChatName) {
  40.             theParameters.getValue(i, (char*)name, valueLength);
  41.             /*If name is a double number, you can do this way.
  42.                 double name;
  43.                 theParameters.getValue(i, (char*)&name, valueLength);
  44.             */
  45.         } else if(paraHandle == hChatSentence) {
  46.             sentence.resize(theParameters.getValueLength(i));
  47.             theParameters.getValue(i, (char*)sentence.c_str(), valueLength);
  48.         } else {
  49.             cout << "Receive wrong parameter handle." << endl;
  50.         }
  51.     }
  52.     cout << endl << name << ": " << sentence << endl;
  53. }

4.3.4编译运行

4.3.4.1编译

       编译GNU C++程序,通常要先建一个Makefile文件。在Makefile文件中指定了所使用的编译器、编译命令、编译选项、连接的库和库目录等。

       表4.3是编译聊天程序的Makefile文件,对于其他程序而言,格式都一样。在该Makefile中,只要关心第10、11、13行即可。其中,第10、11行指明了本程序有两个.cpp文件;第13行则指明了最终生成的可执行程序名。

                                                              表4.3  Makefile

  1. C++FLAGS = -DRTI_USES_STD_FSTREAM -DREENTRANT -D_RH72_GCC302 \
  2.                 -DRTI_HAS_THREADS -DPOSIX_PTHREAD_SEMANTICS -g -O3
  3. LDFLAGS += -g -O3
  4. LIBS = -lRTI-NG -lfedtime -lpthread
  5. C++ = g++
  6. OBJS =  Chat.o \
  7.         HwFederateAmbassador.o
  8. EXECUTABLE = chat
  9. RTI_ROOT_DIR = ${RTI_HOME}/${RTI_BUILD_TYPE}
  10. RTI_INC_DIR = ${RTI_ROOT_DIR}/include
  11. RTI_LIB_DIR = ${RTI_ROOT_DIR}/lib
  12. INC_PATH = -I${RTI_INC_DIR} -I.
  13. LIB_PATH = -L${RTI_LIB_DIR}
  14. # Build targets
  15. %.o : %.cpp
  16.     @echo
  17.     @echo Compiling $< ...
  18.     @echo
  19.     ${C++} -c ${C++FLAGS} ${INC_PATH} $< -o $@
  20. default: ${EXECUTABLE}
  21. ${EXECUTABLE}: ${OBJS}
  22.     @echo
  23.     @echo Linking $@ ...
  24.     @echo
  25.     ${C++} ${LDFLAGS} ${OBJS} -o $@ ${LIB_PATH} ${LIBS}
  26. clean:
  27.     rm -rf *.o core *~ .depend Templates.DB ${EXECUTABLE}

       执行下列命令生成可执行程序chat。

       make

4.3.4.2测试运行

       测试项目:在银河麒麟操作系统上运行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支持中英文传输。

                                                              图4.5 GNU C++聊天者1

                                                              图4.6 GNU C++聊天者2

4.4 时间管理程序

4.4.1需求分析

       本仿真项目的名称为“TimeManagementExample”,当然也可以叫做“空中雄鹰”、“飞行表演”之类的响亮名字。名称规定了不是“TimeManagementExample”的程序不属于本项目。对HLA/RTI程序来说,联邦名称为“TimeManagementExample”,不是该名字的仿真成员不属于本仿真。

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

4.4.2项目设计

       飞机每隔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目录。

4.4.3代码设计

       该程序包括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

  1. #include "HwFederateAmbassador.hh"
  2. #include <RTI.hh>
  3. #include <fedtime.hh>
  4. #include <unistd.h> //for usleep
  5. #include <stdlib.h> //for rand
  6. #include <iostream>
  7. using namespace std;
  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. bool                           g_bConstrained = false;
  13. bool                           g_bRegulation = false;
  14. bool                           g_granted = false;
  15. int hw_main(int argc, char *argv[])
  16. {
  17.     const char *federationExecutionName = "TimeManagementExample";
  18.     const char *FEDfile = "tracer.fed";
  19.     char federateName[50];
  20.     cout << "Please input the federate name: ";
  21.     cin >> federateName;
  22.     RTI::RTIambassador       rti;
  23.     HwFederateAmbassador     fedAmb;
  24.     RTIfedTime               lookahead = 1.0;
  25.     try {
  26.         RTI::FederateHandle      federateId;
  27.         try {
  28.             rti.createFederationExecution(federationExecutionName, FEDfile);
  29.         }
  30.         catch ( RTI::FederationExecutionAlreadyExists& e ) {
  31.             //According to the HLA standard, only the first federate can call this service succesfully.
  32.             //cerr << "FED_HW: Note: Federation execution already exists." << e << endl;
  33.         } catch ( RTI::Exception& e ) {
  34.             cerr << "FED_HW: ERROR:" << e << endl;
  35.             return -1;
  36.         }
  37.         try {
  38.             federateId = rti.joinFederationExecution(federateName, federationExecutionName, &fedAmb);
  39.         } catch (RTI::FederateAlreadyExecutionMember& e) {
  40.             cerr << "FED_HW: ERROR: " << argv[1]
  41.                  << " already exists in the Federation Execution "
  42.                  << federationExecutionName << "." << endl;
  43.             cerr << e << endl;
  44.             return -1;
  45.         } catch (RTI::FederationExecutionDoesNotExist&) {
  46.             cerr << "FED_HW: ERROR: Federation Execution "
  47.                  << "does not exists."<< endl;
  48.             return -1;
  49.         } catch ( RTI::Exception& e ) {
  50.             cerr << "FED_HW: ERROR:" << e << endl;
  51.             return -1;
  52.         }
  53.         ///
  54.         RTI::ObjectClassHandle hPlaneClass = rti.getObjectClassHandle("plane");
  55.         g_hxPos = rti.getAttributeHandle("xPos", hPlaneClass);
  56.         g_hyPos = rti.getAttributeHandle("yPos", hPlaneClass);
  57.         RTI::AttributeHandleSet *theAttributes;
  58.         theAttributes = RTI::AttributeHandleSetFactory::create(2);
  59.         theAttributes->add(g_hxPos);
  60.         theAttributes->add(g_hyPos);
  61.         rti.publishObjectClass(hPlaneClass, *theAttributes);
  62.         rti.subscribeObjectClassAttributes(hPlaneClass, *theAttributes);
  63.         theAttributes->empty();
  64.         delete theAttributes;
  65.         //register one plane
  66.         g_hInstance1 = rti.registerObjectInstance(hPlaneClass);
  67.         //register 2nd plane
  68.         g_hInstance2 = rti.registerObjectInstance(hPlaneClass);
  69.         //register 3rd plane
  70.         g_hInstance3 = rti.registerObjectInstance(hPlaneClass);
  71.     } catch ( RTI::Exception& e ) {
  72.         cerr << "FED_HW: ERROR:" << e << endl;
  73.         return -1;
  74.     }
  75.     try {
  76.         rti.enableTimeConstrained();
  77.         while(!g_bConstrained) {
  78.             //use tick
  79.             //    RTI.rid: ';; UsingTickSwitch On'
  80.             rti.tick(0.001, 1.0);
  81.             //don't use tick
  82.             //    RTI.rid: ';; UsingTickSwitch Off'
  83.             //usleep(1000); //1 millisecond
  84.         }
  85.         rti.enableTimeRegulation((RTIfedTime)0.0, lookahead);
  86.         while(!g_bRegulation) {
  87.             //use tick
  88.             //    RTI.rid: ';; UsingTickSwitch On'
  89.             rti.tick(0.001, 1.0);
  90.             //don't use tick
  91.             //    RTI.rid: ';; UsingTickSwitch Off'
  92.             //usleep(1000); //1 millisecond
  93.         }
  94.         rti.enableAsynchronousDelivery();
  95.     } catch ( RTI::Exception& e ) {
  96.         cerr << "FED_HW: ERROR:" << e << endl;
  97.         return -1;
  98.     }
  99.     try {
  100.         RTIfedTime intervalTime = 1.0;
  101.         int xPos, yPos;
  102.         const char *tag = "C++";
  103.         int step = 0;
  104.         while(1) {
  105.             step++;
  106.             cout << "Step: " << step << endl;
  107.             xPos=rand();
  108.             yPos=rand();
  109.             RTI::AttributeHandleValuePairSet* pAttrs = NULL;
  110.             pAttrs = RTI::AttributeSetFactory::create (2);
  111.             /* 如果两个仿真成员都是C++程序,则使用下面两条语句即可 */
  112.             //pAttrs->add(g_hxPos, (char*)&xPos, sizeof(int));
  113.             //pAttrs->add(g_hyPos, (char*)&yPos, sizeof(int));
  114.             /* 如果两个仿真成员采用不同语言编程,则应转为字符串再发送 */
  115.             char s[20];
  116.             sprintf(s, "%d\0", xPos);
  117.             pAttrs->add(g_hxPos, (char*)s, strlen(s));
  118.             sprintf(s, "%d\0", yPos);
  119.             pAttrs->add(g_hyPos, (char*)s, strlen(s));
  120.             try {
  121.                 rti.updateAttributeValues(g_hInstance1, *pAttrs, g_currentTime + lookahead, tag);
  122.             } catch(...) {
  123.                 cerr << "Error: send interaction" << endl;
  124.             }
  125.             pAttrs->empty();
  126.             delete pAttrs;
  127.             //-----------------------------------------------------------
  128.             RTIfedTime targetTime = g_currentTime + intervalTime;
  129.             cout << "This federate will advance to " << targetTime << endl;
  130.             try {
  131.                 rti.timeAdvanceRequest(targetTime);
  132.                 while(!g_granted) {
  133.                     //use tick
  134.                     //    RTI.rid: ';; UsingTickSwitch On'
  135.                     rti.tick(0.001, 1.0);
  136.                     //don't use tick
  137.                     //    RTI.rid: ';; UsingTickSwitch Off'
  138.                     //usleep(1000); //1 millisecond
  139.                 }
  140.                 g_granted = false;
  141.                 cout << "The federate has advanced to " << g_currentTime << endl << endl;
  142.             } catch ( RTI::Exception& e ) {
  143.                 cerr << "FED_HW: ERROR:" << e << endl;
  144.                 return -1;
  145.             }
  146.         }
  147.         //After the program exits, the RTI will automatically calls 'resignFederationExecution' and 'destroyFederationExecution'. Of course, you can write them for yourself.
  148.         try {
  149.             rti.resignFederationExecution( RTI::DELETE_OBJECTS_AND_RELEASE_ATTRIBUTES );
  150.         } catch ( RTI::Exception& e ) {
  151.             cerr << "FED_HW: ERROR:" << e << endl;
  152.             return -1;
  153.         }
  154.         try {
  155.             rti.destroyFederationExecution( federationExecutionName );
  156.         } catch ( RTI::FederatesCurrentlyJoined& /* e */ ) {
  157.             cerr << "FED_HW: FederatesCurrentlyJoined" << endl;
  158.             return 0;
  159.         } catch ( RTI::FederationExecutionDoesNotExist& /* e */) {
  160.             cerr << "FED_HW: FederationExecutionDoesNotExist" << endl;
  161.             return 0;
  162.         } catch ( RTI::Exception& e ) {
  163.             cerr << "FED_HW: ERROR:" << e << endl;
  164.             return -1;
  165.         }
  166.     } catch (RTI::ConcurrentAccessAttempted& e) {
  167.         cerr << e << endl;
  168.         return -1;
  169.     } catch ( RTI::Exception& e ) {
  170.         cerr << "FED_HW: ERROR:" << e << endl;
  171.         return -1;
  172.     }
  173.     return 0;
  174. }
  175. int
  176. main(int argc, char** argv)
  177. {
  178.     return hw_main(argc, argv);
  179. }

       HwFederateAmbassador.cpp代码说明:

7-14行:定义全局变量,由两个线程共享;

16-26行:将发现的飞机输出到终端;

28-89行:将收到的飞机态势信息输出到终端;

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

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

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

                                                              表4.5  C++时间管理示例:HwFederateAmbassador.cpp

  1. #include "fedtime.hh"
  2. #include "HwFederateAmbassador.hh"
  3. #include <stdlib.h> //atoi
  4. #include <iostream>
  5. using namespace std;
  6. extern RTI::ObjectHandle               g_hInstance1, g_hInstance2, g_hInstance3;
  7. extern RTI::AttributeHandle           g_hxPos;
  8. extern RTI::AttributeHandle           g_hyPos;
  9. extern RTIfedTime                       g_currentTime;
  10. extern bool                              g_bConstrained;
  11. extern bool                              g_bRegulation;
  12. extern bool                              g_granted;
  13. void HwFederateAmbassador::discoverObjectInstance (
  14.     RTI::ObjectHandle          theObject,      // supplied C1
  15.     RTI::ObjectClassHandle     theObjectClass, // supplied C1
  16.     const char *          theObjectName)  // supplied C4
  17. throw (
  18.     RTI::CouldNotDiscover,
  19.     RTI::ObjectClassNotKnown,
  20.     RTI::FederateInternalError)
  21. {
  22.     cout << "discoverObjectInstance: " << theObject << "," << theObjectClass << "," << theObjectName << endl;
  23. }
  24. void HwFederateAmbassador::reflectAttributeValues (
  25.     RTI::ObjectHandle                 theObject,     // supplied C1
  26.     const RTI::AttributeHandleValuePairSet& theAttributes, // supplied C4
  27.     const RTI::FedTime&                     theTime,       // supplied C1
  28.     const char                             *theTag,        // supplied C4
  29.     RTI::EventRetractionHandle        theHandle)     // supplied C1
  30. throw (
  31.     RTI::ObjectNotKnown,
  32.     RTI::AttributeNotKnown,
  33.     RTI::FederateOwnsAttributes,
  34.     RTI::InvalidFederationTime,
  35.     RTI::FederateInternalError)
  36. {
  37.     //call the next service.
  38.     reflectAttributeValues(theObject, theAttributes, theTag);
  39. }
  40. void HwFederateAmbassador::reflectAttributeValues (
  41.     RTI::ObjectHandle                 theObject,     // supplied C1
  42.     const RTI::AttributeHandleValuePairSet& theAttributes, // supplied C4
  43.     const char                             *theTag)        // supplied C4
  44. throw (
  45.     RTI::ObjectNotKnown,
  46.     RTI::AttributeNotKnown,
  47.     RTI::FederateOwnsAttributes,
  48.     RTI::FederateInternalError)
  49. {
  50.     cout << "reflectAttributeValues: " << theObject << endl;
  51.     RTI::AttributeHandle attrHandle;
  52.     RTI::ULong           valueLength;
  53.     int value;
  54.     char str[20];
  55.     for (int i = 0; i < theAttributes.size(); i++) {
  56.         attrHandle = theAttributes.getHandle( i );
  57.         if(attrHandle == g_hxPos) {
  58.             /* 如果两个仿真成员都是C++程序,则使用下面语句即可 */
  59.             //theAttributes.getValue(i, (char*)&value, valueLength);
  60.             /* 如果两个仿真成员采用不同语言编程,则发送方应使用字符串发送,接收方应将接收到的字符串转为整数 */
  61.             theAttributes.getValue(i, (char*)str, valueLength);
  62.             value = atoi(str);
  63.         } else if(attrHandle == g_hyPos) {
  64.             /* 如果两个仿真成员都是C++程序,则使用下面语句即可 */
  65.             //theAttributes.getValue(i, (char*)&value, valueLength);
  66.             /* 如果两个仿真成员采用不同语言编程,则发送方应使用字符串发送,接收方应将接收到的字符串转为整数 */
  67.             theAttributes.getValue(i, (char*)str, valueLength);
  68.             value = atoi(str);
  69.         } else {
  70.             cout << "Receive wrong parameter handle." << endl;
  71.         }
  72.         cout << "    <" << attrHandle << "," << value << ">" << endl;
  73.     }
  74.     cout << "    tag:" << theTag << endl;
  75. }
  76. void HwFederateAmbassador::timeRegulationEnabled (
  77.     const  RTI::FedTime& theFederateTime) // supplied C4
  78. throw (
  79.     RTI::InvalidFederationTime,
  80.     RTI::EnableTimeRegulationWasNotPending,
  81.     RTI::FederateInternalError)
  82. {
  83.     g_currentTime = theFederateTime;
  84.     g_bRegulation = true;
  85.     cout << "timeRegulationEnabled: " << theFederateTime << endl;
  86. }
  87. void HwFederateAmbassador::timeConstrainedEnabled (
  88.     const RTI::FedTime& theFederateTime) // supplied C4
  89. throw (
  90.     RTI::InvalidFederationTime,
  91.     RTI::EnableTimeConstrainedWasNotPending,
  92.     RTI::FederateInternalError)
  93. {
  94.     g_currentTime = theFederateTime;
  95.     g_bConstrained = true;
  96.     cout << "timeRegulationEnabled: " << theFederateTime << endl;
  97. }
  98. void HwFederateAmbassador::timeAdvanceGrant (
  99.     const RTI::FedTime& theTime) // supplied C4
  100. throw (
  101.     RTI::InvalidFederationTime,
  102.     RTI::TimeAdvanceWasNotInProgress,
  103.     RTI::FederateInternalError)
  104. {
  105.     g_currentTime = theTime;
  106.     g_granted = true;
  107.     cout << "timeAdvanceGrant: " << theTime << endl;
  108. }

4.4.4编译运行   

4.4.4.1编译

       在编译时要创建一个Makefile文件,参考4.3.4.1节把第10行的“Chat”改成“TimeManagement”,第13行的“chat”改成“timemanagement”即可。

       执行下列命令生成可执行程序timemanagement。

       make

4.4.4.2测试运行

       测试项目:在银河麒麟操作系统上运行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多,其实就是一瞬间运行了上万步。

                                                              图4.7 KY-RTI运行界面

                                                              图4.8 使用时间管理服务的仿真成员Air01

                                                              图4.9 使用时间管理服务的仿真成员Air02

4.4.5 tick服务/非tick服务的切换

       TimeManagement.cpp使用了tick服务编程方法。下列代码在TimeManagement.cpp中一共出现了3处,使用tick服务来获取RTI的回调消息,如果没有收到,则循环调用tick服务,直到收到回调消息。

       然而,如果把下面代码中的tick语句注释掉,换成后面的usleep语句,程序同样能够正常运行,于是程序就变成非tick服务方式。

                                                              表4.6  tick/usleep代码切换

  1.                     //use tick
  2.                     //    RTI.rid: ';; UsingTickSwitch On'
  3.                     rti.tick(0.001, 1.0);
  4.                     //don't use tick
  5.                     //    RTI.rid: ';; UsingTickSwitch Off'
  6.                     //usleep(1000); //1 millisecond

       对本程序而言,这两种编程方法并没有本质区别,甚至非tick服务方式效率更高。但这两种方式在编程时还是有一些细节要注意,以表4.7中TimeManagement.cpp中的下列代码段为例,如果把第12行代码放到第1行与第2行之间,变成表4.8,那么对于tick服务来说,不会有任何问题;但改成usleep则可能while循环会一直等不到g_granted标识设置为真。

                                                              表4.7  tick/usleep代码切换不会导致问题

  1.                 rti.timeAdvanceRequest(targetTime);
  2.                 while(!g_granted) {
  3.                     //use tick
  4.                     //    RTI.rid: ';; UsingTickSwitch On'
  5.                     rti.tick(0.001, 1.0);
  6.                     //don't use tick
  7.                     //    RTI.rid: ';; UsingTickSwitch Off'
  8.                     //usleep(1000); //1 millisecond
  9.                 }
  10.                 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代码切换潜在问题

  1.                 rti.timeAdvanceRequest(targetTime);
  2.                 g_granted = false;
  3.                 while(!g_granted) {
  4.                     //use tick
  5.                     //    RTI.rid: ';; UsingTickSwitch On'
  6.                     rti.tick(0.001, 1.0);
  7.                     //don't use tick
  8.                     //    RTI.rid: ';; UsingTickSwitch Off'
  9.                     //usleep(1000); //1 millisecond
  10.                 }

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分布仿真技术:附录1 分组聊天(HLA数据分发管理的应用)

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

随机推荐

  • Qt Creator中添加菜单栏实例

    Qt Creator中添加菜单栏 nbsp 实例是本文介绍的内容 内容不多 很丰富的实现 先来看内容 Qt中的菜单栏添加相对VC比较简单 今天找了一天的槽函数如何添加 到了下午的时候终于有些眉目 现在我们来看一下在Qt中如何添加菜单栏 1
  • 使用 OBS 进行区域录制

    1 OBS 与区域录屏 实际上 OBS 的使用场景可谓是与区域录屏格格不入的 虽然我们依旧有一些办法在 OBS 中达到区域录屏的目的 xff0c 但其操作实在过于繁琐 xff0c 还不如直接使用 QQ 或者 Windows 最新的自带截屏录
  • Linux驱动与一般应用的区别【Linux驱动之路一】

    Linux驱动和一般应用的区别 xff0c 大致可以归类为以下几点 xff1a 一 Linux驱动 属于内核级 xff0c 驱动程序的崩溃会导致整个系统的崩溃 xff0c 例如在驱动程序中出现了非法指针的应用 xff0c 就会导致系统的崩溃
  • Docker 初学者指南 — 如何使用 Docker-Compose 创建客户端/服务器端

    您是一名开发人员并且想探索 docker compose xff1f 这篇文章是为你而写的 在对 Docker Compose 进行简短介绍后 xff0c 您将能够使用 Docker 创建您的第一个客户端 服务器端应用程序 注意 xff1a
  • WSTOOL中止下载后的错误。{ROS}

    运行wstool init j8 src hydro desktop full wet rosinstall时 xff0c 由于网络原因中止 xff0c 再开的时候会报错 错误提示 xff1a Error There already is
  • FreeRTOS 启动流程浅析 vTaskStartScheduler --待续

    1 Scheduler status xff1a 全局标志 xff1a static volatile BaseType t xSchedulerRunning 61 pdFALSE 取值 xff1a define taskSCHEDULE
  • Windows的linux子系统无法使用NFS

    需求 xff1a 不能装双系统 xff0c 虚拟机本地文件共享又不方便 xff0c 所以想要用win10商店的linux子系统 xff08 ubuntu18 04 xff09 配合NFS实现本地的linux环境编程测试 xff0c 再同步到
  • Docker

    文章目录 Docker1 Docker简介1 1 什么是虚拟化1 2 什么是Docker1 3 容器与虚拟机比较1 4 Docker 组件1 4 1 Docker服务器与客户端1 4 2 Docker镜像与容器1 4 3 Registry
  • tensorflow图优化详解

    一 运行时优化 Grappler是TensorFlow提供的运行时优化功能 xff0c 图1为Grappler模块主要功能的UML关系图 其中tensorflow grappler GrapplerItem 表示待优化的TensforFlo
  • 理解一维数组中 buf、buf[0]、&buf[0]、&buf 四个符号的含义

    本文内容摘自 C语言内核深度解析 一书的深入学习数组部分 以 int buf 100 61 0 为例 xff0c 集中讨论 buf buf 0 amp buf 0 amp buf 四个字符含义的内涵 1 buf xff1a 有两层含义 xf
  • 无人机航线规划软件[航线通]

    无人机航线规划软件 航线通 xff08 RoutePlan xff09 是数据禾 xff08 databox store xff09 研发的具有自主知识产权的无人机航线规划及飞行的移动端控制软件 xff0c 主要适用于大疆无人机 xff0c
  • Ubuntu20.04安装Cartographer_ros,conda环境X11问题

    absl的解决 conda 环境的 X11 问题 xff1a conda span class token function install span c conda forge xorg libx11
  • 单目视觉定位测距的两种方式

    单目定位和双目定位的选择 xff0c 我觉得主要还是成本和时间的考虑 之前也尝试过双目定位 xff0c 感觉要更精准些 xff0c 但双目测距需要对两幅图像进行图像变换和极线匹配 xff0c 稍微耗时了一些 这几天尝试了一下单摄像头进行测距
  • KY-RTI分布仿真技术:附录3 国产化(操作系统+CPUs)

    以操作系统和CPU为代表的国产化是当前仿真系统实现的必然趋势 本章以聊天程序为例 xff0c 展示了KY RTI在多种国产操作系统和国产CPU上的运行结果 聊天程序是一个入门程序 xff0c 本身比较简单 xff0c 不追求界面的美观 相信
  • KY-RTI分布仿真技术:前言

    前 言 自从美国国防部建模与仿真办公室 xff08 DMSO xff09 首次提出高层体系结构 xff08 High Level Architecture xff0c HLA xff09 概念以来 xff0c HLA仿真技术得到了迅猛发展
  • KY-RTI分布仿真技术:第一章 简介

    第一章 简介 高层体系结构 xff08 High Level Architecture xff0c HLA xff09 是美国国防部为了解决美军在各个领域开发出来的多种模型和各类仿真系统的互联和互操作问题而提出的一种分布式仿真标准 xff0
  • KY-RTI分布仿真技术:第二章 系统安装

    第二章 系统安装 本章介绍KY RTI在Linux和Windows操作系统下的安装方法 2 1 Linux安装 安装程序 xff1a RTI 1 3NGv6 tar gz 假设当前的Linux用户名为lbq xff0c HOME目录为 ho
  • KY-RTI分布仿真技术:第三章 KY-OMT对象模型模板工具

    第三章 KY OMT对象模型模板工具 本章介绍了对象模型模板的相关知识 xff0c 以及如何使用KY OMT对象模型模板工具创建对象模型文件 3 1 对象模型模板 HLA1 3包含3个标准 xff0c 第一个是描述整个联邦和联邦成员必须遵循
  • 基于SSM的后台管理系统总结

    文章目录 SSM环境搭建环境准备1 1 数据库与表结构1 2 maven工程搭建 pom 1 3编写实体类编写接口 SSM整合 web 与产品查询Spring环境搭建web xml 配置Spring MVC 环境搭建测试运行 资源地址 首先
  • KY-RTI分布仿真技术:第四章 C++程序设计

    第四章 C 43 43 程序设计 本章讲述如何在Linux操作系统上设计GNU C 43 43 程序 演示了2个程序 xff1a 聊天程序chat和时间管理程序time chat使用HLA的交互类进行通信 xff0c 没有采用tick服务