Aller au contenu principal

DomainParticipant

Le DomainParticipant est votre point d'entree vers la communication DDS et la fabrique de toutes les autres entites DDS.

Vue d'ensemble

Un DomainParticipant :

  • Represente la presence d'une application dans un Domain DDS
  • Cree des Publishers, Subscribers et Topics
  • Gere la decouverte des autres Participants
  • Possede toutes les entites enfants et leurs ressources

Creer un Participant

use hdds::{Participant, TransportMode};

// Join domain 0
let participant = Participant::builder("my_app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// With custom configuration
let participant = Participant::builder("my_application")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.participant_id(Some(5)) // Explicit participant ID
.build()?;

Isolation par Domain

Les Participants ne communiquent qu'au sein du meme Domain :

Domain 0                          Domain 1
+---------------------+ +---------------------+
| Participant A | | Participant C |
| Participant B | | Participant D |
| <-> | | <-> |
| (peuvent communiquer) | (peuvent communiquer)
+---------------------+ X +---------------------+

Les Domain IDs correspondent a des ports reseau :

  • Domain 0 : ports 7400-7410
  • Domain 1 : ports 7411-7421
  • Domain N : ports 7400 + (N * 11)
// Different domains are isolated
let domain_0 = Participant::builder("app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;
let domain_1 = Participant::builder("app")
.domain_id(1)
.with_transport(TransportMode::UdpMulticast)
.build()?;

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

Fabrique d'entites

Le Participant cree toutes les entites DDS :

let participant = Participant::builder("app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// 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)?;

Propriete des entites

DomainParticipant (proprietaire)
+-- Topic "SensorTopic"
+-- Publisher
| +-- DataWriter<SensorData>
+-- Subscriber
+-- DataReader<SensorData>

Supprimer le Participant supprime toutes les entites enfants :

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

GUID du Participant

Chaque Participant possede un identifiant globalement unique (GUID) :

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

La structure du GUID :

  • Prefix (12 octets) : Identifie le Participant
  • Entity ID (4 octets) : Identifie l'entite au sein du Participant

Discovery

Les Participants se decouvrent automatiquement 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));
}

Callbacks de decouverte

use hdds::{Participant, TransportMode};

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 = Participant::builder("app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.with_listener(MyListener)
.build()?;

Liveliness

Les Participants assertent automatiquement leur liveliness :

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

Duree du bail de liveliness :

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

User Data

Attachez des metadonnees specifiques a l'application :

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

let participant = Participant::builder("app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.user_data(b"app=sensor_node;version=2.1".to_vec())
.build()?;

// 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));
}
}

Options de configuration

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()?);

Participants multiples

Vous pouvez creer plusieurs Participants dans une seule application :

// Join multiple domains
let domain_0 = Participant::builder("app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;
let domain_1 = Participant::builder("app")
.domain_id(1)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// Or multiple participants in same domain (less common)
let p1 = Participant::builder("app1")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;
let p2 = Participant::builder("app2")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?; // Separate GUID

Cycle de vie

// Create
let participant = Participant::builder("app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?;

// 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);

Gestion d'erreurs

match Participant::builder("app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()
{
Ok(p) => println!("Joined domain 0"),
Err(hdds::Error::InvalidDomainId(id)) => {
eprintln!("Domain {} is out of range (0-232)", id);
}
Err(hdds::Error::BindFailed { address, reason }) => {
eprintln!("Cannot bind to {}: {}", address, reason);
}
Err(e) => eprintln!("Failed: {}", e),
}

Bonnes pratiques

  1. Un Participant par application - Sauf si vous avez besoin d'isolation par Domain
  2. Definir des noms significatifs - Aide au debogage et au monitoring
  3. Utiliser user data - Partager les infos de version, les capacites
  4. Gerer les evenements de decouverte - Savoir quand les pairs rejoignent/quittent
  5. Arret propre - Drop le Participant pour liberer les ressources

Thread Safety

Le DomainParticipant est thread-safe :

use std::sync::Arc;

let participant = Arc::new(Participant::builder("app")
.domain_id(0)
.with_transport(TransportMode::UdpMulticast)
.build()?);

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

Prochaines etapes