DDS协议————Topics, keys and instances详解
1.什么是Topics, keys and instances?
上图是Fast DDS 给出的一个图,用来描述3者的关系:
- Topic : 用于两个数据之间的传递通道,一个Topic 对应一个数据类型,也就是图中的Foo
- Instance : 通过Key将同一个数据类型进行逻辑区分,从而形成不同的Instance。实例用于管理和跟踪数据对象的状态和生命周期。每个实例代表一种特定的数据状态,具有唯一的标识符,这些标识符由一个或多个键字段组成。
- Key:在一个Topic中,可以将同一个数据类型进行不同的逻辑区分,就像Map<Key,Object>一样,进行不同的映射,但是有所区别的是,在DDS中一个数据类型可以有多个Key。
数据类型举例:
/* 单一的key */
struct SensorData
{
@key int sensorId;
float temperature;
float humidity;
}
/* 多个key */
struct ComplexData {
@key int id1;
@key int id2;
float value;
};
2.Topic,Instance,Key的具体使用场景
在机场中,需要进行相应的空中管制,当航班飞出管制区时则将这辆飞机移除管制列表,不在对它进行监控。
因为DDS是以数据为中心的,故如何定义数据类型至关重要:
- 将航空公司名字,航班编号作为Key
- 记录相关数据
大致流程如下
定义数据类型:
struct FlightPosition { @key string<256> airline_name; /* 航空公司名字 */ @key short flight_number; /* 航班编号 */ /* 记录的数据 */ double latitude; double longitude; double altitude; }
当一个新的飞机被空管中心发现,则进行Instance的初始化和注
/* 创建数据样本 */ FlightPosition first_flight_position; /* 修改这个Instance 设置它的Keys */ first_flight_position.airline_name("IBERIA"); first_flight_position.flight_number(1234); /* 将Instance进行注册 ,使用Writer 的Api 进行Instance的注册 */ eprosima::fastrtps::rtps::InstanceHandle_t first_flight_handle = data_writer->register_instance(&first_flight_position); /* 在注册实例后将返回一个对应的句柄 */
在空管范围内不断修改飞机的属性,并使用句柄,对发布的数据Instance进行修改
/* 修改属性 */ first_flight_position.latitude(39.08); first_flight_position.longitude(-84.21); first_flight_position.altitude(1500); /* 使用Write 的API 修改对应的值 */ data_writer->write(&first_flight_position, first_flight_handle);
值得注意的一点是,对与同一个数据类型,可以重新更新到不同的句柄中,因此管理好句柄至关重要,以下就是一个例子:
/* 一个新的航班 */ FlightPosition second_flight_position; second_flight_position.airline_name("RYANAIR"); second_flight_position.flight_number(4321); second_flight_position.latitude(40.02); second_flight_position.longitude(-84.32); second_flight_position.altitude(5000); /* 不注册Instance 直接写入 */ data_writer->write(&second_flight_position); data_writer->write(&second_flight_position, first_flight_handle);
Notes:注册实例和不注册实例的区别
注册实例是指在首次写入数据之前,通过调用
register_instance
方法将实例显式地注册到DDS中。这通常用于对实例进行显式的生命周期管理,如需要手动注销实例(unregister_instance
)或处理实例释放(dispose
)。优点
- 显式管理生命周期:可以显式地控制实例的注册、注销和释放,有助于精细管理数据实例。
- 性能优化:对于频繁更新的实例,提前注册实例可以减少内部开销,因为DDS不需要每次都查找和创建实例。
- 一致性控制:确保实例状态的一致性,避免未注册实例的误操作。
缺点
- 复杂性增加:需要显式管理实例的注册和注销,增加了代码复杂性。
- 资源管理:需要显式释放实例以避免资源泄漏。
虽然在语法上不会出现错误,但可能会极大的影响功能逻辑
/* 错误示例 */ data_writer->write(&second_flight_position, first_flight_handle);
Notes:
/*如果是一个空的实例可以使用HANDLE_NIL进行替代,相当于C语言中防止野指针的NULL*/ data_writer->register_instance(&second_flight_position, HANDLE_NIL);
在处理数据时,应进行一些逻辑上的检测,以增强代码的健壮性
if (ReturnCode_t::RETCODE_OK == data_reader->take_next_sample(&data, &info)) { if (info.valid_data) { // Data sample has been received } else if (info.instance_state == NOT_ALIVE_DISPOSED_INSTANCE_STATE) { // A remote DataWriter has disposed the instance } else if (info.instance_state == NOT_ALIVE_NO_WRITERS_INSTANCE_STATE) { // None of the matched DataWriters are writing in the instance. // The instance can be safely disposed. } }