Skip to main content

DomainParticipant

The DomainParticipant is your entry point to DDS communication and the factory for all other DDS entities.

Overview

A DomainParticipant:

  • Represents an application's presence in a DDS domain
  • Creates Publishers, Subscribers, and Topics
  • Manages discovery of other participants
  • Owns all child entities and their resources

Creating a Participant

use hdds::prelude::*;

// Join domain 0 (default)
let participant = DomainParticipant::new(0)?;

// With custom configuration
let config = DomainParticipantConfig::default()
.name("my_application")
.user_data(b"version=1.0".to_vec());

let participant = DomainParticipant::with_config(0, config)?;

Domain Isolation

Participants only communicate within the same domain:

Domain 0                          Domain 1
┌─────────────────────┐ ┌─────────────────────┐
│ Participant A │ │ Participant C │
│ Participant B │ │ Participant D │
│ ↕ │ │ ↕ │
│ (can communicate) │ ✗ │ (can communicate) │
└─────────────────────┘ └─────────────────────┘

Domain IDs map to network ports:

  • Domain 0: ports 7400-7410
  • Domain 1: ports 7411-7421
  • Domain N: ports 7400 + (N * 11)
// Different domains are isolated
let domain_0 = DomainParticipant::new(0)?;
let domain_1 = DomainParticipant::new(1)?;

// These writers cannot see each other's readers
let writer_0 = /* ... on domain_0 */;
let reader_1 = /* ... on domain_1 */; // No match

Entity Factory

The participant creates all DDS entities:

let participant = DomainParticipant::new(0)?;

// Create topic (type must be registered)
let topic = participant.create_topic::<SensorData>("SensorTopic")?;

// Create publisher and subscriber
let publisher = participant.create_publisher()?;
let subscriber = participant.create_subscriber()?;

// Create endpoints
let writer = publisher.create_writer(&topic)?;
let reader = subscriber.create_reader(&topic)?;

Entity Ownership

DomainParticipant (owner)
├── Topic "SensorTopic"
├── Publisher
│ └── DataWriter<SensorData>
└── Subscriber
└── DataReader<SensorData>

Deleting the participant deletes all child entities:

// All writers, readers, topics are cleaned up
drop(participant);

Participant GUID

Each participant has a globally unique identifier (GUID):

let guid = participant.guid();
println!("Participant GUID: {:?}", guid);
// Output: GUID { prefix: [01, 0f, aa, ...], entity_id: [00, 00, 01, c1] }

The GUID structure:

  • Prefix (12 bytes): Identifies the participant
  • Entity ID (4 bytes): Identifies the entity within participant

Discovery

Participants automatically discover each other via SPDP:

// Wait for other participants
loop {
let count = participant.discovered_participants().len();
println!("Found {} other participants", count);

if count > 0 {
break;
}
std::thread::sleep(Duration::from_millis(100));
}

Discovery Callbacks

use hdds::prelude::*;

struct MyListener;

impl DomainParticipantListener for MyListener {
fn on_participant_discovered(
&mut self,
participant: &DomainParticipant,
info: DiscoveredParticipantInfo,
) {
println!("New participant: {:?}", info.guid);
println!(" User data: {:?}", info.user_data);
}

fn on_participant_lost(
&mut self,
participant: &DomainParticipant,
guid: GUID,
) {
println!("Participant left: {:?}", guid);
}
}

let participant = DomainParticipant::with_listener(0, MyListener)?;

Liveliness

Participants assert their liveliness automatically:

// Manual assertion (if using ManualByParticipant)
participant.assert_liveliness()?;

Liveliness lease duration:

let config = DomainParticipantConfig::default()
.lease_duration(Duration::from_secs(30)); // 30-second lease

User Data

Attach application-specific metadata:

let config = DomainParticipantConfig::default()
.user_data(b"app=sensor_node;version=2.1".to_vec());

let participant = DomainParticipant::with_config(0, config)?;

// Other participants can read this
for info in participant.discovered_participants() {
if let Some(data) = &info.user_data {
println!("Peer user data: {}", String::from_utf8_lossy(data));
}
}

Configuration Options

let config = DomainParticipantConfig::default()
// Identity
.name("my_application")
.user_data(b"metadata".to_vec())

// Discovery
.lease_duration(Duration::from_secs(30))
.initial_peers(vec!["192.168.1.100:7400".parse()?])

// Transport
.transport(TransportConfig::default()
.enable_udp_multicast(true)
.enable_shared_memory(true))

// Security (if enabled)
.security(SecurityConfig::builder()
.identity_certificate("cert.pem")
.private_key("key.pem")
.build()?);

Multiple Participants

You can create multiple participants in one application:

// Join multiple domains
let domain_0 = DomainParticipant::new(0)?;
let domain_1 = DomainParticipant::new(1)?;

// Or multiple participants in same domain (less common)
let p1 = DomainParticipant::new(0)?;
let p2 = DomainParticipant::new(0)?; // Separate GUID

Lifecycle

// Create
let participant = DomainParticipant::new(0)?;

// Use (create entities, exchange data)
let topic = participant.create_topic::<Message>("Topic")?;
// ...

// Explicit cleanup (optional, happens on drop)
participant.delete_contained_entities()?;

// Participant is destroyed when dropped
drop(participant);

Error Handling

match DomainParticipant::new(0) {
Ok(p) => println!("Joined domain 0"),
Err(HddsError::InvalidDomainId(id)) => {
eprintln!("Domain {} is out of range (0-232)", id);
}
Err(HddsError::BindFailed { address, reason }) => {
eprintln!("Cannot bind to {}: {}", address, reason);
}
Err(e) => eprintln!("Failed: {}", e),
}

Best Practices

  1. One participant per application - Unless you need domain isolation
  2. Set meaningful names - Helps with debugging and monitoring
  3. Use user data - Share version info, capabilities
  4. Handle discovery events - Know when peers join/leave
  5. Clean shutdown - Drop participant to release resources

Thread Safety

DomainParticipant is thread-safe:

use std::sync::Arc;

let participant = Arc::new(DomainParticipant::new(0)?);

// Safe to use from multiple threads
let p1 = participant.clone();
std::thread::spawn(move || {
let writer = p1.create_publisher()?.create_writer(&topic)?;
// ...
});

Next Steps