1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
//! A pure Rust implementation of Data Distribution Service (DDS).
//!
//! DDS is an object-oriented API [specified](https://www.omg.org/spec/DDS/) by
//! the Object Management Group.
//!
//! DDS communicates over the network using the [RTPS](https://www.omg.org/spec/DDSI-RTPS/)
//! protocol, which by default runs over UDP/IP.
//!
//! This implementation does not attempt to make an accurate implementation of
//! the DDS object API, as it would be quite unnatural to use in Rust as such.
//! However, we aim for functional compatibility, while at the same time using
//! Rust techniques and conventions.
//!
//! Additionally, there is a [ROS2](https://index.ros.org/doc/ros2/) interface, that is simpler to use than DDS
//! when communicating to ROS2 components.
//!
//! # DDS usage summary
//!
//! * Create a [`DomainParticipant`]. You have to choose a domain id. The
//!   default value is zero.
//! * Create or find a [`Topic`] from the [`DomainParticipant`]. Topics have a
//!   name and a type.
//! * Create a [`Publisher`] and/or [`Subscriber`] from the
//!   [`DomainParticipant`].
//! * To receive data, create a [`DataReader`](with_key::DataReader) from
//!   [`Subscriber`] and [`Topic`].
//! * To send data, create a [`DataWriter`](with_key::DataWriter) from
//!   [`Publisher`] and [`Topic`].
//! * Data from `DataReader` can be read or taken. Taking removes the data
//!   samples from the DataReader, whereas reading only marks them as read.
//!
//! # Concepts
//!
//! * Data is sent and received in consecutive *samples*. When read, a sample is
//!   accompanied with [`SampleInfo`], which contains DDS-generated metadata.
//! * Topics are either With_Key or No_Key.
//!   * With_Key topics are like map data
//!   structures, containing multiple *instances* (map entries), identified by a
//!   *key*. The key must be something that can be extracted from the data
//!   samples. Instances can be created (published) and deleted (disposed).
//!   * No_Key topics have always only one instance, which cannot be disposed.
//!   * Many types and traits in RustDDS have both with_key and no_key versions.
//!     This is
//!   because with_key communication must be able to access keys from data
//! samples, so it is required   in type signatures. Such requirement makes no
//! sense for no_key communication, so signature   must be different.
//!
//!
//! # Interfacing Rust data types to DDS
//!
//! * DDS, as specified, takes care of data serialization and deserialization.
//! In order for RustDDS to do this, the payload data must be [Serde](https://serde.rs/)
//! serializable/deserializable.
//! * If your data is to be communicated over a WithKey topic, the payload data
//!   type must implement [`Keyed`] trait from this crate.
//! * If you are using [CDR serialization](https://en.wikipedia.org/wiki/Common_Data_Representation)
//!   ([specification, Section 15.3](https://www.omg.org/cgi-bin/doc?formal/02-06-51)) , which
//!   is the DDS default, then use [`CDRSerializerAdapter`] and
//!   [`CDRDeserializerAdapter`]
//!   when such adapters are required. If you need to use another serialization format, then you should find or write
//!   a [Serde data format](https://serde.rs/data-format.html) implementation and wrap it as a (De)SerializerAdaper.
//!
//! # Polling multiple DataReaders
//!
//! RustDDS is designed to used with [mio](mio) version 0.6.x. DataReaders
//! implement [`Evented`](mio::event::Evented) so that they can be directly
//! registered to a [`poll`](mio::Poll).
//!
//! # Usage Example
//!
//! ```
//! use rustdds::*;
//! use rustdds::no_key::{DataReader, DataWriter, DataSample}; // We use a NO_KEY topic here
//! use serde::{Serialize, Deserialize};
//!
//! // DomainParticipant is always necessary
//! let domain_participant = DomainParticipant::new(0).unwrap();
//!
//! let qos = QosPolicyBuilder::new()
//!   .reliability(policy::Reliability::Reliable { max_blocking_time: rustdds::Duration::DURATION_ZERO })
//!   .build();
//!
//! // DDS Subscriber, only one is necessary for each thread (slight difference to
//! // DDS specification)
//! let subscriber = domain_participant.create_subscriber(&qos).unwrap();
//!
//! // DDS Publisher, only one is necessary for each thread (slight difference to
//! // DDS specification)
//! let publisher = domain_participant.create_publisher(&qos).unwrap();
//!
//! // Some DDS Topic that we can write and read from (basically only binds readers
//! // and writers together)
//! let some_topic = domain_participant.create_topic("some_topic".to_string(), "SomeType".to_string(), &qos, TopicKind::NoKey).unwrap();
//!
//! // Used type needs Serialize for writers and Deserialize for readers
//! #[derive(Serialize, Deserialize)]
//! struct SomeType {
//!   a: i32
//! }
//!
//! // Creating DataReader requires type and deserializer adapter (which is recommended to be CDR).
//! // Reader needs to be mutable if any operations are used.
//! let mut reader = subscriber
//!   .create_datareader_no_key::<SomeType, CDRDeserializerAdapter<SomeType>>(
//!     &some_topic,
//!     None)
//!   .unwrap();
//!
//! // Creating DataWriter required type and serializer adapter (which is recommended to be CDR).
//! let writer = publisher
//!   .create_datawriter_no_key::<SomeType, CDRSerializerAdapter<SomeType>>(
//!     &some_topic,
//!     None)
//!   .unwrap();
//!
//! // Readers implement mio Evented trait and thus function the same way as
//! // std::sync::mpcs and can be handled the same way for reading the data
//!
//! let some_data = SomeType { a: 1 };
//!
//! // This should send the data to all who listen "some_topic" topic.
//! writer.write(some_data, None).unwrap();
//!
//! // ... Some data has arrived at some point for the reader
//! let data_sample = if let Ok(Some(value)) = reader.read_next_sample() {
//!   value
//! } else {
//!   // no data has arrived
//!   return;
//! };
//!
//! // Getting reference to actual data from the data sample
//! let actual_data = data_sample.value();
//! ```
#![deny(clippy::all)]
#![warn(clippy::needless_pass_by_value, clippy::semicolon_if_nothing_returned)]
#![allow(
  clippy::option_map_unit_fn,
  // option_map_unit_fn suggests changing option.map( ) with () return value to if let -construct,
  // but that may break code flow.
)]

#[macro_use]
mod serialization_test;
#[macro_use]
mod checked_impl;
mod discovery;
mod messages;
mod network;
pub(crate) mod structure;

#[cfg(test)]
mod test;

// Public modules
pub mod dds; // this is public, but not advertised
pub mod ros2;
/// Helpers for (De)serialization and definitions of (De)serializer adapters
pub mod serialization;

// Re-exports from crate root to simplify usage
#[doc(inline)]
pub use dds::{
  participant::DomainParticipant,
  pubsub::{Publisher, Subscriber},
  qos,
  qos::{policy, QosPolicies, QosPolicyBuilder},
  readcondition::ReadCondition,
  sampleinfo::{InstanceState, NotAliveGenerationCounts, SampleInfo, SampleState, ViewState},
  statusevents::StatusEvented,
  topic::{Topic, TopicDescription, TopicKind},
  traits::{Key, Keyed, RTPSEntity},
  typedesc::TypeDesc,
  with_key::{WriteOptions, WriteOptionsBuilder},
};
/// Needed to specify serialized data representation in case it is other than
/// CDR.
pub use messages::submessages::submessage_elements::serialized_payload::RepresentationIdentifier;
#[doc(inline)]
pub use serialization::{
  CDRDeserializerAdapter, CDRSerializerAdapter, CdrDeserializer, CdrSerializer,
};
pub use structure::{
  duration::Duration, guid::GUID, sequence_number::SequenceNumber, time::Timestamp,
};
// re-export from a helper crate
pub use cdr_encoding_size::CdrEncodingSize;

/// Components used to access NO_KEY Topics
pub mod no_key {
  pub use crate::dds::{no_key::*, traits::serde_adapters::no_key::*};
}

/// Components used to access WITH_KEY Topics
pub mod with_key {
  pub use crate::dds::{traits::serde_adapters::with_key::*, with_key::*};
}

pub mod rpc {
  pub use crate::structure::rpc::*;
}