OpenDDS(DDS)主要的目的是用于在网络上交换数据,但究竟什么是数据?
DDS中,可以被交换的数据是sturct(结构体)。例如我们可以创建一个包含特定领域的结构体,比如:
struct MyData {
long counter;
string message;
double threshold;
};
这个结构描述了一段可以通过dds发送和接收的数据,换句话说,就是topic(主题)。
我们必须考虑opendds能够在用不同语言创建的进程之间交换数据。例如,C++ Publisher发送一个主题,它可以被 Java 进行接收。基于这个原因,我们必须定义交换C++和Java数据的结构。为了避免这个问题,使用.idl文件在一个单独的文件中定义主题文件格式。当我们完成创建它时,可以从这个文件创建Java和C ++中的所有必需的源文件。我们以不可知的格式定义主题,然后使用一些OpenDDS工具创建我们需要的C ++或Java文件。有了这个,我们可以仅创建一次主题,以避免错误,因为数据仅在同一个地方被定义了一次。换句话说,当我们决定创建一些要交换的主题时,我们需要:
- 定义主题的字段
- 在.idl文件中定义主题
- 编译用于生成C ++文件的.idl文件
- 编译用于生成Java文件的.idl文件
当然,如果我们只使用一种语言,我们不需要为其他语言创建文件。
我们需要定义我们想要在 IDL 中交换的结构。这是一个用C ++风格编写的定义结构化文件的文件。
IDL(接口描述语言)是一种使用C ++风格定义结构的文本文件。该文件包含必须交换的数据结构。结构由struct
关键字定义。以下是一个包含真实值和字符串的结构示例:
struct SomeUsefulStruct {
double myPreciousDouble;
string veryVeryImportantString;
};
正如我们所看到的,创建一个字符串并不困难。在下表中,我们将显示可以在字符串内使用的所有数据类型:
Type | Description | C++ equivalent |
---|---|---|
Boolean | A true/false value | bool |
char | Simple char value | char |
wcahr | Wide char value | wchar_t |
string | A string with variable length | string |
wstring | A string with variable length with wide char characters | wstring |
octet | A byte | |
short | Short integer value | short |
unsigned short | Unsigned short integer value | unsigned short |
long | 32 bit integer value | int |
unsigned long | Unsigned 32 bit integer value | unsigned int |
long long | 64 bit integer value | long long |
unsigned long long | Unsigned 64 bit integer value | unsigned long long |
float | Float value | float |
double | Double value | double |
fixed | Big value | Not supported at the moment |
作为一个例子,我们可以尝试创建一个用于检查数据的IDL消息。创建一个test.idl文件,并复制以下内容:
module Sample {
struct DataTypesList
{
boolean booleanValue;
char charValue;
wchar wcharValue;
string stringValue;
wstring wstringValue;
octet octedValue;
short shortValue;
unsigned short unsignedShortValue;
long longValue;
unsigned long unsignedLongValue;
long long longLongValue;
unsigned long long unsignedLongLongValue;
float floatValue;
double doubleValue;
fixed fixedValue;
};
}; // module Sample
将其保存,并使用IDL命令build。
opendds_idl.exe .\file.idl -o .
tao_idl.exe -Sg file.idl
tao_idl.exe -Sg fileTypeSupport.idl -o .
编译之后得到一堆文件,打开fileC.h
我们可以发现C++结构。
struct DataTypesList
{
// TAO_IDL - Generated from
// be\be_type.cpp:307
typedef DataTypesList_var _var_type;
typedef DataTypesList_out _out_type;
static void _tao_any_destructor (void *);
::CORBA::Boolean booleanValue;
::CORBA::Char charValue;
::CORBA::WChar wcharValue;
::TAO::String_Manager stringValue;
::TAO::WString_Manager wstringValue;
::CORBA::Octet octedValue;
::CORBA::Short shortValue;
::CORBA::UShort unsignedShortValue;
::CORBA::Long longValue;
::CORBA::ULong unsignedLongValue;
::CORBA::LongLong longLongValue;
::CORBA::ULongLong unsignedLongLongValue;
::CORBA::Float floatValue;
::CORBA::Double doubleValue;
};
正如所看到的,idl编译器转换自定义c ++类型中的idl数据类型,允许执行某种抽象。其使用常见的数据类型映射到C++中。
我们想要构建一个场景模拟器。模拟器由两个过程组成:
这是两个独立的过程,它们之间会发生交互。
我们需要创建一个包含idl,其中包含了我们想要交换的消息。消息的类型取决于我们想要执行的操作。
该示例中用户可以从场景中添加和删除玩家。对于这个用例,Gui必须能够发送添加玩家的命令和删除玩家的命令。而且,一旦玩家被创建,GUI必须读取玩家的位置以便将其显示在正确的位置。另一方面,Core必须接收添加/删除玩家的命令,并向操作结果发送回复给Gui,然后定期发送玩家的位置。
如下是这个示例的示意图:
从用例中可以看到需要以下消息:
我们需要创建一个包含这些消息的idl
:
#ifndef SIMULATION_IDL_
#define SIMULATION_IDL_
module Message {
#pragma DCPS_DATA_TYPE "Message::LLA"
struct LLA {
double latitude;
double longitude;
double altitude;
};
#pragma DCPS_DATA_TYPE "Message::Orientation"
struct Orientation {
double roll;
double pitch;
double yaw;
};
#pragma DCPS_DATA_TYPE"Message::PlayerPosition"
// Position of a player that is presentinside a scenario.
struct PlayerPosition {
string name;
LLA pos;
Orientation ori;
};
#pragma DCPS_DATA_TYPE "Message::AddPlayer"
// Command for adding a player to thescenario.*
struct AddPlayer {
string name;
string profile;
LLA pos;
Orientation ori;
};
#pragma DCPS_DATA_TYPE "Message::RemovePlayer"
// Command for removing a player from thescenario.*
struct RemovePlayer {
string name;
};
#pragma DCPS_DATA_TYPE "Message::PlayerAdded"
// Response that says if a player is addedto the scenario.*
struct PlayerAdded {
string name;
string profile;
LLA pos;
Orientation ori;
boolean result;
};
#pragma DCPS_DATA_TYPE "Message::PlayerRemoved"
// Response that says if a player isremoved from the scenario.*
struct PlayerRemoved {
string name;
boolean result;
};
}; // module Message
#endif // !SIMULATION_IDL_
该代码显示了一些基本结构和一些组件(例如选手的位置)。
此时,我们可以构建IDL以获取所需的C++文件,因此将IDL保存在一个文件中,例如sample.idl
, 且给出了如下的命令:
opendds_idl.exe sample.idl -o .
tao_idl.exe -Sg sample.idl -o .
tao_idl.exe -Sg sampleTypeSupport.idl -o .
此处创建了一堆文件,可以将其包含到项目中。以下是这些生成的代码分析:
sampleC.h / sampleC.cpp:这是文件定义了用C++创建的结构体。每个结构体都包含在idl文件中定义的所有成员。它们还包含用户和发布者中使用的一些typedef,以及一些重载的流操作符
sampleC.inl:目前它不包含任何内容,仅包含评论
需要注意的一点是:在.idl文件中,我们将消息创建为一个module
; 特别是在消息模块中。这在我们的C++文件中被翻译成命名空间。所以我们在Message模块中创建了IDL中的结构,而在C++文件中,这些结构进入了Message名称空间。这非常有用,特别是当我们想要组织大量的主题时,我们希望避免在代码中与其他类名称冲突。
这些文件是IDL描述文件的C++翻译。为了使用它们,需要将其包含进我们的工程中,然后就可以利用OpenDDS的API收发数据。
本文介绍了如何创建一个基本的IDL,且简要分析了通过编译创建的源代码。