本章从RTI开发者的角度简单地介绍HLA1.3标准中的数据分发管理(DDM,Data Distributed Management)并给出了一个具体的示例。前面介绍了基于各种程序设计语言开发的聊天程序示例,一个仿真成员的聊天内容会被发送给所有其他仿真成员;但能不能将仿真成员进行分组,组内的成员在聊天时不影响其他组呢?HLA数据分发管理可以完美地实现这一功能,甚至实现更进一步的功能:将仿真成员分组,一个仿真成员可以向任意组内的仿真成员发送消息;当然自己除外,因为HLA标准规定一个仿真成员不会收到自己发送的消息,自己发送的消息没有必要在网络上走一圈再送给自己。
HLA是一个数据交换的标准,仿真成员通过RTI软件交换数据。HLA为数据交换提供了两种过滤机制:
(1)声明管理服务
声明管理服务主要包含对象类和交互类的公布、订购、取消公布、取消订购等一系列服务。以战场仿真为例,如果一个仿真成员订购了对象类plane,则它会收到任何该对象类实例所更新的属性值;以本文的聊天程序为例,如果一个仿真成员订购了交互类chat,则它会收到任何其他人的聊天内容。
(2)数据分发管理服务
数据分发管理服务提供了更精细的数据过滤方法。以战场仿真为例,一个仿真成员在订购对象类plane时可以加一个“限制”,当一个plane对象实例在更新属性时也带一个“限制”,在HLA1.3标准中这个“限制”称之为区域“region”。我们把前一个区域称之为“订购区域”,后一个区域称之为“更新区域”,当两个区域有部分重叠时,则订购者会收到发送者发送的数据;以本文的聊天程序为例,如果一个仿真成员在订购交互类时带一个“订购区域”,发送交互时带一个“发送区域”,则仅当两个区域有部分重叠时,则订购者会收到发送者发送的聊天内容。这就意味着,如果将“订购区域”分为2个组,则发送者可以向任意一个组发送聊天内容。
10.2 数据分发管理中的概念
如图10.1所示,本节以红蓝双方的战场仿真为例,简单地介绍数据分发管理中的相关概念。
(1)routing space(路由空间):路由空间可以理解为整个仿真空间。
路由空间可以定义自己的维“dimension”。在HLA中,正如“联邦”代表整个系统,是一个“虚”的概念,它到底代表所有的人、所有的机器、所有的程序还是其他什么呢?路由空间也是一个“虚”的概念,不特指某个具体的空间。
(2)region(区域):一个区域包含多个分域(extent)。图10.1可以理解为有3支红方部队要去攻占蓝方的2个山头。红方区域包含3个分域,每个分域可以理解为1支部队的部署态势;蓝方区域包含2个分域,每个分域可以理解为1个山头。
区域没有维的概念。你可以把它理解为一个集合,该集合中含有若干个分域。
(3)extent(分域):本文把extent称之为分域,有些文章中称之为“限域”。
分域有维的概念,是区域中的一个具体的小空间。在数学上,如果每个集合只包含1个元素,那么我们可以离开“集合”这个概念,而直接讨论其中的元素就可以了。类似地,假设规定每个区域只有1个分域,那么HLA中可以去掉区域的概念,而直接讨论分域;由此分域与路由空间这两个概念就更容易理解了,两者都有“维”的概念,前者是由多个“维”构成的具体空间;后者是由多个“维”构成的“虚”空间。
一个具体空间的大小是有尺寸的,而“虚”空间的尺寸则无限大(不超过32位的最大整数)。这样,就引入了range的概念。
(4)range(范围):将一个“维”的上界和下界定义后就是范围。有人可能会说,本次仿真规定,“作战范围为方圆300千米”,这是路由空间的范围吗?其实,在RTI中,你没有办法去设置一个路由空间的范围,你只能设置一个分域中维的范围。前面的一句话意味着每个分域的范围在方圆300千米之内。当你设置维的范围之后,这就意味着由多个维构成的分域是一个方形空间,绝对不会是一个类似三角形、菱形、五角星、六边形之类的奇怪形状。
(5)dimension(维):不要简单地将“维”理解为三维空间中的“维”,任何属性都可以定义为一个“维”。譬如面包师傅每天做“咸”和“甜”两种面包,则面包的这种“味道”就是面包的一个属性,可以定义为一个“维”;从公布订购关系来看,面包师傅公布“咸”和“甜”两种面包,顾客按照自己的喜好订购这两类面包,快递员(RTI)按照顾客的需求负责将面包配送给顾客。在后面的聊天程序中,按照仿真成员句柄的奇偶特性分为2组,这也是一个维。
在HLA1.3标准中,“维”必须定义为大于等于0的整数,不能超过32位整数的最大值。假设最大值为1000,如果你需要的最大值为2000,则可用的一种办法是对各个数进行线性化处理,将所有的值除以2,于是2000/2=1000满足要求。
(6)overlap(重叠):图10.1定义了红方区域和蓝方区域,如果任意一个红方分域与任意一个蓝方分域相交,则称两个区域重叠。
图10.1 数据分发管理中的概念
当仿真成员加入联邦时,RTI会返回一个句柄。如图10.2所示,本项目根据该句柄的奇偶数将所有仿真成员分为两组:group0为偶数,group1为奇数。两个组内的成员互相通信,组间不能通信。在程序中使用了订购区域和发送区域,稍微修改一下代码,则可以做到向任何一个组发送消息。
图10.2两个分组
每条聊天信息应包含2个内容:聊天者昵称、聊天的一句话,这样接收者就会知道是谁在发言。将聊天信息封装为一个交互类chat_group,“聊天者昵称”用name表示,“聊天的一句话”用sentence表示,两个都是字符串类型。聊天程序使用的FED文件如表10.1所示,该文件可使用KY-OMT对象模型模板工具自动生成。
表10.1 C++分组聊天示例:chat_ddm.fed
- (FED
- (Federation fed_example)
- (FEDversion v1.3)
- (spaces
- (space group
- (dimension groupid)
- )
- )
- (interactions
- (class InteractionRoot reliable receive
- (class RTIprivate reliable receive)
- (class chat_group reliable receive group
- (parameter name)
- (parameter sentence)
- )
- )
- )
- )
|
从表10.1可以看到,“spaces”部分定义了一个名为“group”的路由空间,它有1个“groupid”维。
从字面意义上看,“spaces”是一个英语复数名词,意味着在其中可以定义多个“space”,每个“space”都是一个路由空间。这就是说,一个仿真可以定义多个路由空间。譬如对战场仿真而言,所有的飞机和雷达可以将整个三维战场空间作为它们的路由空间;而“红方部队”、“蓝方部队”则可以将“参战部队”作为它们的路由空间,其取值范围就是“红方部队”和“蓝方部队”这两个枚举值,相当于本例中的group0和group1两个聊天小组。
该程序比较简单,一个Chat.cpp和HwFederateAmbassador.cpp就可以实现。前者通过调用创建联邦执行、加入联邦执行、创建区域、设计区域的分域中的维的上下界、公布交互类、带区域订购交互类、带区域发送交互,仿真完成时退出联邦;后者用来接收RTI的回调消息,当订购区域与发送区域重叠时,会收到发送方的数据。
本项目对时间没有特别要求,不需要采用HLA时间管理机制。当RTI收到聊天信息时就立即发送给其他人,不需要调用tick服务。
Chat.cpp代码说明:
11-13行:定义交互类及其参数句柄变量;
31-40行:创建联邦执行;
42-59行:加入联邦执行;
44行:根据返回的仿真成员句柄设置分组号;
63-65行:获取交互类及其参数句柄;
67-68行:获取路由空间和维句柄;
72行:定义发送区域;
73行:定义订购区域;
75-76行:设置发送区域的extent0分域中维的上下界;
77行:通知RTI更新发送区域;
79-80行:设置订购区域的extent0分域中维的上下界;
81行:通知RTI更新订购区域;
85行:公布交互类,只有公布之后才能够向RTI发送交互;
87行:带区域订购交互类,只有订购并且区域匹配之后才能够从RTI收到其他人的聊天内容;
91-114行:循环操作,每次输入一句话,并调用sendInteraction服务发送给RTI;当用户输入“exit”时则退出执行;
104-107行:带区域发送交互;
116-121行:退出联邦执行,不再参加仿真;
123-134行:销毁联邦。如果是最后一个仿真成员执行该操作,则整个仿真结束。
表10.2 C++分组聊天示例:chat.cpp
- #include "HwFederateAmbassador.hh"
- #include <RTI.hh>
- #include <fedtime.hh>
- #include <iostream>
- using namespace std;
- /*
- 本程序有2个分组,一个人的聊天内容只能被同组中的人看到.
- 使用HLA的数据分发管理服务(DDM)来实现,分组必须为[0, 4294967295)间的整数.
- */
- RTI::InteractionClassHandle hChatClass;
- RTI::ParameterHandle hChatName;
- RTI::ParameterHandle hChatSentence;
- int mygroup = 0; //取值0或1,本程序有2个分组
- int hw_main(int argc, char *argv[]) {
- const char *federationExecutionName = "chat";
- const char *FEDfile = "chat_ddm.fed"; //本例使用chat_ddm.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); - mygroup = federateId % 2; //取值0或1,本程序有2个分组
- cout << "***I am in group " << mygroup << "***" << endl;
- } 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_group"); //对应chat_ddm.fed中的chat_group
- hChatName = rti.getParameterHandle("name", hChatClass);
- hChatSentence = rti.getParameterHandle("sentence", hChatClass);
- RTI::SpaceHandle hGroup = rti.getRoutingSpaceHandle("group"); //对应chat_ddm.fed中的group
- RTI::DimensionHandle hGroupid = rti.getDimensionHandle("groupid", hGroup);
- //-------------------------------------------------
- //本例实际上只需要一个group即可,这里定义两个group,分别用于发送和接收,
//其实二者可以使用相同的group. - RTI::Region* send_group = rti.createRegion(hGroup, 1); //只有1个extent,即extent0
- RTI::Region* receive_group = rti.createRegion(hGroup, 1); //只有1个extent,即extent0
- send_group->setRangeLowerBound(0, 0, mygroup);
//extent0的第0维为groupid,设置其下界为mygroup - send_group->setRangeUpperBound(0, 0, mygroup);
//extent0的第0维为groupid,设置其上界为mygroup - rti.notifyAboutRegionModification(*send_group); //通知RTI更改
- receive_group->setRangeLowerBound(0, 0, mygroup);
//extent0的第0维为groupid,设置其下界为mygroup - receive_group->setRangeUpperBound(0, 0, mygroup);
//extent0的第0维为groupid,设置其上界为mygroup - rti.notifyAboutRegionModification(*receive_group); //通知RTI更改
- //-------------------------------------------------
- //如果向外发送,则需要公布
- rti.publishInteractionClass(hChatClass); //没有后缀WithRegion
- //如果需要接收,则必须订购
- rti.subscribeInteractionClassWithRegion(hChatClass, *receive_group); //有后缀WithRegion
- 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 {
- //这里演示如何使用HLA中的tag,tag是一个字符串,设置为"0"或"1"
- if(mygroup == 0)
- rti.sendInteractionWithRegion(hChatClass, *pParams, "0", *send_group); //有后缀WithRegion
- else
- rti.sendInteractionWithRegion(hChatClass, *pParams, "1", *send_group); //有后缀WithRegion
- } 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-65行:处理接收到的聊天信息并输出,tag按原样输出。
表10.3 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
- if(theInteraction == hChatClass) {
- 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 << "(group " << theTag << "): " << sentence << endl;
- }
- }
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) |