A tour of the cyclonedds-rust API with short code snippets for each major feature area.
The entry point for any DDS application. Represents membership in a DDS domain.
use cyclonedds::DomainParticipant;
let participant = DomainParticipant::new(0)?; // domain 0From the participant you create Publishers, Subscribers, and Topics. Access the underlying entity handle with participant.entity().
A Topic associates a type with a name. The type must implement the DdsType trait.
use cyclonedds::{Topic, DdsType, adr, TYPE_4BY, OP_FLAG_SGN};
let topic: Topic<MyData> = Topic::new(&participant, "MyTopic")?;See type-system.md for how to implement DdsType manually or via derive macros.
Publishes typed samples. Create one from a Publisher and a Topic.
use cyclonedds::{Publisher, DataWriter};
let publisher = Publisher::new(&participant)?;
let writer: DataWriter<MyData> = DataWriter::new(&publisher, &topic)?;
writer.write(&sample)?; // publish
writer.write_dispose(&sample)?; // publish + dispose
writer.write_cdr(&cdr_bytes)?; // publish pre-serialized CDRRequest a loaned buffer from DDS, populate it, and write without copying:
let mut loan = writer.request_loan()?;
loan.get_mut().id = 42;
loan.get_mut().value = 99;
WriteLoan::write(loan)?; // transfers ownership to DDSReceives typed samples. Create one from a Subscriber and a Topic.
use cyclonedds::{Subscriber, DataReader};
let subscriber = Subscriber::new(&participant)?;
let reader: DataReader<MyData> = DataReader::new(&subscriber, &topic)?;
let samples: Vec<MyData> = reader.take()?; // removes from cache
let samples: Vec<MyData> = reader.read()?; // keeps in cacheAccess sample data without copying (loan is returned on drop):
let loan = reader.take_loan()?;
for (data, info) in loan.iter() {
println!("id={}, valid={}", data.id, info.valid_data);
}Read/take samples as raw CDR bytes for interoperability or custom deserialization:
let cdr_samples = reader.read_cdr()?;
let cdr_samples = reader.take_cdr()?;Use QosBuilder to construct a QoS object. Each method returns Self for chaining.
use cyclonedds::{QosBuilder, Reliability, Durability, History, Ownership};
// Reliable, transient-local, keep-last 10
let qos = QosBuilder::new()
.reliable()
.transient_local()
.keep_last(10)
.build()?;
// Exclusive ownership with strength 100
let qos = QosBuilder::new()
.ownership(Ownership::Exclusive)
.ownership_strength(100)
.build()?;Pass QoS when creating entities:
let writer = DataWriter::with_qos(&publisher, &topic, Some(&qos))?;
let reader = DataReader::with_qos(&subscriber, &topic, Some(&qos))?;See qos-reference.md for the full policy list.
Listeners provide event-driven callbacks. Build one with ListenerBuilder:
use cyclonedds::ListenerBuilder;
let listener = ListenerBuilder::new()
.on_data_available(|reader_entity| {
println!("Data available on reader {}", reader_entity);
})
.on_publication_matched(|writer_entity, status| {
println!("Matched {} subscriptions", status.current_count);
})
.build()?;Available callbacks: on_data_available, on_publication_matched, on_subscription_matched, on_liveliness_changed, on_inconsistent_topic, on_liveliness_lost, on_offered_deadline_missed, on_offered_incompatible_qos, on_data_on_readers, on_sample_lost, on_sample_rejected, on_requested_deadline_missed, on_requested_incompatible_qos.
Pass a listener when creating entities:
let reader = DataReader::with_listener(&subscriber, &topic, &listener)?;A WaitSet blocks until one or more attached conditions trigger.
use cyclonedds::{WaitSet, ReadCondition, GuardCondition};
let waitset = WaitSet::new(&participant)?;
let cond = ReadCondition::not_read(&reader)?;
let guard = GuardCondition::new(&participant)?;
waitset.attach(cond.entity(), 1)?;
waitset.attach(guard.entity(), 2)?;
loop {
let triggered = waitset.wait(1_000_000_000)?; // 1 second timeout
for cookie in triggered {
match cookie {
1 => { /* data available */ let _ = reader.take(); }
2 => { /* guard triggered */ }
_ => {}
}
}
}Filter samples with a Rust closure:
use cyclonedds::QueryCondition;
let qc = QueryCondition::with_filter(&reader, 0, |sample_ptr| {
// Return true to include the sample
true
})?;The StatusExt trait provides typed status getters on any entity. It is implemented for all DdsEntity types via a blanket impl.
use cyclonedds::StatusExt;
let status = writer.publication_matched_status()?;
println!("Matched: total={}, current={}", status.total_count, status.current_count);
let status = reader.subscription_matched_status()?;
println!("Subscriptions matched: {}", status.current_count);
let status = reader.liveliness_changed_status()?;
println!("Alive writers: {}", status.alive_count);Available: inconsistent_topic_status, liveliness_lost_status, liveliness_changed_status, offered_deadline_missed_status, offered_incompatible_qos_status, requested_deadline_missed_status, requested_incompatible_qos_status, sample_lost_status, sample_rejected_status, publication_matched_status, subscription_matched_status.
Serialize and deserialize DDS samples to/from CDR byte streams.
use cyclonedds::{CdrSerializer, CdrDeserializer, CdrEncoding};
// Serialize
let bytes = CdrSerializer::<MyData>::serialize(&sample, CdrEncoding::Xcdr1)?;
// Deserialize
let sample = CdrDeserializer::<MyData>::deserialize(&bytes, CdrEncoding::Xcdr2)?;Build types at runtime without compile-time struct definitions.
use cyclonedds::{DynamicTypeBuilder, DynamicTypeExtensibility, DynamicData, DynamicValue};
let mut builder = DynamicTypeBuilder::new(64, 4, DynamicTypeExtensibility::Appendable);
builder.add_member("id", DynamicPrimitiveKind::Int32);
builder.add_member("name", DynamicPrimitiveKind::String);
let dyn_type = builder.build();Enable the async feature (on by default) to use async/await with DDS operations.
[dependencies]
cyclonedds = "2.0" # async feature enabled by default
tokio = { version = "1", features = ["full"] }use cyclonedds::{DataReader, WaitSet};
// Async wait on a WaitSet
let triggered = waitset.wait_async(5_000_000_000).await?;
// Async take on a DataReader
let samples = reader.take_async().await?;The async methods offload blocking DDS calls to tokio's thread pool via spawn_blocking.