0 简介
RTI®代码生成器Code Generator,创建用RTI Connext®DDS定义define和注册register用户数据类型所需的代码。
在以下情况下,使用代码生成器是可选的:
您正在使用动态类型(请参阅RTI Connext DDS核心库用户手册[RTI ConnextDDS Core Libraries Users Manual]中的“管理内置类型的内存”(第3.2.7节))。
您正在使用一种内置类型(请参阅RTI Connext DDS核心库用户手册[RTI ConnextDDS Core Libraries Users Manual]中的内置数据类型(第3.2节))。
要使用代码生成器Code Generator,您需要在IDL或XML文件中提供数据类型的描述。可以在同一类型定义文件中定义多个数据类型。有关这些文件的详细信息,请参阅RTIConnext DDS核心库用户手册[RTI Connext DDS CoreLibraries Users Manual](第3.3节和第3.4节)。
1 常用路径
1.1 <NDDSHOME>
这是指RTI®Connext®DDS的安装目录。默认安装路径为:
macOSsystems:
/Applications/rti_connext_dds-6.1.1
Linuxsystems, non-root user:
/home/<youruser name>/rti_connext_dds-6.1.1
Linux systems,root user:
/opt/rti_connext_dds-6.1.1
Windowssystems, user without Administrator privileges:
<yourhome directory>\rti_connext_dds-6.1.1
Windowssystems, user with Administrator privileges:
C:\ProgramFiles\rti_connext_dds-6.1.1
您还可以看到$NDDSHOME或%NDDSHOME%,它是指设置到安装路径的环境变量。
无论您在路径中看到<NDDSHOME>,请将其替换为安装路径。
Windows用户注意:使用命令提示符输入包含路径C:\Program Files(或任何有空格的目录名)的命令时,请将路径括在引号中。例如:
“C:\ProgramFiles\rti_connext_dds6.1.1\bin\rtiddsgen”
或者如果已定义NDDSHOME环境变量:
“%NDDSHOME%\bin\rtiddsgen”
1.2 <pathto examples>
默认情况下,当您第一次运行RTI Launcher或<NDDSHOME>/bin中的任何脚本时,会将示例复制到主目录中。本文档将复制示例的位置称为
<path to examples >。
无论您在哪里看到<path toexamples>,请将其替换为适当的路径。
示例的默认路径:
macOS系统:/Users/<用户名>/rti_workspace/6.1.1/examples
Linux系统:/home/<用户名>/rti_workspace/6.1.1/example
Windows系统:<您的Windows文档文件夹>\rti_workspace\6.1.1\examples其中“您的Windows文件文件夹”取决于您的Windows版本。例如,在Windows 10上,文件夹为C:\Users\<用户名>\Documents。
注意:您可以为rti_workspace指定不同的位置。还可以指定不希望将示例复制到工作空间。有关详细信息,请参阅《RTI Connext DDS安装指南》中的“控制RTI工作区位置和复制示例”。
2 rtiddsgen的命令行参数
在Windows系统上:在运行rtiddsgen之前,请在运行rtiddsgen所使用的同一命令提示符中运行适用于您的体系结构的“vcvars”批处理文件。例如:
vcvarsall.batx86。
“vcvars”批处理文件的位置因您的Visual Studio版本而异。有关安装的确切位置和参数,请参阅MicrosoftVisual Studio文档。或者,从Visual Studio Tools文件夹下的Visual Studio命令提示符运行rtiddsgen。或者,使用命令行参数ppDisable。请参见第6页表3.1rtiddsgen选项中的-ppDisable。
如果要为Connext DDS生成代码,则选项如下:
rtiddsgen [-help] [-allocateWithMalloc] [-alwaysUseStdVector] [-autoGenFiles <architecture>] [-constructor] [-create <typefiles| examplefiles|makefiles>] [-convertToIdl | -convertToXML | -convertToXsd] [-D <name>[=<value>]] [-d <outdir>] [-disableXSDValidation] [-dllExportMacroSuffix <suffix>] [-dotnet <modern|legacy>] [-enableEscapeChar] [-example <architecture>] [-exampleTemplate] [-I <directory>] [[-inputIdl] <IDLInputFile.idl> | [-inputXml] <XMLInputFile.xml> |[-inputXsd <IDLInputFile.idl>]] [-language <Ada|C|C++|C++11|C++/CLI|C#|Java>] [-namespace] [-obfuscate] [-optimization <level>] [-package <packagePrefix>] [-platform <architecture>] [-ppDisable] [-ppPath <path to preprocessor>] [-ppOption <option>] [-qualifiedEnumerator] [-reader] [-replace] [-sequenceSize <unbounded sequences size>] [-sharedLib] [-showTemplates] [-strict] [-stringSize <unbounded strings size>] [-typeSequenceSuffix <suffix>] [-U <name>] [-unboundedSupport] [-update <typefiles| examplefiles|makefiles>] [-use52Keyhash] [-use526Keyhash] [-useStdString] [-V <name< [=<value>]] [-verbosity [1-3]] [-version] [-virtualDestructor] [-writer] |
如果您正在为RTI Connext DDSMicro生成代码,则选项如下:
rtiddsgen [-help] [-create <typefiles| examplefiles|makefiles>] [-convertToIdl | -convertToXML] [-D <name>[=<value>]] [-d <outdir>] [-enableEscapeChar] [-example] [-exampleTemplate] [-I <directory>] [[-inputIdl] <IDLInputFile.idl> | [-inputXml] <XMLInputFile.xml>] [-language <C|C++>] [-micro] [-namespace] [-ppDisable] [-ppPath <path to preprocessor>] [-ppOption <option>] [-reader] [-replace] [-showTemplates] [-sequenceSize <unbounded sequences size>] [-strict] [-stringSize <unbounded strings size>] [-U <name>] [-update <typefiles| examplefiles|makefiles>] [-V <name< [=<value>]] [-verbosity [1-3]] [-version] [-writer] |
注意:在使用代码生成器创建的makefile来编译应用程序之前,请确保${NDDSHOME}环境变量的设置如中的设置环境变量(artisetenv)所述RTI Connext DDS入门指南中(RTI Connext DDS GettingStarted Guide.)发布/订阅简介的“动手1”。
表3.1 rtiddsgen选项
选项 | 描述 |
-allocateWithMalloc | 在C++中使用DDS_Heap_malloc分配可选成员时,使用此标志可获得向后兼容性。 |
-alwaysUseStdVector | 仅适用于指定了-语言C++11的情况。 生成将所有序列映射到std::vector的代码,甚至将映射到rti::core::bounded_sequence的有界序列。 或者,@use_vector注释可以应用于每个序列成员。 |
-autoGenFiles <architecture> | 更新自动生成的文件,即typefile和makefile/项目文件。 要查看每种语言的<体系结构>有效选项,请使用-help选项运行rtiddsgen,或使用字符串 “通用”(-autoGenFiles通用)为所有支持的平台生成兼容的发布者/订户代码。universalarchitecture不会生成makefile/项目文件。 这是以下项的快捷方式: -更新typefiles-更新makefiles-平台<体系结构> |
-constructor | 仅当同时指定了-语言C++时适用。 生成类型默认构造函数、复制构造函数、复制赋值运算符和析构函数。使用此选项还将禁用以下TypeSupport方法的生成:create_data(_ex)、delete_ data(_ex)、initialize_data(_e x)、finalize_data。 |
-create <typefiles| examplefiles|makefiles> | 创建指定的文件(typefile、examplefile或makefile)(如果它们不存在)。例如: rtiddsgen-语言C++11-创建类型文件test.idl如果文件已经存在,则不会修改文件并打印警告。 可以有多个创建选项。 如果为同一文件类型同时指定-create和-update,则只应用-update。 如果使用-create makefile,则需要-platform<arch>选项。例如: rtiddsgen-语言c-创建makefile-平台x64Darwin17clang9.0 test.idl |
-convertToIdl | 将输入类型描述文件转换为IDL格式。此选项将创建一个与输入文件同名且扩展名为.idl的新文件。 |
-convertToXML | 将输入类型描述文件转换为XML格式。此选项创建一个与输入文件同名且扩展名为.xml的新文件。 |
-convertToXsd | 将输入类型描述文件转换为XSD格式。此选项将创建一个与输入文件同名且扩展名为.xsd的新文件。 |
-D <name>[=<value>] | 定义预处理器宏。 在Windows系统上,将参数括在引号中: -D“<name>[=<value>]” |
-d <outdir> | 在指定目录中生成输出。默认情况下,代码生成器将在找到输入类型定义文件的目录中生成文件。 |
-disableXSDValidation | 导致代码生成器不检查输入XSD文件的格式是否正确。 通常不建议使用此选项,因为代码生成器可能会收到无效输入。 |
-dllExportMacroSuffix <suffix> | 定义在生成Windows DLL时用于导出符号的宏的后缀。默认宏为NDDS_USER_DLL_EXPORT。指定此选项时,宏的名称为NDDS_USER_DLL_ EXPORT_<后缀>。 |
3 生成示例代码
大多数Connext DDS入门指南(如RTI Connext DDS《入门指南》或RTI Connxt DDS核心库《嵌入式系统入门指南附录》)都要求您创建一个简单的“HelloWorld”应用程序来入门。例如,在macOS上:
$rtiddsgen -language c++11 -example x64Darwin17clang9.0 hello_world.idl |
注意:在使用代码生成器Code Generator创建的makefile来编译应用程序之前,请确保按照RTI Connext DDS入门指南中发布/订阅简介的“Hands On 1”中的设置环境变量(artisetenv)中所述设置了${NDDSHOME}环境变量。
熟悉简单的HelloWorld示例后,可以通过指定“高级”模板选项来创建更高级的示例。高级模板已包含在代码生成器中(请参见-exampleTemplate和-showTemplates)。例如,在macOS上:
rtiddsgen -language C+11 -example x64Darwin17clang9.0 -exampleTemplate advanced hello_ world.idl |
代码生成器生成的简单示例和高级示例之间有一些主要区别。
注意:Android不支持高级生成的示例™ 或INTEGRITY®平台。
3.1 is_default_qos(true vs. false)
ConnextDDS从当前工作目录中名为USER_QOS_PROFILES.xml的文件加载QoS配置文件。(Connext DDS也可以在其他位置查找此文件;请参阅RTI Connext DDS核心库用户手册中的“如何加载XML指定的QoS设置”)。
简单示例在USER_QOS_PROFILES.xml文件中设置is_default_qos=true。它创建DDS实体而不指定配置文件,因此使用USER_QOS_PROFILES.xml中的默认值。
高级示例还从USER_QoS_PROFILES.xml文件加载QoS。但是,高级示例将is_default_qos设置为false,因此XML没有提供默认值。该示例明确指定了在创建DDS实体时要从XML文件中使用的QoS配置文件。
设置is_default_qos=true是快速入门的一种方便方法,但在生产应用程序中,您应该明确指定要使用的qos配置文件,而不是依赖默认值。请参阅RTI Connext DDS入门指南中的基本QoS一章。
3.2 侦听器Listeners与等待集WaitSets
简单和高级示例都使用WaitSets来阻塞线程,直到数据可用。这是获取数据的最安全的方法,因为它不会影响任何中间件线程(它会阻塞应用程序的主线程,直到数据可用)。
此外,该高级示例在DataReader和DataWriter上安装侦听器,并提供回调,您可以实现这些回调以实现所需的行为。这些侦听器回调是针对各种事件触发的,例如发现匹配的DataWriter或DataReader。侦听器回调是从中间件线程回调的,您不应该阻止它。使用监听器进行非数据回调有好处,因为您不会错过事件。但是,如果在侦听器中阻止或执行缓慢的处理,则可能会导致不希望的行为,例如数据丢失。请参阅RTI Connext DDS核心库用户手册中的侦听器一章。
4 产生文件
下表显示了代码生成器为名为Hello.IDL的示例IDL文件创建的文件。
表5.1 C、传统C++、C++/CLI、为示例“Hello.idl”创建的旧C#文件
表5.2为示例“Hello.idl”创建的现代C++文件
表5.3为示例“Hello.idl”创建的C#文件
表5.4为示例“Hello.idl”创建的Java文件
表5.5为示例“Hello.idl”创建的Ada文件
表5.1 C、传统C++、C++/CLI、为示例创建的旧C#文件“Hello.idl”
产生文件 | 描述 | |
用户数据类型需要以下文件。源文件应编译并与应用程序链接。需要头文件才能在源中使用数据类型。 除非您打算自定义生成的支持您类型的代码,否则不应修改这些文件。 | ||
Hello.[c, cxx, cpp] HelloSupport.[c, cxx, cpp] HelloPlugin.[c, cxx, cpp] | 为数据类型生成的代码。这些文件包含数据类型的实现。 | |
Hello.h HelloSupport.h HelloPlugin.h | 包含数据类型实现中使用的声明的头文件。 | |
使用-example<architecture>命令行选项时会生成以下可选文件。您可以修改并使用这些文件作为创建发布或订阅用户数据类型的简单应用程序的方法。 | ||
application.h (traditional C++ only) | Hello_publisher.cxxand Hello_subscriber.cxxapplications的帮助程序代码 | |
Hello_publisher.[c, cxx, cpp, cs] | 发布用户数据类型的应用程序的示例代码。此示例显示了创建发送数据所需的所有DDS对象的基本步骤。 您需要修改代码以设置和更改数据结构中发送的值。否则,只需编译并运行。 | |
Hello_subscriber.[c, cxx, cpp,cs] | 订阅用户数据类型的应用程序的示例代码。此示例显示了创建接收数据所需的所有DDS对象的基本步骤。 不需要修改此文件。它已准备好供您编译和运行。 | |
Hello-<architecture>.sln Hello_publisher-<architecture>.v[c, cs, cx]proj Hello_subscriber-<architecture>.v[c, cs, cx]-proj | Microsoft Visual Studio解决方案和项目文件,仅为基于Visual Studio的体系结构生成。(示例<architecture>isarmv8Linux4gcc7.3.0。)要编译生成的源代码,请打开工作区文件并构建两个项目。 | |
makefile_Hello_<architecture> | 非基于Visual Studio的架构的Makefile。一个<体系结构>的例子是rmv8Linux4gcc7.3.0。 | |
USER_QOS_PROFILES.xml | 生成的示例中DDS实体的服务质量(QoS)配置从该文件加载。 | |
表5.2为示例“Hello.idl”创建的现代C++文件
产生文件 | 描述 |
用户数据类型需要以下文件。源文件应编译并与应用程序链接。需要头文件才能在源中使用数据类型。 除非您打算自定义生成的支持您类型的代码,否则不应修改这些文件。 | |
Hello.cxx HelloPlugin.cxx | 为数据类型生成的代码。这些文件包含数据类型的实现。 |
Hello.hpp HelloPlugin.hpp | 包含数据类型实现中使用的声明的头文件 |
使用-example<architecture>命令行选项时会生成以下可选文件。您可以修改并使用这些文件作为创建发布或订阅用户数据类型的简单应用程序的方法。 | |
application.hpp | Hello_publisher.cxxand Hello_subscriber.cxxapplications的帮助程序代码 |
Hello_publisher.cxx | 发布用户数据类型的应用程序的示例代码。此示例显示了创建发送数据所需的所有DDS对象的基本步骤。 您需要修改代码以设置和更改数据结构中发送的值。否则,只需编译并运行。 |
Hello_subscriber.cxx | 订阅用户数据类型的应用程序的示例代码。此示例显示了创建接收数据所需的所有DDS对象的基本步骤。 不需要修改此文件。它已准备好供您编译和运行。 |
Hello-<architecture>.sln Hello_publisher-<architecture>.vcxproj Hello_subscriber-<architecture>.vcxproj | Microsoft Visual Studio解决方案和项目文件,仅为基于Visual Studio的体系结构生成。(示例<architecture>isarmv8Linux4gcc7.3.0。)要编译生成的源代码,请打开工作区文件并构建两个项目。 |
makefile_Hello_<architecture> | 非基于Visual Studio的架构的Makefile。一个<体系结构>的例子是rmv8Linux4gcc7.3.0。 |
USER_QOS_PROFILES.xml | 生成的示例中DDS实体的服务质量(QoS)配置从该文件加载。 |
表5.3为示例“Hello.idl”创建的C#文件
产生文件 | 描述 |
用户数据类型需要以下文件。源文件应与.NET项目一起编译。 您不应修改这些文件。 | |
Hello.cs HelloPlugin.cs | 为数据类型生成的代码。这些文件包含数据类型的实现。 |
使用-example<platform>命令行选项(其中<platform> 是.NET平台标识符,如net5或netcoreapp3.1)。您可以修改并使用这些文件来创建发布或订阅用户数据类型的简单应用程序。 | |
HelloProgram.cs | 运行HelloPublisher.cs和HelloSubscriber.cs应用程序的代码 |
HelloPublisher.cs | 发布用户数据类型的应用程序的示例代码。此示例显示了创建发送数据所需的所有DDS对象的基本步骤。 您需要修改代码以设置和更改数据结构中发送的值。否则,只需编译并运行。 |
HelloSubscriber.cs | 订阅用户数据类型的应用程序的示例代码。 此示例显示了创建接收数据所需的所有DDS对象的基本步骤。 不需要修改此文件。它已准备好供您编译和运行。 |
Hello.csproj NuGet.Config | 用于构建和运行应用程序。NuGet.Config告诉.NET编译器在哪里可以找到Rti.ConnextDds NuGet包。 |
USER_QOS_PROFILES.xml | 生成的示例中DDS实体的服务质量(QoS)配置从该文件加载。 |
表5.4为示例“Hello.idl”创建的Java文件
类型 | 产生文件 | 描述 |
由于Java语言要求为每个类创建单独的文件,所以Code Generator将为每个IDL构造生成一个源文件,并将其转换为Java中的类。 | ||
常量 | Hello.java | 与常量关联的类 |
枚举 | Hello.java | 与枚举类型关联的类 |
结构/活接头 | Hello.java HelloSeq.java HelloDataReader.java HelloDataWriter.java HelloTypeSupport.java | 结构/联合类序列类DDS DataReader和DataWriter类支持(序列化、反序列化等)类 |
序列数组的类型定义 | Hello.java HelloSeq.java HelloTypeSupport.java | 包装类序列类支持(序列化、反序列化等)类 |
使用-example<architecture>命令行选项时会生成以下可选文件。您可以修改并使用这些文件作为创建发布或订阅用户数据类型的简单应用程序的方法。 | ||
最后一个结构/联合 | HelloPublisher.java HelloSubscriber.java | 发布或订阅用户数据类型的应用程序的示例代码。您应该修改发布应用程序中的代码,以设置和更改发布数据的值。否则,两个文件都应该准备好编译和运行。 |
makefile_Hello_ <architecture> | 非基于Windows的体系结构的Makefile。一个<体系结构>的例子是rmv8Linux4gcc7.3.0。 | |
USER_QOS_PROFILES.xml | 生成的示例中DDS实体的服务质量(QoS)配置从该文件加载。 | |
结构/活接头/ 类型定义/枚举 | HelloTypeCode.java | 与IDL类型关联的类型代码类,Hello |
| USER_QOS_PROFILES.xml | 生成的示例中DDS实体的服务质量(QoS)配置从该文件加载。 |
表5.5为示例“Hello.idl”创建的Ada文件
产生文件 | 描述 |
Hello[.h,.c] | 为数据类型生成的代码,其中包含数据类型的实现,以及包含数据类型实现中使用的声明的头文件。 |
HelloSupport[.h,.c] | |
HelloPlugin[.h,.c] | |
hello_idl_file[.adb, .ads] | |
hello_idl_file-hello_datawriter.ads | DataReader和DataWriter类以及序列化/反序列化方法。 |
hello_idl_file-hello_datawriter.ads | |
hello_idl_file-hello_typesupport[.adb,.ads] | |
hello_idl_file-hello_metptypesupport[.adb,.ads] | 这些文件仅为支持零拷贝传输共享内存的类型生成(即,在IDL文件中用@transfer_mode(SHMEM_REF)注释)。 |
hello_idl_file-hello_publisher[.adb,.ads](在示例/目录中) | 发布用户数据类型的应用程序的示例代码。您需要修改代码以设置和更改数据结构中发送的值。否则,只需编译并运行。subscriberlistener文件实现了on_data_available()回调。 |
hello_idl_file-hello_subscriber[.adb,.ads](在示例/目录中) | |
hello_idl_file-hello_subscriberlistener[.adb,.ads](在示例/目录中) | |
hello.gpr | 使用类似Ada语法的项目文件。这些文件定义了应用程序的构建相关特性。这些特征包括源列表、这些源的位置、生成的对象文件的位置、主程序的名称以及构建过程中涉及的各种工具的选项。每个文件都是一组不同的文件(hello示例用于示例,hello_c用于c文件,hello用于其余ada文件) |
hello_c.gpr | |
hello-samples.gpr (in the samples directory) | |
使用-example<architecture>命令行选项时会生成以下可选文件。您可以修改并使用此文件来创建发布或订阅用户数据类型的简单应用程序。 | |
USER_QOS_PROFILES.xml | 生成的示例中DDS实体的服务质量(QoS)配置从该文件加载。 |
5 自定义生成的代码
代码生成器允许您通过更改提供的模板,为不同的语言自定义生成的代码。此版本不允许您创建新的输出文件。
可以在现有模板中使用以下命令加载新模板,其中<pathToTemplate>是具有自定义模板的文件夹的绝对路径:
#parse(“<pathToTemplate>/template.vm”) |
如果template.vm文件包含宏,则可以在原始模板中使用它。如果template.vm仅包含纯文本而没有宏,则该文本将直接包含在原始文件中。
您可以使用代码生成器提供的预定义变量集自定义模板的行为。有关详细信息,请参阅RTI_rtiddsgen_template_variables.xlsx中的表。
此文件包含两个不同的工作表:语言模板Language-Templates和模板变量Template variables。语言模板工作表显示了所使用的Velocity模板与为每种语言生成的文件之间的对应关系。例如,如果我们想在Hello.C文件中添加一个C语言的方法,我们需要修改templates/C目录下的模板类型Body.vm。
模板的范围可以是:
类型type:如果我们为IDL文件中的每种类型生成一个带有该模板的文件。例如,在Java中,我们为IDL中的每个类型生成一个TypeSupport文件。
file:如果我们为每个IDL文件生成一个带有该模板的文件。例如,在C中,我们生成一个包含所有类型插件信息的插件文件。
lastTopLevelType:如果我们使用IDL文件中最后一个顶级类型的模板生成一个文件。这通常用于发布者/订阅者示例。
模块:如果我们为IDL文件中的每个模块生成一个带有该模板的文件。这在Ada中使用,其中有包含模块所有类型的文件。
topLevelType:如果我们为idl文件中的每个类型生成一个具有该模板的文件。这在ADA中使用,其中发布者/订阅者文件仅为顶级类型生成
该表还显示了可用于该模板的top_level变量。这些变量在工作表模板变量中进行了说明。例如,在Java中,变量的主要单位是constructMap,它是表示类型的变量的hashMap。在C语言中,我们将constructMapList作为主要单元,它是constructMap的列表。在模板变量表中,我们可以看到每个constructMap中包含哪些变量,它适用于的constructKind或类型,以及它包含的值,具体取决于我们使用的语言。
包含类型的constructMap的一个重要变量是memberFieldMapList。此列表表示类型中包含的成员。每个成员也表示为hashMap,其变量也在模板变量表中描述。
除此之外,还有一些环境或常规变量与在名为envMap的hashMap中定义的类型无关。
让我们看一个例子如何使用这些变量。假设我们要在C中生成一个方法,该方法打印结构的成员,如果是数组或序列,则打印相应的大小。对于此IDL:
module Mymodule{ struct MyStruct{ int32 longMember; int32 arrayMember [2][100]; sequence<char,2> sequenceMember; sequence <int32, 5> arrayOfSequenceMember[28]; }; }; |
我们希望生成以下内容:
void MyModule_MyStruct_specialPrint(){ printf(" longMember \n"); printf(" arrayMember is an array [2, 100] \n "); printf(" sequenceMember is a sequence <2> \n"); printf(" arrayOfSequenceMember is an array [28] is a sequence <5> "); } |
模板中的代码如下所示:
## We go through all the list of types #foreach ($node in $constructMapList) ##We only want the method for structs #*--*##if ($node.constructKind.equals(“¡ãstruct”¡À)) void ${node. nativeFQName}_specialPrint(){ ##We go through all the members and call to the macros that check if they are array or sequences #*----*##foreach($member in $node.memberFieldMapList) print("$member.name #isAnArray($member) #isASeq($member) \n"); #*----*##end } #*--*##end #end |
isAnArray宏检查成员是否为数组(即,具有变量dimensionList),在这种情况下,打印它:
#macro (isAnArray $member) #if($member.dimensionList) is an array $member.dimensionList #end #end |
isASeq宏检查成员是否为序列(即,具有变量seqSize),在这种情况下,打印它:
#macro (isASeq $member) #if($member.seqSize) is a sequence <$member.seqSize> #end #end |
启动代码生成器时,可以使用-V<name<[=<value>]命令行选项将新变量添加到模板中。此变量将添加到userVarList hashMap中。您可以在模板中将其称为$userVarList.name或$userVarList.name.equals(value)。
有关速度模板的详细信息,请参见https://velocity.apache.org/engine/1.5/user-guide.html.
6 优化代码生成过程
序列化serialization和反序列化deserialization操作的成本,随着类型复杂性typecomplexity和样本大小sample size的增加而增加。它可以成为发送和接收样本所需的延迟的重要因素。
代码生成器Code Generator提供了命令行选项-optimization,可用于指示序列化serialize/反序列化deserialize操作的优化级别level of optimization。此命令行选项允许选择三个不同级别中的一个。
6.1 优化级别
0:无优化
1:rtiddsgen为typedef生成额外代码,但优化了其使用。如果使用的类型是可以解析为基元、枚举或聚合类型(结构struct、联合union或值类型)的typedef,则生成的代码将调用可以解析typedef的最基本类型的代码。如果不希望修改为typedef生成的代码,则可以使用此级别。
这是Java、C#和C++/CLI语言支持的唯一优化级别。
例如:
typedef int32 Latitude; typedef int32 Longitude; struct Position { Latitude x; Longitude y; }; |
对于优化0,Position类型的样本的序列化将需要调用Latitude和Longitude的序列化方法。例如:
LatitudePlugin_serialize(...) { serialize_long(...) } LongitudePlugin_serialize(...) { serialize_long(...) } Position_serialize(...) { LatitudePlugin_serialize(...) LongitudePlugin_serialize(...) } |
通过优化1,rtiddsgen将纬度Latitude和经度Longitude解析为最原始的类型,以实现序列化,从而实现更高效的序列化。在这种情况下,rtiddsgen将保存两个函数/方法调用。
Position_serialize(...) { serialize_long(...) serialize_long(...) } |
2: 如果未指定,则此优化级别为默认值。(您也可以明确指定。)此优化级别仅适用于C、C++、C++11、microC、microC++和Ada语言。使用此优化级别,rtiddsgen通过使用更积极的技术来优化结构struct和值类型的序列化/反序列化。这些技术包括嵌套类型nested types的内联扩展,以及当内存布局(C,C++结构布局)与连线布局(XCDR)相同时,使用单个复制函数调用(memcpy)对一组连续成员进行序列化serialization/反序列化deserialization。
6.2 如何应用优化
在代码生成器中,优化(嵌套类型的内联扩展和具有单个副本的连续成员的序列化)是相关的。嵌套结构的内联扩展仅在具有结构标准封装的C/C++内存布局与XCDR布局匹配时进行。(在这种情况下,可以使用单个memcpy对结构的成员进行序列化。)如果具有结构的标准封装的C/C++内存布局与XCDR布局匹配,则rtiddsgen将首先尝试进行内联扩展,然后使用单个副本对连续成员进行串行化serialization。
6.2.1 嵌套类型nested types的内联扩展
内联扩展是一种优化,其中代码生成器将一个类型定义替换为另一个嵌套类型被展平的类型定义。这样做是为了在序列化serialization/反序列化deserialization期间删除额外的函数调用。例如:
struct Point { int32 x; int32 y; }; struct Dimension { int32 height; int32 width; }; struct Rectangle { Point leftTop; Dimension size; }; |
对于优化级别2,代码生成器将矩形的定义替换为以下等效定义:
struct Rectangle { int32 leftTop_x; int32 leftTop_y; int32 size_height; int32 size_width; }; |
此优化仅用于序列化/反序列化。C/C++中生成的类型继续使用点和维度。
6.2.2 使用单个副本序列化连续成员
在前面的矩形示例中,代码生成器使用优化级别2,通过使用单个复制single copy操作(memcpy)而不是四个来序列化矩形样本,进一步优化了序列化和反序列化。
优化前:
Rectangle_serialize(...) { memcpy(..., 4) // leftTop_x memcpy(..., 4) // leftTop_y memcpy(..., 4) // size_height memcpy(..., 4) // size_width } |
优化后:
Rectangle_serialize(...) { memcpy(..., 16) // leftTop_x } |
此优化仅在C/C++结构的内存布局与串行化布局等效时适用,后者使用XCDR版本1或版本2格式。
6.2.3 内联扩展规则
要实现内联,结构“MyStruct”必须满足以下两个要求:
它必须具有C/C++友好的XCDR布局。
“MyStruct”的任何成员都不应标记为@min、@max或@range注释。
当满足以下所有条件时,结构/值类型“MyStruct”具有C/C++友好的XCDR布局:
当数据表示为XCDR版本1时,MyStruct标记为@final或@appendable。可变结构不可内联。
MyStruct没有基类型。
MyStruct仅包含基本成员,或仅由基本成员组成的复杂成员。基元成员是具有以下任意类型的成员:int8,uint8、int16、int32、int64、uint16、uint32、uint64、浮点、双精度、八位字节和字符。内联不支持以下基元类型:long double、wchar、boolean、enum。
struct Dimension { int32 height; int32 width; }; // Inlinable
struct Dimension { string label; // Inlinable structures cannot contain strings int32 height; int32 width; }; // Not Inlinable |
如果任何初始对齐方式(1、2、4、8)大于结构的第一个成员的对齐方式,则属于MyStruct的成员之间没有填充。要应用此规则,请考虑图元类型的这些对齐方式和大小:
表7.1图元类型的对齐和尺寸
基本类型 | 对齐方式(字节) | 大小(字节) |
int8 | 1 | 1 |
uint8 | 1 | 1 |
int16 | 2 | 2 |
uint16 | 2 | 2 |
int32 | 4 | 4 |
uint32 | 4 | 4 |
int64 | 8 | 8 |
int64 | 8 | 8 |
float | 4 | 4 |
double | 8 | 8 |
octet | 1 | 1 |
char | 1 | 1 |
struct Dimension { int32 height; int16 width; }; // Inlinable. Independently of the alignment of the starting memory address (4 or 8), there is no padding between height and width
struct Dimension { int16 height; int32 width; }; // Not Inlinable. Starting in a memory address aligned to 4 will require adding a padding of two bytes between height and width |
如果任何初始对齐方式(1、2、4、8)大于结构的第一个成员的对齐方式,则MyStruct数组的元素之间没有填充。
struct Dimension { int32 height; int16 width; }; // Not inlinable. Let's assume an array of two dimensions Dimension[2]. If the array starts in a memory address aligned to 4, there would be padding between the first and the second element of the array
struct Dimension { int32 height; int16 width; int16 padding; }; // Inlinable |
出于序列化和反序列化的目的,代码生成器将把可内联结构(根据前面的规则)视为基元数组,其中基元类型的对齐方式对应于结构的第一个成员的对齐方式。类型为“MyStruct”的成员将使用单个副本(memcpy)调用进行序列化。
当代码生成器序列化数据结构的成员时,如果可能,它还将尝试将连续基元成员的序列化合并为单个复制操作。代码生成器仅在下一个成员的对齐等于或小于当前成员的对齐时应用此优化。
struct Dimension { int16 height; int32 width; }; // Coalescing not possible because the alignment of width 4 is greater than the alignment of height 2
struct Dimension { int32 width; int16 height; }; // Coalescing is possible because the alignment of width 4 is greater than the alignment of height 2 |
6.2.4 指南
根据经验,要利用仅包含基本类型的类型的优化级别2:
按降序排列成员(这将有助于复制合并)。
对于XCDR版本2封装,如果您的类型不会演变,请使用@final扩展性。对于XCDR版本1封装,如果可能,请使用@final或@appendable(这将有助于内联扩展)。
如果使用ContentFilteredTopics,建议将筛选器表达式中出现的字段放在类型的开头。
7 提升性能
如果需要使用不同的参数和/或类型文件多次调用代码生成器Code Generator,则每次调用代码生成器时,加载Java虚拟机(JVM)和编译速度模板都会导致性能损失。如果是这样的场景,可以在服务器模式server mode下运行代码生成器Code Generator,以避免多次执行此过程。或者,如果您希望减少JVM启动和执行时间,请使用代码生成器Code Generator提供的JVM优化机制,以减少代码生成时间。
这两个选项(服务器模式server mode和JVM优化)不能一起使用。
7.1 使用服务器模式server mode
提高性能的一种方法是,在服务器模式server mode下运行代码生成器Code Generator。服务器模式运行本机可执行文件,该可执行文件打开与代码生成器的服务器实例的TCP连接,该代码生成器在第一次运行可执行文件时生成,如下所示:
要在服务器模式servermode下调用代码生成器,请使用脚本rtiddsgen_server(.bat),该脚本位于scripts目录中。
CodeGenerator服务器连接到的默认端口是14662。如果要修改Code Generator服务器连接到的端口,请使用-n_serverport<port>参数。请注意,代码生成器服务器最多可以使用三个端口;确保在指定端口之后有两个可用端口(如14663和14664)。
代码生成器服务器提供了一个日志到文件选项,您可以使用参数-n_logfilepath<log directory>来启用该选项。在指定的日志目录中,代码生成器服务器将创建一个名为CodegenServerLog<portNumber>.txt的文件,其中包含来自服务器端的所有日志消息。
代码生成器服务器带有内置超时builtintimeouts,其中一些超时可以更改:
当代码生成器在服务器模式server mode下使用时,JVM在服务器启动时加载一次;速度模板也是一次性编译的。服务器将等待最多5秒以初始化代码生成器。您可以通过使用参数-n_connectiontimeout<timein millises>指定毫秒数来更改此值。
如果代码生成器服务器Code Generator server在一定时间内未被使用(即,如果它没有收到任何呼叫),则它将自动停止。默认值为20秒;您可以通过编辑rtiddsgen_server脚本并调整参数-nservertimeout的值来更改这一点。
代码生成器服务器Code Generator server在接受连接后向客户端发送握手handshake消息。在客户端中,等待握手消息时会超时。如果客户端在短时间内(10秒,一个无法更改的内部值)未收到此消息,则代码生成器客户端将超时。此超时可能意味着端口中正在运行另一个应用程序。
注意:
不支持混合不同版本的代码生成器服务器Code Generator server。请参阅RTI代码生成器发行说明中的限制。
代码生成器服务器Code Generator server无法并行化。代码生成器服务器的每次执行都连接到接收请求的端口,并且每次只能为一个请求生成代码。所以,若您尝试同时发送多个请求,代码生成器服务器将按顺序处理它们。
7.2 使用JVM优化
Java虚拟机(JVM)提供了不同的选项,有助于提高其性能。RTI在创建代码生成器JVM时应用了其中的一些选项,从而提高了代码生成器Code Generator的执行时间。由于这些选项是非标准的,因此默认情况下将禁用它们。如果您使用的是Connext DDS附带的JRE,则可以启用这些选项;否则,启用这些选项的风险自负。
应用于代码生成器JVM的选项如下:
-XX:+TieredCompilation -XX:TieredStopAtLevel=1 -XX:CICompilerCount=4 -Xverify:none -XX:+UseParallelGC -XX:+OptimizeStringConcat -XX:CompileThreshold=5000 |
有关这些选项的更多信息,请参阅JDK文档:
https://docs.oracle.com/en/java/javase/11/ 。(目前,您可以在“创建和构建应用程序的主要工具Main Tools to Create and Build Applications”>“java”中找到“工具参考”中描述的大多数选项)。
若要启用与代码生成器JVM选项相关的性能改进,请将RTIDDSGEN_JVM_OPTIONIC环境变量设置为true。要禁用改进,请取消设置环境变量。(默认情况下,未设置。)