Aller au contenu principal

DomainParticipant

Le DomainParticipant est votre point d'entrée vers la communication DDS et la fabrique de toutes les autres entités DDS.

Vue d'ensemble

Un DomainParticipant :

  • Représente la presence d'une application dans un Domain DDS
  • Crée des Publishers, Subscribers et Topics
  • Gère la découverte des autres Participants
  • Possède toutes les entités enfants et leurs ressources

Créer 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 même Domain :

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

Les Domain IDs correspondent à des ports réseau :

  • 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'entités

Le Participant crée toutes les entités 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)?;

Propriété des entités

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

Supprimer le Participant supprime toutes les entités enfants :

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

GUID du Participant

Chaque Participant possède 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'entité au sein du Participant

Discovery

Les Participants se découvrent 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 découverte

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

Durée du bail de liveliness :

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

User Data

Attachez des metadonnees spécifiques à 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 créer 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

Cyclé 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. Définir des noms significatifs - Aide au débogage et au monitoring
  3. Utiliser user data - Partager les infos de version, les capacites
  4. Gérer les événements de découverte - Savoir quand les pairs rejoignent/quittent
  5. Arret propre - Drop le Participant pour libérer 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 étapes