63次发布
0.10.3 | 2024年8月2日 |
---|---|
0.10.2 | 2024年7月31日 |
0.10.1 | 2024年4月19日 |
0.9.1 | 2024年3月27日 |
0.0.3 | 2020年11月25日 |
139 在 网络编程
2,927 每月下载量
用于 3 个包
2MB
40K SLoC
RustDDS
RustDDS 是 数据分发服务 的纯Rust实现。最新版本可在 crates.io 上找到,API文档在 docs.rs 上。GitHub仓库 RustDDS 跟踪开发。
RustDDS 由 Atostek Oy 开发。Atostek 提供与DDS、ROS2和机器人软件相关的支持和软件开发服务。作为我们工作的一部分,我们开源了RustDDS实现。
我们试图将DDS应用程序接口的关键思想翻译成Rust概念,同时也遵循Rust约定。因此,API并不是严格按照DDS规范编写的,而是使用Rust概念和约定实现的函数等效近似。
数据分发服务
数据分发服务(DDS)是面向实时系统的数据分发服务,是一个面向机器到机器连接性的对象管理组(OMG)框架,旨在通过发布-订阅模式实现可扩展、实时、可靠、高性能和互操作的数据交换。DDS满足了空中交通管制、智能电网管理、自动驾驶汽车、机器人、交通系统、发电、医疗设备、仿真和测试、航空航天和国防等需要实时数据交换的应用的需求 [Wiki]。
当前实现状态
目前,实现已足够完整,可以与ROS2软件进行数据交换。
建议使用ros2-client与ROS组件通信。RustDDS中的ros2
模块不再使用。
版本 0.10.0
扩展了用于将序列化格式附加到RTPS的DeserializerAdpter
接口,以支持使用"种子"值进行反序列化。这允许反序列化过程输入除输入字节流之外的其他运行时数据。
0.10.1
- 使RTPS时间戳的本地滴答计数公开访问
0.10.2
- 错误修复:如果数据样本到达顺序错误,可靠的DataReader交付的数据样本顺序错误。
- 更改Windows上的套接字初始化行为。
- CDR序列化现在是一个独立的Rust crate。
0.10.3
- 修复了提高与CycloneDDS互操作性的错误。
- 两个示例程序用于测试CycloneDDS示例
版本 0.9.2
- 重新设计内部缓存以解决连接中的问题。
版本 0.9.1
- 修复了许多错误。
- DDSCache中存在内存泄漏。
- 可靠的接收器可能会卡住。
- 与FastDDS的DDS安全性互操作性得到改善。
- 新增安全功能,例如PKCS#11支持、RSA身份验证支持
版本 0.9
- 新版本发布,以启用
ros2-client
中的新功能 - DDS安全性正在进行的互操作性测试。
- 支持域参与者状态事件,主要与发现相关。
- 小的API更改
- 简化命名
- QoS对象现在是可序列化的
- 与可靠连接的协议错误修复。
版本 0.8.6
- 功能
security
即将完成。RustDDS可以安全地与自己通信,但与其他DDS实现进行的互操作性测试仍在进行中。 - 修复了处理序列号时的一些错误。
- RTPS Writer数据发送重写。
- 修复错误:重传数据上缺少源时间戳。
版本 0.8.5
- 功能
security
已合并到master,但它仍然是一个工作正在进行中的项目,因此尚未启用。 - 应该再次在Windows上工作
- 反序列化中更宽松的生命周期限制
- 简化Key特质的用法
版本 0.8
新功能
- 异步API可用。
- 使用mio-0.6或mio-0.8进行轮询。
- 简化了DataReader
SimpleDataReader
。它只支持.take()
调用,但应该比常规DataReader更轻更快。它旨在只提供足够的函数来实现ROS2订阅者。
此版本破坏了兼容性
- 从
read()
/.take()
调用返回的数据的命名已从Result
更改为Sample
。这样做是为了减少混淆的命名,因为在以前的用法中,Err
变体中的Result
并不表示实际的错误条件,而是一个数据实例丢弃操作。 - 错误类型已重新设计,以更好地反映可能导致的错误,而不是为整个API有一个复杂的错误类型。这是与DDS规范有意的偏差,以使实现更类似于Rust。
版本 0.6
此版本与0.5.x不兼容。公共API名称有一些细微差异。进行了更改,以遵循Rust命名约定。版本0.6.0修复了回归,其中与eProsima FastRTPS的通信只能持续一段时间。
版本 0.5
此版本与0.4.0不兼容。差异包括
- 命名规范更接近Rust风格,而不是DDS规范——主要是大写和下划线。
- 一些新函数现在需要拥有自己的
String
而不是&str
。只需添加.to_string()
即可修复。 - 键大小检测(是否超过16字节?)现在通过派生宏实现了一个特质。
特性状态
- 发现 ✅
- 可靠性QoS:可靠和尽力而为 ✅
- 历史QoS ✅
- RTPS over UDP ✅
- 广播UDP ✅
- 非阻塞I/O ✅
- 主题种类:有键和无键 ✅
- 零拷贝接收路径 ✅
- 零拷贝传输路径
- 主题创建 ✅
- 主题查找 ✅
- 分区QoS
- 基于时间的过滤器QoS
- 所有权QoS
- 表示QoS:一致/原子样本集和排序
- 截止时间和延迟预算QoS
- 样本碎片化(大对象交换) ✅
wait_for_acknowledgments
✅- 域参与者监听器(或等效) ✅
- 主题监听器(或等效)
- 使用Rust
async
任务的其他API ✅ - 本地连接的共享内存传输
互操作性
使用可用的"形状"演示程序。数据交换在两个方向上都工作
- RTI Connext
- eProsima FastRTPS
- OpenDDS
- Twin Oaks Computing
使用
请参阅包内包含的示例,以及互操作性测试。
数据序列化和密钥
一些现有的DDS实现使用代码生成来为每种有效负载类型实现DataReader和DataWriter类。
我们不依赖于代码生成,而是使用Rust泛型编程:有一个泛型DataReader和DataWriter,由有效负载类型D和序列化适配器类型SA参数化。使用Serde库进行有效负载数据的序列化和反序列化。
当与DataWriter一起使用时,有效负载类型D必须实现serde::Serialize
,当与DataReader一起使用时,必须实现serde::DeserializeOwned
。许多现有的Rust类型和库已经支持Serde,因此它们无需修改即可使用。
在DDS中,包含多个不同实例的WITH_KEY主题,这些实例通过键来区分。密钥必须以某种方式嵌入到数据样本中。在我们的实现中,如果有效负载类型D在WITH_KEY主题中通信,则D还必须实现特质Keyed
。
特质Keyed
需要一个方法:key(&self) -> Self::K
,它用于从D
中提取相关类型K
的键。键类型K
必须实现特质Key
,这是现有特质Eq + PartialEq + PartialOrd + Ord + Hash + Clone + Serialize + DeserializeOwned
的组合,并且没有其他方法。
为OMG通用数据表示(CDR)提供了序列化适配器类型SA(Serde数据格式的包装),因为这是DDS/RTPS使用的默认序列化格式。可以通过提供Serde 数据格式实现来使用其他序列化格式进行DDS通信的对象。
对DDS规范的有意偏离
原因
DDS 1.4规范指定了一个对象模型和一组API,这些API构成了DDS规范。这些API的设计,例如命名约定和内存管理语义,在Rust世界中并不完全适用。我们试图创建一个设计,在其中,重要的DDS思想得以保留和实现,但以适合Rust的方式。这些设计妥协只能在DDS面向应用的API中体现。网络侧仍在努力与现有的DDS实现完全互操作。
类层次结构
DDS指定了一个类层次结构,它是API的一部分。这个层次结构不一定被遵循,因为Rust在继承和派生类方面并不像C++那样使用。
命名约定
我们试图遵循Rust的命名约定。
数据监听器和WaitSets
DDS提供了两种等待到达数据的替代方法,即WaitSets和Listeners。我们选择使用来自mio crate的非阻塞IO API来替代这些方法。DDS DataReader对象可以直接与mio的Poll
接口一起使用。应该可以实现其他API,例如在该API之上实现异步API。
实例句柄
DDS使用“实例句柄”,它们类似于指向由DDS实现管理的对象的指针。这似乎与Rust内存处理不太相容,所以我们选择不实现这些。
实例句柄可以用来引用具有特定键的数据值(样本)。我们已经编写了API来直接使用键,因为这看起来在语义上是等效的。
返回代码
DDS指定的标准方法返回代码列表(第2.2.1.1节)被修改,特别是
- 不使用
OK
代码来指示成功操作。成功或失败使用标准的Result
类型来指示。 - 不使用
TIMEOUT
代码。超时应表示为Result::Err
或Option::None
。 - 不应使用通用的
ERROR
代码,而应使用更具体的价值。 NO_DATA
不使用。数据不存在应编码为Option::None
。
DataReader和DataWriter接口
DDS规范指定了多个函数来从DataReader中读取接收到的数据样本
read
:从DataReader访问反序列化数据对象,并标记为已读取。如果请求已读取的样本,则可以再次读取相同的样本。take
:与read类似,但移除了从DataReader返回的对象,因此无法再次访问。read_w_condition、
take_w_condition
:读取/获取匹配指定条件的样本。read_next_sample、
take_next_sample
:读取/获取下一个非先前访问的样本。read_instance、
take_instance
:读取/获取属于单个实例(具有相同键)的样本。read_next_instance、
take_next_instance
:_next
和_instance
的组合。read_next_instance_w_condition
,take_next_instance_w_condition
:是_next
,_instance
和_w_condition
的组合。
我们决定不实现这12个中的全部。相反,我们实现了一组更小的方法。
read
:从 DataReader 中借用数据。take
:将数据从 DataReader 移动。read_instance
,take_instance
:访问属于单个键的样本。
上述所有方法都需要 ReadCondition 来指定要访问的样本,但指定“任何”条件,即无条件访问,非常简单。
还有 read_next_sample
,take_next_sample
方法,但这些都是 read/take 的简化包装器。
除了这些,我们还提供 Rust Iterator 接口来读取数据。
内存管理
DDS 规范指定在手动内存管理方面的操作,即许多对象类型通过 create_
方法调用创建,并通过匹配的 delete_
方法调用销毁。我们选择在可能的情况下依赖 Rust 内存管理,包括处理有效载荷数据。
基于 rtps-rs
这里使用的 RTPS 实现源自 rtps-rs。
依赖关系
~6–23MB
~329K SLoC