QualNet外部接口调试
QualNet-6.1-Programmer'sGuide文档中第6章节讲述了外部接口的使用。在根据教程说明测试时,出现很多问题,为更好地了解外部接口的工作过程及使用方法,特意对相关源码进行了研究。现将研究成果及问题排查过程整理如下。
一、外部接口API
外部接口API允许QualNet与其他程序或物理设备等外部实体交互。图1说明了接口开发人员的职责以及外部接口API如何与QualNet一起运行。
外部实体存在于QualNet模拟之外,可以是运行在同一台计算机上的程序,或在不同的计算机、硬件设备或完全不同的东西上运行的程序。每种类型的外部实体以不同的方式与QualNet交互,接口开发人员根据外部实体来定制接口。
外部接口负责执行与外部实体的通信任务,并使用外部接口API与QualNet仿真交互。该API主要分为两个部分:接口注册功能和实用功能。接口注册功能允许接口开发人员创建接口并定义其操作方式。实用功能通过自动化常规任务简化接口开发。
二、示例
文档以一个示例说明了为QualNet开发外部接口的过程。示例包括以下三个部分:
·外部实体:tutorialtester.cpp。
·QualNet应用:interfacetutorial_app.cpp,interfacetutorial_app.h。
·外部接口:interfacetutorial.cpp,interfacetutorial.h。
(一)外部实体
示例中,外部实体是一个名为TUTORIALTESTER的简单交互程序,源代码为~/scenarios/interfacetutorial/tutorialtester.cpp。该程序接受以下格式的用户输入:
<source node id> <destination node id> <command>
其中<command>为命令,可以是set命令,也可以是get命令。TUTORIALTESTER接受用户的命令,通过外部接口与QualNet应用程序交互。应用程序的每个节点都维护一个变量,set命令通过为该变量分配一个新值来对该变量进行操作,get命令检索变量的当前值并将其发送回TUTORIALTESTER程序。
(二)QualNet应用
源码位于~/interfaces/interfacetutorial/src目录下,主要有以下函数:
1.AppInterfaceTutorialGet:由消息处理函数调用,搜索节点上运行的应用列表,查找INTERFACETUTORIAL应用,以获取应用的数据结构。
2.AppInterfaceTutorialNew:由初始化函数调用,分配一个新的INTERFACETUTORIAL应用数据结构。
3.AppLayerInterfaceTutorial:消息处理函数,是应用的核心,分析每个接收到的命令。对于set命令,应用程序的变量将更新为命令中指定的值。对于get命令,将应用程序变量的值转发回外部接口,由接口将其发送到外部实体。
4.AppInterfaceTutorialInit:初始化函数,调用AppInterfaceTutorialNew分配一个新的INTERFACETUTORIAL应用数据结构。
5.AppInterfaceTutorialFinalize:终结函数,通常用于输出统计信息,此处没有要执行的任务。
(三)外部接口
源码位于~/interface/interfacetutorial/src/目录下。主要有以下函数:
1.InterfaceTutorialInitializeNodes:此函数初始化外部接口。它是在所有节点和协议创建之后调用的。它创建了一个特定于接口的数据结构,并为监听器打开TCP端口5132进行监听。外部实体连接到这个TCP端口并发送命令。
2.InterfaceTutorialReceive:此函数从套接字接收用户输入并解析,确定源节点、目标节点和命令。通过调用EXTERNAL_SendDataAppLayerUDP函数从源节点向目标节点发送命令。
3.InterfaceTutorialForward:通过TCP套接字将信息发送回TUTORIALTESTER程序。当目标节点接收到“get”命令时调用此函数。
4.InterfaceTutorialFinalize:在仿真结束时调用此函数,并关闭打开的套接字连接。
三、示例演示及问题排查过程
(一)文档中标明的示例使用方法
1.使用QualNet自带的命令行工具QualNet 6.1 Command-Line:
cl tutorialtester.cpp /link ws2_32.lib,生成tutorialtester.exe。
2.取消注释文件~/main/Makefile-addons-windows中的行:
include ../interfaces/interfacetutorial/Makefile-windows
使用vs2008重新编译生成qualnet.exe(此时应用层interfacetutorial协议和外部接口启用)
3.从~/scenarios/interfacetutorial中复制文件tutorial.config、tutorial.nodes和tutorial.app到目录~/bin,并使用tutorial.config作为配置文件运行QualNet。
(二)实际操作报错及问题排除
前两步执行没有问题,第三步用GUI方式打开QualNet运行tutorial.config时,报错①“INTERFACETUTORIAL 应用不支持”,强行运行,报错②“InterfaceTutorial接口不存在”。此时点击保存,会发现tutorial.app文件自动消失,且tutorial.config被大幅改变。此时运行不会报错,用windows自带的命令行打开tutorialtester.exe时显示正在连接QualNet且允许输入命令,但命令执行没有实现设想效果。
分析原因:
1.tutorial.app文件消失,tutorial.config被改变,运行不报错,可能是该场景不再运行interfacetutorial协议,没有任何应用协议,这对排除问题没有意义。删除相关的配置文件,重新拷贝备份配置(一开始没有备份,只能卸载重装QualNet,一定要备份!)。
2.报错①应是协议没有在GUI中加载。修改~/gui/settings/Toolsets/Standard.xml,在"Applications"类别下添加一行以加载新协议,并在~/gui/settings/components下新建interfacetutorial.cmp。根据~/main/application.cpp中对该协议的输入文件读取函数:
sscanf(appInput.inputStrings[i], "%*s %s %s",nodeString,dataString);
可知协议输入两个参数,一个为节点id号,一个为文本。在interfacetutorial.cmp文件中按格式编写好两个参数(参照文档第5章节)。
重新运行tutorial.config,①不再报错,②依然报错。且在GUI中不论为协议第二个参数配置什么文本,保存后.app文件中要么没有第二个参数,协议参数格式报错,要么显示为127.0.0.1。可能是底层有什么冲突。
3.对于报错②,可能是外部接口没有开启或加载。修改场景属性,打开External Interfaces,其中有HLA、DIS、AGI和Socket Interface,本示例程序需要用到socket,打开socket接口并在端口数中配置端口号为5132(tutorialtester.cpp的main函数中明确使用5132端口)。
重新运行,tutorialtester.exe显示正在连接QualNet且允许输入命令,QualNet显示MTS接口通过5132端口连接到QualNet,tutorialtester.exe输入的命令没有效果。注意,是MTS接口连接到QualNet。查看socket-interface代码发现该接口专为MTS设计,与InterfaceTutorial接口没有关系。
4.将对GUI的修改恢复原样,重新拷贝备份配置,用命令行形式打开tutorial.config,①直接不报错,②依然报错。可见报错①是由GUI与.app文件冲突带来的,在命令行形式下不会有冲突。现在问题的关键是去哪开启InterfaceTutorial接口。
5.继续学习文档第6章节的内容,得知所有外部接口都需由external.cpp中的EXTERNAL_UserFunctionRegistration函数注册并注册实用函数。查看代码,从第1523行开始,是注册InterfaceTutorial接口的代码,没有注册的可能原因:一是“#ifdef”语句,TUTORIAL_INTERFACE未定义;将该行注释,并将第53行注释,依然报错。二是if语句未执行,添加else输出语句,重新编译,报错并执行了else输出语句,即if语句确实未执行,没有注册接口。
将if语句改为if(1),必然执行,重新编译后不报错,QualNet自动启用了socket接口,且在5132端口监听,应该正常了。但执行tutorialtester.exe时,却显示socket连接失败。
if语句中EXTERNAL_ConfigStringPresent(nodeInput, "INTERFACE-TUTORIAL"),是查找输入文件中是否由指定文本"INTERFACE-TUTORIAL",输入文件有很多,.app,.config都有可能。研究了application.cpp中对输入文件的读取,可知最原始的输入文件是.config,然后在这个文件中可以通过函数定位相关文本再读取其他如.app的输入文件。根据代码分析,此处if语句读取的应是.config文件。另外,在最初的tutorial.app文件中有INTERFACETUTORIAL,将if语句中的文本修改去掉中间下划线,重新编译运行依然错误,证实了if语句中读取的不是.app输入文件。
6.据以上推断,将external.cpp中的if语句恢复,然后重新复制原始tutorial.config配置文件并添加“INTERFACE-TUTORIAL”字样,报错说该参数没有值,随便添加了一个“YES”值,执行到达“Listening for socket connection on port 5132...”。运行tutorialtester.exe成功!!!
tutorial.config配置中是没有打开socket接口的,但是interfacetutorial接口在注册时就自动使用了socket接口,且端口是5132,与配置中是否开启socket无关(如前所述,外部接口中的socket接口专为MTS设计)。
(三)其他注意事项
此次问题排查用时一天一夜,走了很多弯路。感触最大的两点:一是一定要看懂源码;二是最好用命令行形式操作。此外还有些注意事项:
1.文档标明的步骤第三步没什么必要,可以直接在原路径下打开,只是在命令行中要多输入几行命令以到达配置文件所在路径。
2.QualNet的GUI形式在运行出错后,可能会关闭不正常,会出现qualnet.exe一直在使用的情况,导致vs2008清理解决方案时不彻底,生成解决方案时总是报错“qualnet.exe打开失败”。需手动在任务管理器——》详细信息中关闭qualnet.exe。
3.在其他场景,只要打开socket接口并设定好端口号5132后,启用tutorialtester.exe就能连接上QualNet仿真器,但QualNet内部没有启用外部接口和对应的应用层协议,因此输入的命令没有任何效果。