QuickLink开发指南
分布式仿真互连可视化开发工具QuickLink开发指南
版权所有? 2008
海军航空工程学院飞行仿真技术研究所
目录
一、产品功能 (1)
二、安装及互连说明 (2)
三、用户使用流程 (3)
四、可视化开发界面 (3)
五、编译与链接设置 (6)
六、接口函数说明 (7)
七、应用实例 (12)
一、产品功能
QuickLink是基于HLA分布式仿真体系结构设计和开发的仿真互连引擎可视化开发工具,突出解决了仿真互连设计和开发过程中的网络驱动引擎、可视化开发工具、仿真互连管理等三个方面的关键内容,从而为大规模分布式仿真互连提供一个高效、灵活、快捷的开发工具和集成平台。
该产品主要功能及特色如下:
●方便快捷的可视化开发环境。该工具实现了分布式仿真互连引擎可视化开发环
境,并提供友好的开发界面,能够根据用户输入的仿真互连数据定义名称和类
型自动生成相应的仿真互连引擎接口,整个开发过程只需用户进行简单地操作
就可以实现,而无须编写任何代码。
●统一高效的仿真互连引擎接口。引擎接口层采用了面向对象软件设计思想,把仿
真系统中实体或交互对象的结构与行为分开,并且应用设计模式的原理提供通
用的机制解决系统中具有共性的问题,所有对象(实体、交互)的行为接口统一化
设计,即无论任何实体或交互对象,引擎都提供相同的管理与通信接口,以消
除开发人员因为HLA联邦成员接口扩展的复杂性和繁琐性而带来的不良后果。
此外,该引擎采用多线程的工作模式,在框架上保证了各个组件无论在一个联
邦成员内,还是在不同联邦成员内,都能并行工作,提高了系统实时性。
●高度灵活、可伸缩的扩展机制。通过可视化开发环境,用户可以方便地对实体或
交互类进行扩展和派生,只需要重新生成接口和编译应用程序即可完成功能的
增加和删除,而不用修改任何代码。
●丰富的数据类型及通用化接口支持。该引擎除了支持标准的C语言数据类型外,
还提供了数组、向量等复合数据类型的支持,并且引擎的接口采用了通用化数
据类型的设计,避免了不同版本C++语言编译工具的类型兼容性问题,具有较
强的通用性和适应性。
●提供分布式仿真互连管理工具。该工具负责配置和管理各仿真互连节点,帮助用
户实现分布式层次化集群服务体系的构架,以解决大规模分布式仿真互连中的
通信管理和网络负载均衡等关键问题,并且通过简单的网络互连服务配置可以
支持广域网(WLAN)上的大规模分布式仿真互连。
1
二、安装及互连说明
QuickLink包括运行版和开发版,运行版为各仿真节点提供RTI运行环境支持和仿真互连管理工具,开发版则帮助用户完成分布式仿真互连引擎接口的开发。
QuickLink开发版运行环境:
●操作系统:Windows XP或更高版本;
●编译工具:Microsoft Visual C++.net 2003(VC7.1)
QuickLink运行版运行环境:
●操作系统:Windows XP或更高版本。
QuickLink开发版输出的仿真互连引擎接口应用环境:
●操作系统:Windows XP或更高版本;
●编译工具:Microsoft Visual C++5.0或更高版本;
●RTI环境:QuickLink运行版。
在分布式仿真互连网络体系中,一般采用服务器客户端模式,即一个节点或多个节点配置为服务器,其它节点全部配置为客户端,在互连之前启动服务器节点的RTI服务程序,各节点仿真应用通过互连引擎接口就可以进行通信。下面图1为单服务器分布式仿真互连软硬件体系结构图,一般中小规模的仿真系统可以采用这种方案;而大规模分布式仿真系统或广域网互连则采用图2所示多服务器分布式层次化集群服务体系结构,以解决仿真互连中的通信管理和网络负载均衡等问题。
图1 单服务器分布式仿真互连软硬件体系结构图
2
图2 多服务器分布式层次化集群服务仿真互连软硬件体系结构图
三、用户使用流程
用户使用QuickLink工具进行分布式仿真互连开发主要包括三个步骤:
●首先使用开发版附带的OMDT工具打开已有的QuickLink.fed文件,输入需要互
连的实体和交互类名称以及相应的属性和参数名,保存后完成FOM模型的开发;
●然后执行QuickLink可视化开发工具,同样输入FOM模型定义的实体和交互类
名称以及相应的属性和参数名,并设置属性和参数的数据类型,完成后执行生成命令输出相应的仿真互连引擎接口;
●最后根据网络配置及分布情况使用仿真互连管理工具配置各仿真节点,并启动服
务器节点RTI服务程序,各节点仿真应用通过互连引擎接口就可以进行通信。
四、可视化开发界面
QuickLink开发版用户界面如下图3所示。左边树形区显示FOM模型实体和交互类信息,右面列表区显示树形区选中类的属性或参数信息,下面窗口显示类的统计信息。
工具栏和菜单执行的功能包括五个方面:
(1)项目操作
3
●新建:新建一个项目,并包括一个预定义的作战实体Entity类;
●打开:打开已存在的项目文档;
●保存:保存项目到文档。
(2)类操作
●添加:添加当前选中的实体或交互类的派生类;
●删除:删除当前选中的实体或交互类;
●修改:修改当前选中的实体或交互类的名称和基类信息;
注:不能对预定义的作战实体Entity类执行操作(派生、修改、删除),但可以添加属性来扩展Entity类。
图3 QuickLink可视化开发用户界面
(3)属性或参数操作
首先在左边树形区选中需要操作的实体或交互类,然后在右边列表区选中属性或参数点击鼠标右键弹出右键菜单执行相应的操作,如图4所示。
●添加类属性:添加属性到当前的类;
●删除类属性:删除当前选中的属性;
●修改类属性:修改当前选中的属性的名称和数据类型。
注:不能对作战实体Entity类预定义的属性修改或删除,但可以对用户添加的属性
4
5执行操作。
图3 属性或参数操作界面
(4)生成接口
执行生成命令将弹出生成互连接口对话框,选择输出路径,点击生成按钮即可。
图4 生成互连接口操作界面
(5)查看设置
显示继承:显示或隐藏类的继承属性或参数;
FOM数据树:显示或隐藏左边树形窗口;
统计信息:显示或隐藏下面统计显示窗口。
五、编译与链接设置
(1)仿真互连接口头文件
QlTool.h:引擎接口总头文件;
QlStateRep.h:实体状态基类头文件;
QlInter.h:交互信息基类头文件;
QlVector.h:向量数据类型头文件;
QlEntitySR.h:预定义作战实体Entity状态头文件;
QlMyEntitySR.h:扩展实体Entity状态头文件(用户添加自定属性后);
QlXXXSR.h:用户定义实体类状态头文件;
QlXXXInter.h:用户定义交互类信息头文件。
(2)仿真互连接口库文件
QlCore.lib、QlCore.dll:引擎内核库文件;
QlTool.lib、QlTool.dll:引擎扩展库文件。
(3)编译与链接设置
在程序中包括引擎接口总头文件QlTool.h,并在项目→属性→C/C++→常规→附加包含目录中添加所有仿真互连接口头文件所在的路径;
在项目→属性→链接→常规→附加包含目录中添加QlCore.lib 和QlTool.lib库文件所在的路径;
在项目→属性→链接→输入→附加依赖项中添加QlCore.lib 和QlTool.lib库文件;
在项目→属性→C/C++→代码生成→运行时库中选择MDd(Debug版)和MD (Release版);
拷贝QlCore.dll和QlTool.dll到仿真应用程序运行目录。
6
六、接口函数说明
QuickLink可视化开发工具生成的仿真互连引擎接口主要包括三个部分:系统控制接口、实体操作接口、交互操作接口,总共包括25个函数。在实际应用开发中,接口函数执行的位置有两个位置:一是引擎线程内部(用户注册的系统回调、实体回调、交互回调函数内),二是用户仿真应用程序内部(用户线程内)。下面在对接口的介绍过程中将对函数的执行位置以及时间顺序详细说明。
1)系统控制接口。执行仿真互连的初始化、开始、结束等功能。
⑴bool QlInit(const char* execName,const char* federateName,const char* fedFileName=0,bool absoluteTimeStamp=false,bool destroyFedExec=false);
初始化仿真互连,execName为联邦名,所有仿真互连节点联邦名必须保持一致;federateName为联邦成员名,由各仿真互连节点自己定义,可以重名;fedFileName为fed 文件名,默认为0时在程序路径寻找与联邦同名的fed文件;
如果函数初始化失败返回false,否则为true;
该函数执行成功后,将连接到RTI服务器并注册联邦成员名;
该函数仅在QlInit未执行前或QlEnd执行以后执行才有效;
该函数仅在用户线程内执行。
⑵bool QlStart(void);
开始仿真互连,此函数执行成功后,数据才开始更新;
该函数仅在QlInit成功执行以后执行才有效;
该函数仅在用户线程内执行。
⑶void QlEnd(void);
结束仿真互连,该函数执行后,数据停止更新,并删除本地创建的所有对象,最后与RTI服务器断开连接;
该函数仅在QlInit成功执行以后执行才有效;
该函数仅在用户线程内执行。
⑷void QlSetTimeStep(double timeStep);
设置仿真数据更新时间步长,单位为秒,默认为0.01秒,更新速率为100帧;
该函数仅在QlStart执行前执行才有效;
7
该函数仅在用户线程内执行。
⑸double QLSysFps(void);
获取仿真更新速率最大帧数,用于查看仿真更新运行状况,如低于预先设置数据更新速率,则应考虑降低回调函数中仿真计算量;
该函数执行时间无限制;
该函数执行位置无限制。
⑹void QlSetReflecting(void);
设置反射本地发布实体到远程实体列表,该函数执行后,远程实体列表中将包括本地发布的实体;
该函数仅在QlInit成功执行后,QlStart执行前期间执行才有效;
该函数仅在用户线程内执行。
⑺bool QlSubscribeFOMClass(const char* fomClassName);
声明远程订购实体,fomClassName为FOM模型实体类名,只有执行此函数声明某实体类后,才能接受到远程该实体类对象的信息;
该函数仅在QlInit成功执行后,QlStart执行前期间执行才有效;
该函数仅在用户线程内执行。
⑻bool QlPublishFOMClass(const char* fomClassName);
声明本地发布实体,fomClassName为FOM模型实体类名,只有执行此函数声明某实体类型后,才能创建该实体类对象;
该函数仅在QlInit成功执行后,QlStart执行前期间执行才有效;
该函数仅在用户线程内执行。
⑼void QlAddPostCallback(QlPostCallbackFuc cb,void* userData);
增加系统回调函数,用户只能在此回调函数类获取远程实体类对象的状态信息;
该函数仅在QlInit成功执行后,QlEnd执行前期间执行才有效;
该函数执行位置无限制。
⑽void QlRemovePostCallback(QlPostCallbackFuc cb,void* userData);
删除系统回调函数;
该函数仅在QlInit成功执行后,QlEnd执行前期间执行才有效。
该函数执行位置无限制。
8
⑾void QlRegSystemMessageCallback(QlMessageReportFuc outputFuc);
注册系统消息回调函数,在回调函数内可以接收到系统输出的运行信息,用于查看接口函数执行的结果。
该函数执行时间无限制;
该函数执行位置无限制。
2)实体操作接口。执行实体类对象的创建、删除、查询、事件回调注册等功能。
⑴QlStateRepository* QlCreateObject(const char* fomClassName,const char* ObjectName,const char* entityType=0,unsigned short forceID=0);
创建实体类对象,fomClassName为FOM模型实体类名,ObjectName为实体类对象名,整个联邦内所有实体类的对象名必须是唯一的;
entityType为实体类型,当FOM模型实体类名为系统预定义作战实体Entity时,实体类型为7位整形数字组成的字符串(如"1:0:32:12:6:7:1"),其代表的意义DIS协议中规定如下(kind,domain,country,category,subCategory,specific,extra),用户可以通过定义不同的实体类型来表示不同的作战实体;当FOM模型实体类名为用户自定义实体类时,该参数无效,实体类型与FOM模型实体类名相同。
forceID为兵力属性,定义为:0不明,1蓝方,2红方,3中立;
如果函数执行成功返回所创建对象的状态池指针,否则为NULL;
该函数仅在QlStart成功执行后,QlEnd执行前期间执行才有效;
该函数执行位置无限制。
⑵void QLRemoveObject(const char* fomClassName,const char* globalID);
删除实体类对象,fomClassName为FOM模型实体类名,globalID为实体类对象名;
该函数仅在QlStart成功执行后,QlEnd执行前期间执行才有效;
该函数执行位置无限制。
⑶QlStateRepository* QlFindObject(const char* fomClassName,const char* globalID,bool local=true);
查找本地或远程实体类对象状态池信息,fomClassName为FOM模型实体类名,globalID为实体类对象名,状态池基类指针需要显示转换为用户定义实体类状态池指针QlfomClassNameSR*后才能获得对象状态信息;
local为true时查找本地发布的实体类对象状态信息,函数执行位置无限制;
9
local为false时查找远程实体类对象状态信息,只能在引擎线程系统回调函数内执行;
如果函数执行成功返回对象的状态池指针,否则为NULL;
该函数仅在QlStart成功执行后,QlEnd执行前期间执行才有效。
⑷QlStateRepository* QlFirstObject(const char* fomClassName,bool local=true);
查找本地或远程实体类对象列表中第一个对象状态池信息,fomClassName为FOM 模型实体类名,状态池基类指针需要显示转换为用户定义实体类状态池指针QlfomClassNameSR*后才能获得对象状态信息;
local为true时查找本地发布的实体类对象信息,函数执行位置无限制;
local为false时查找远程实体类对象信息,只能在引擎线程系统回调函数内执行;
如果函数执行成功返回对象的状态池指针,否则为NULL;
该函数仅在QlStart成功执行后,QlEnd执行前期间执行才有效;
⑸QlStateRepository* QlNextObject(const char* fomClassName,QlStateRepository* sr,bool local=true);
查找本地或远程实体类对象列表中某对象的下一个对象状态池信息,fomClassName 为FOM实体类名,sr为该对象的状态池指针,状态池基类指针需要显示转换为用户定义实体类状态池指针QlfomClassNameSR*后才能获得对象状态信息;
local为true时查找本地发布的实体类对象信息,函数执行位置无限制;
local为false时查找远程实体类对象信息,只能在引擎线程系统回调函数内执行;
如果函数执行成功返回对象的状态池指针,否则为NULL;
该函数仅在QlStart成功执行后,QlEnd执行前期间执行才有效。
⑹bool QlIsObject(const char* fomClassName,const char* globalID,bool local=true);
判断本地或远程实体类对象是否存在,fomClassName为FOM实体类名,globalID 为实体类对象名,
local为true时为本地发布的实体类对象,函数执行位置无限制;
local为false时为远程实体类对象,函数执行位置无限制;
该函数仅在QlStart成功执行后,QlEnd执行前期间执行才有效;
⑺int QlObjectNum(const char* fomClassName,bool local=true);
获取本地或远程实体类对象数目,fomClassName为FOM模型实体类名,globalID
10
为实体类对象名;
local为true时获取本地发布的实体类对象数目,函数执行位置无限制;
local为false时获取远程反射的实体类对象数目,函数执行位置无限制;
该函数仅在QlStart成功执行后,QlEnd执行前期间执行才有效;
⑻void QlAddObjectAdditionCallback(const char*fomClassName, QlObjectCallBackFuc cb);
注册远程实体类对象生成事件回调函数,fomClassName为FOM模型实体类名;
该函数仅在QlInit成功执行后,QlEnd执行前期间执行才有效;
该函数执行前必须先执行QlSubscribeFOMClass订购该FOM模型实体类;
该函数执行位置无限制。
⑼void QlRemoveObjectAdditionCallback( const char* fomClassName, QlObjectCallBackFuc cb);
删除远程实体类对象生成事件回调函数,fomClassName为FOM模型实体类名;
该函数仅在QlInit成功执行后,QlEnd执行前期间执行才有效;
该函数执行前必须先执行QlSubscribeFOMClass订购该FOM模型实体类;
该函数执行位置无限制。
⑽void QlAddObjectRemovalCallback( const char* fomClassName, QlObjectCallBackFuc cb);
注册远程实体类对象删除事件回调函数,fomClassName为FOM模型实体类名;
该函数仅在QlInit成功执行后,QlEnd执行前期间执行才有效;
该函数执行前必须先执行QlSubscribeFOMClass订购该FOM模型实体类;
该函数执行位置无限制。
⑾void QlRemoveObjectRemovalCallback( const char* fomClassName, QlObjectCallBackFuc cb);
删除远程实体类对象删除事件回调函数,fomClassName为FOM模型实体类名;
该函数仅在QlInit成功执行后,QlEnd执行前期间执行才有效;
该函数执行前必须先执行QlSubscribeFOMClass订购该FOM模型实体类;
该函数执行位置无限制。
3)交互操作接口。执行交互类对象信息的发送、接受等功能。
11
⑴void QlSendInteraction(const QlInteraction* msg);
发送交互类对象信息,msg为交互类对象指针,发送前创建交互类对象时,交互类对象构造函数参数必须为true,如QlfomClassNameInter msg(true);
该函数仅在QlStart成功执行后,QlEnd执行前期间执行才有效;
该函数不能在引擎线程交互回调函数内执行。
⑵void QlAddInteractionCallback(const char*fomClassName,QlInteractionCallbackFuc cb);
注册交互类对象信息接收回调函数,fomClassName为FOM模型交互类名,在回调函数内交互基类对象指针需要显示转换为用户定义的交互类对象指针QlfomClassNameInter* 后才能获得对象信息;
该函数仅在QlInit成功执行后,QlEnd执行前期间执行才有效;
该函数执行位置无限制。
⑶void QlRemoveInteractionCallback( const char* fomClassName, QlInteractionCallbackFuc cb);
删除交互类对象信息接收回调函数,fomClassName为FOM模型交互类名;
该函数仅在QlInit成功执行后,QlEnd执行前期间执行才有效;
该函数执行位置无限制。
七、应用实例
下面以一个简单Sample项目为例详细说明如何进行仿真互连的开发与应用,所有文档及代码保存在该工具Samples文件夹下。
首先使用开发版附带的OMDT工具打开已有的QuickLink.fed文件,添加实体类;
①对实体类Entity扩展,在BaseEntity类下添加两个属性(必须在BaseEntity类下):
Mass
TestVector
②在实体类列表根部添加一个新类Device,并添加一个属性:
Name
③添加一个从Device派生的RadioJam类,并增加三个属性:
JamFrequency
12
JamMode
TestVector
添加交互类
①在交互类列表根部添加一个新类Command,并添加两个参数:
Host
Code
②添加一个从Command派生的Environment类,并增加两个参数:
Temperature
TestVector
添加完成后保存为Sample.fed,即完成该项目FOM模型的开发。
然后执行QuickLink开发版,重复上面的步骤,并设置属性和参数的数据类型,保存为Sample.proj,执行生成命令输出互连接口即完成该项目仿真互连引擎的开发。
最后介绍如何利用输出的互连接口进行仿真互连应用开发。
①发布实体信息(SendEntity工程)
//初始化连接
QlInit("QuickLink","SendEntity","Sample.fed");
//设置仿真时钟(秒)
QlSetTimeStep(0.01);
//声明发布实体
QlPublishFOMClass("Entity");
//开始
QlStart();
//创建实体类型为"1:0:32:12:6:7:1"的作战实体,名为F16
QlMyEntityStateRep* pEntity=(QlMyEntityStateRep*) QlCreateObject("Entity","F16",
"1:0:32:12:6:7:1");
if(pEntity!=0)
{
pEntity->setMass(12345.6789);
pEntity->setTestVector(QlVector64(123,456,789));
}
//等待输入结束
char wait;
scanf("%c",&wait);
QlEnd();
从上面代码中可以看出,创建本地实体对象前必须声明发布实体;此外创建本地实体
13
对象后,可以保存返回的状态池指针,也可以不断调用QlFindObject获取本地发布对象的状态池指针进行数据更新,返回的指针在用户调用QLRemoveObject删除该对象前保持有效。因此对本地发布对象操作的QlCreateObject、QlFindObject、QLRemoveObject、QlFirstObject、QlNextObject五个函数本身是多线程安全的,即用户可以在不同的线程内执行这几个函数,但函数执行操作之间的逻辑关系需要用户来维护。
②接受实体信息(RecvEntity工程)
QlInit("QuickLink","ReceiveEntity","Sample.fed");
//设置仿真时钟(秒)
QlSetTimeStep(0.01);
//订购实体声明
QlSubscribeFOMClass("Entity");
//注册获取实体数据回调函数
QlAddPostCallback(GetEntityData,NULL);
//注册远程实体生成事件回调函数
QlAddObjectAdditionCallback("Entity",GetEntityInfo);
//注册远程实体删除事件回调函数
QlAddObjectRemovalCallback("Entity",GetEntityInfo);
//开始
QlStart();
//等待输入结束
char wait;
scanf("%c",&wait);
QlEnd();
void GetEntityData(void* data)
{
QlMyEntityStateRep*pEntity=(QlMyEntityStateRep*)QlFindObject("Entity",
"F16",false);
//获取远程订购对象数据为false,获取本地发布对象为true
if(pEntity!=NULL)
{
printf("%f %f\n",pEntity->getMass());
printf("%f %f %f\n",pEntity->getTestVector().x(),
pEntity->getTestVector().y(),pEntity->getTestVector().z());
}
}
//获取远程实体生成或删除时信息
void GetEntityInfo(const char* entityType,const char* globalID,const char* localID)
{
printf("%s %s %s\n",entityType,globalID,localID);
}
14
对于远程订购实体对象状态信息的获取必须在QlAddPostCallback函数注册的系统回调函数内完成,这是由于对远程订购对象操作的QlCreateObject、QlFindObject、QLRemoveObject、QlFirstObject、QlNextObject五个函数并不是多线程安全的,即用户只能在系统回调函数内执行这几个函数。
③发送交互信息(SendInter工程)
//初始化连接
QlInit("QuickLink","SendInter","Sample.fed");
//设置仿真时钟(秒)
QlSetTimeStep(0.01);
//开始
QlStart();
//
QlEnvironmentInter msg(true);//注意必须为true,默认为false
msg.setHost(1);
msg.setCode(2);
msg.setTemperature(3);
msg.setTestVector(QlVector64(4,5,6));
QlSendInteraction(&msg);
//等待输入结束
char wait;
scanf("%c",&wait);
QlEnd();
交互信息的发送相对比较简单,只需要注意创建交互类对象时,交互类对象构造函数参数必须为true。
④接受交互信息(RecvInter工程)
//初始化连接
QlInit("QuickLink","RecvInter","Sample.fed");
//设置仿真时钟(秒)
QlSetTimeStep(0.01);
//注册交互对象信息接受回调函数
QlAddInteractionCallback("Environment",GetInterInfo);
//开始
QlStart();
//等待输入结束
char wait;
scanf("%c",&wait);
QlEnd();
void GetInterInfo(const QlInteraction* inter)
{
15
QlEnvironmentInter* msg=(QlEnvironmentInter*)inter;
printf("%d %d %f\n",msg->getHost(),msg->getCode(),msg->getTemperature());
printf("%f %f %f\n",msg->getTestVector().x(),msg->getTestVector().y(),
msg->getTestVector().z());
}
接受交互信息的也比较简单,需要注意在回调函数内,交互基类对象指针需要显示转换为用户定义的交互类对象指针QlfomClassNameInter* 后才能获得对象信息。
16