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
- Un Participant par application - Sauf si vous avez besoin d'isolation par Domain
- Definir des noms significatifs - Aide au debogage et au monitoring
- Utiliser user data - Partager les infos de version, les capacites
- Gerer les evenements de decouverte - Savoir quand les pairs rejoignent/quittent
- 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
- Topics - Creer des canaux de donnees
- Publishers et Subscribers - Distribution des donnees
- Discovery - Comment les Participants se trouvent