Skip to main content

hdds_micro/core/
participant.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2// Copyright (c) 2025-2026 naskel.com
3
4//! MicroParticipant - DDS Participant for embedded
5
6use crate::error::Result;
7use crate::rtps::{EntityId, GuidPrefix, Locator, GUID};
8use crate::transport::Transport;
9
10/// MicroParticipant - DDS Participant
11///
12/// Represents a DDS participant on an embedded device.
13///
14/// # Design
15///
16/// - Single-threaded (no async, no locks)
17/// - Fixed number of readers/writers (compile-time limit)
18/// - BEST_EFFORT QoS only (no reliability, no history)
19///
20/// # Example
21///
22/// ```ignore
23/// let mut transport = NullTransport::default();
24/// let participant = MicroParticipant::new(0, transport)?;
25/// ```
26pub struct MicroParticipant<T: Transport> {
27    /// Domain ID
28    domain_id: u32,
29
30    /// GUID prefix
31    guid_prefix: GuidPrefix,
32
33    /// Transport
34    transport: T,
35
36    /// Next entity ID counter (for creating readers/writers)
37    next_entity_id: u32,
38}
39
40impl<T: Transport> MicroParticipant<T> {
41    /// Create a new participant
42    ///
43    /// # Arguments
44    ///
45    /// * `domain_id` - DDS domain ID (0-232)
46    /// * `transport` - Transport implementation
47    pub fn new(domain_id: u32, mut transport: T) -> Result<Self> {
48        // Initialize transport
49        transport.init()?;
50
51        // Generate GUID prefix from local locator
52        let local_locator = transport.local_locator();
53        let guid_prefix = Self::generate_guid_prefix(&local_locator);
54
55        Ok(Self {
56            domain_id,
57            guid_prefix,
58            transport,
59            next_entity_id: 1, // Start at 1 (0 is reserved)
60        })
61    }
62
63    /// Get domain ID
64    pub const fn domain_id(&self) -> u32 {
65        self.domain_id
66    }
67
68    /// Get GUID prefix
69    pub const fn guid_prefix(&self) -> GuidPrefix {
70        self.guid_prefix
71    }
72
73    /// Get GUID
74    pub const fn guid(&self) -> GUID {
75        GUID::new(self.guid_prefix, EntityId::PARTICIPANT)
76    }
77
78    /// Get local locator
79    pub fn local_locator(&self) -> Locator {
80        self.transport.local_locator()
81    }
82
83    /// Allocate next entity ID
84    pub fn allocate_entity_id(&mut self, is_writer: bool) -> EntityId {
85        let entity_key = self.next_entity_id;
86        self.next_entity_id += 1;
87
88        // Build entity ID (simplified)
89        let kind = if is_writer { 0xc2 } else { 0xc7 }; // Writer or Reader
90
91        EntityId::new([
92            ((entity_key >> 16) & 0xff) as u8,
93            ((entity_key >> 8) & 0xff) as u8,
94            (entity_key & 0xff) as u8,
95            kind,
96        ])
97    }
98
99    /// Get transport (mutable)
100    pub fn transport_mut(&mut self) -> &mut T {
101        &mut self.transport
102    }
103
104    /// Get transport (immutable)
105    pub fn transport(&self) -> &T {
106        &self.transport
107    }
108
109    /// Shutdown participant
110    pub fn shutdown(mut self) -> Result<()> {
111        self.transport.shutdown()
112    }
113
114    /// Generate GUID prefix from locator
115    ///
116    /// Uses last 12 bytes of locator (address + port) for uniqueness.
117    fn generate_guid_prefix(locator: &Locator) -> GuidPrefix {
118        let mut bytes = [0u8; 12];
119
120        // Use address (16 bytes) -> take last 8 bytes
121        bytes[0..8].copy_from_slice(&locator.address[8..16]);
122
123        // Use port (4 bytes)
124        let port_bytes = locator.port.to_be_bytes();
125        bytes[8..12].copy_from_slice(&port_bytes);
126
127        GuidPrefix::new(bytes)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134    use crate::transport::NullTransport;
135
136    #[test]
137    fn test_participant_creation() {
138        let transport = NullTransport::default();
139        let participant = MicroParticipant::new(0, transport).unwrap();
140
141        assert_eq!(participant.domain_id(), 0);
142        assert_ne!(participant.guid_prefix(), GuidPrefix::UNKNOWN);
143    }
144
145    #[test]
146    fn test_entity_id_allocation() {
147        let transport = NullTransport::default();
148        let mut participant = MicroParticipant::new(0, transport).unwrap();
149
150        let writer_id = participant.allocate_entity_id(true);
151        let reader_id = participant.allocate_entity_id(false);
152
153        assert!(writer_id.is_writer());
154        assert!(reader_id.is_reader());
155        assert_ne!(writer_id, reader_id);
156    }
157}