Skip to main content

hdds_micro/rtps/
types.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2// Copyright (c) 2025-2026 naskel.com
3
4//! RTPS Lite types
5
6use core::fmt;
7
8/// RTPS Protocol version (2.5)
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct ProtocolVersion {
11    /// Major version
12    pub major: u8,
13    /// Minor version
14    pub minor: u8,
15}
16
17impl ProtocolVersion {
18    /// RTPS v2.5
19    pub const RTPS_2_5: Self = Self { major: 2, minor: 5 };
20
21    /// Create a new protocol version
22    pub const fn new(major: u8, minor: u8) -> Self {
23        Self { major, minor }
24    }
25}
26
27impl Default for ProtocolVersion {
28    fn default() -> Self {
29        Self::RTPS_2_5
30    }
31}
32
33/// Vendor ID (assigned by OMG)
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct VendorId(pub [u8; 2]);
36
37impl VendorId {
38    /// HDDS vendor ID (unofficial, for testing)
39    pub const HDDS: Self = Self([0x01, 0x14]); // 0x0114 = 276 (unofficial)
40
41    /// Create a new vendor ID
42    pub const fn new(id: [u8; 2]) -> Self {
43        Self(id)
44    }
45}
46
47impl Default for VendorId {
48    fn default() -> Self {
49        Self::HDDS
50    }
51}
52
53/// GUID Prefix (12 bytes)
54///
55/// Uniquely identifies a participant on the network.
56#[derive(Clone, Copy, PartialEq, Eq, Hash)]
57pub struct GuidPrefix(pub [u8; 12]);
58
59impl GuidPrefix {
60    /// Unknown GUID prefix
61    pub const UNKNOWN: Self = Self([0; 12]);
62
63    /// Create a new GUID prefix
64    pub const fn new(bytes: [u8; 12]) -> Self {
65        Self(bytes)
66    }
67
68    /// Get bytes
69    pub const fn as_bytes(&self) -> &[u8; 12] {
70        &self.0
71    }
72}
73
74impl Default for GuidPrefix {
75    fn default() -> Self {
76        Self::UNKNOWN
77    }
78}
79
80impl fmt::Debug for GuidPrefix {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        write!(f, "GuidPrefix(")?;
83        for (i, b) in self.0.iter().enumerate() {
84            if i > 0 {
85                write!(f, ":")?;
86            }
87            write!(f, "{:02x}", b)?;
88        }
89        write!(f, ")")
90    }
91}
92
93/// Entity ID (4 bytes)
94///
95/// Identifies a specific entity (reader/writer) within a participant.
96#[derive(Clone, Copy, PartialEq, Eq, Hash)]
97pub struct EntityId(pub [u8; 4]);
98
99impl EntityId {
100    /// Unknown entity
101    pub const UNKNOWN: Self = Self([0x00, 0x00, 0x00, 0x00]);
102
103    /// Built-in participant
104    pub const PARTICIPANT: Self = Self([0x00, 0x00, 0x01, 0xc1]);
105
106    /// Built-in SEDP publications writer
107    pub const SEDP_BUILTIN_PUBLICATIONS_WRITER: Self = Self([0x00, 0x00, 0x03, 0xc2]);
108
109    /// Built-in SEDP publications reader
110    pub const SEDP_BUILTIN_PUBLICATIONS_READER: Self = Self([0x00, 0x00, 0x03, 0xc7]);
111
112    /// Built-in SEDP subscriptions writer
113    pub const SEDP_BUILTIN_SUBSCRIPTIONS_WRITER: Self = Self([0x00, 0x00, 0x04, 0xc2]);
114
115    /// Built-in SEDP subscriptions reader
116    pub const SEDP_BUILTIN_SUBSCRIPTIONS_READER: Self = Self([0x00, 0x00, 0x04, 0xc7]);
117
118    /// Create a new entity ID
119    pub const fn new(bytes: [u8; 4]) -> Self {
120        Self(bytes)
121    }
122
123    /// Get bytes
124    pub const fn as_bytes(&self) -> &[u8; 4] {
125        &self.0
126    }
127
128    /// Check if this is a built-in entity
129    pub const fn is_builtin(&self) -> bool {
130        // Built-in entities have 0xc0 bit set in last byte
131        self.0[3] & 0xc0 == 0xc0
132    }
133
134    /// Check if this is a writer entity
135    pub const fn is_writer(&self) -> bool {
136        // Writers have 0x02 bit set in last byte
137        self.0[3] & 0x02 == 0x02
138    }
139
140    /// Check if this is a reader entity
141    pub const fn is_reader(&self) -> bool {
142        // Readers have 0x07 bit set in last byte
143        self.0[3] & 0x07 == 0x07
144    }
145}
146
147impl Default for EntityId {
148    fn default() -> Self {
149        Self::UNKNOWN
150    }
151}
152
153impl fmt::Debug for EntityId {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        write!(
156            f,
157            "EntityId({:02x}{:02x}{:02x}{:02x})",
158            self.0[0], self.0[1], self.0[2], self.0[3]
159        )
160    }
161}
162
163/// GUID (16 bytes) = GuidPrefix (12) + EntityId (4)
164#[derive(Clone, Copy, PartialEq, Eq, Hash)]
165pub struct GUID {
166    /// GUID prefix
167    pub prefix: GuidPrefix,
168    /// Entity ID
169    pub entity_id: EntityId,
170}
171
172impl GUID {
173    /// Unknown GUID
174    pub const UNKNOWN: Self = Self {
175        prefix: GuidPrefix::UNKNOWN,
176        entity_id: EntityId::UNKNOWN,
177    };
178
179    /// Create a new GUID
180    pub const fn new(prefix: GuidPrefix, entity_id: EntityId) -> Self {
181        Self { prefix, entity_id }
182    }
183
184    /// Convert to 16-byte array
185    pub fn to_bytes(&self) -> [u8; 16] {
186        let mut bytes = [0u8; 16];
187        bytes[0..12].copy_from_slice(&self.prefix.0);
188        bytes[12..16].copy_from_slice(&self.entity_id.0);
189        bytes
190    }
191
192    /// Create from 16-byte array
193    pub fn from_bytes(bytes: [u8; 16]) -> Self {
194        let mut prefix = [0u8; 12];
195        let mut entity_id = [0u8; 4];
196        prefix.copy_from_slice(&bytes[0..12]);
197        entity_id.copy_from_slice(&bytes[12..16]);
198        Self {
199            prefix: GuidPrefix(prefix),
200            entity_id: EntityId(entity_id),
201        }
202    }
203}
204
205impl Default for GUID {
206    fn default() -> Self {
207        Self::UNKNOWN
208    }
209}
210
211impl fmt::Debug for GUID {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        write!(f, "GUID({:?}:{:?})", self.prefix, self.entity_id)
214    }
215}
216
217/// Sequence Number (64-bit)
218///
219/// Monotonically increasing counter for samples.
220#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
221pub struct SequenceNumber(pub i64);
222
223impl SequenceNumber {
224    /// Unknown sequence number
225    pub const UNKNOWN: Self = Self(-1);
226
227    /// Minimum valid sequence number
228    pub const MIN: Self = Self(1);
229
230    /// Create a new sequence number
231    pub const fn new(value: i64) -> Self {
232        Self(value)
233    }
234
235    /// Get the value
236    pub const fn value(&self) -> i64 {
237        self.0
238    }
239
240    /// Increment by 1
241    pub fn increment(&mut self) {
242        self.0 = self.0.saturating_add(1);
243    }
244
245    /// Get next sequence number
246    pub const fn next(self) -> Self {
247        Self(self.0 + 1)
248    }
249}
250
251impl Default for SequenceNumber {
252    fn default() -> Self {
253        Self::MIN
254    }
255}
256
257impl fmt::Debug for SequenceNumber {
258    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
259        write!(f, "SequenceNumber({})", self.0)
260    }
261}
262
263impl fmt::Display for SequenceNumber {
264    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
265        write!(f, "{}", self.0)
266    }
267}
268
269/// Locator (24 bytes)
270///
271/// Network address for RTPS communication.
272#[derive(Clone, Copy, PartialEq, Eq)]
273pub struct Locator {
274    /// Locator kind (1 = UDPv4, 2 = UDPv6)
275    pub kind: i32,
276    /// Port number
277    pub port: u32,
278    /// IPv4/IPv6 address (16 bytes)
279    pub address: [u8; 16],
280}
281
282impl Locator {
283    /// Invalid locator
284    pub const INVALID: Self = Self {
285        kind: -1,
286        port: 0,
287        address: [0; 16],
288    };
289
290    /// UDPv4 locator kind
291    pub const KIND_UDPV4: i32 = 1;
292
293    /// UDPv6 locator kind
294    pub const KIND_UDPV6: i32 = 2;
295
296    /// Create a new UDPv4 locator
297    pub const fn udpv4(ip: [u8; 4], port: u16) -> Self {
298        let mut address = [0u8; 16];
299        // IPv4-mapped IPv6 address format: ::ffff:a.b.c.d
300        address[10] = 0xff;
301        address[11] = 0xff;
302        address[12] = ip[0];
303        address[13] = ip[1];
304        address[14] = ip[2];
305        address[15] = ip[3];
306
307        Self {
308            kind: Self::KIND_UDPV4,
309            port: port as u32,
310            address,
311        }
312    }
313
314    /// Check if locator is valid
315    pub const fn is_valid(&self) -> bool {
316        self.kind > 0 && self.port > 0
317    }
318}
319
320impl Default for Locator {
321    fn default() -> Self {
322        Self::INVALID
323    }
324}
325
326impl fmt::Debug for Locator {
327    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328        if self.kind == Self::KIND_UDPV4 {
329            write!(
330                f,
331                "Locator(UDPv4, {}.{}.{}.{}:{})",
332                self.address[12], self.address[13], self.address[14], self.address[15], self.port
333            )
334        } else if self.kind == Self::KIND_UDPV6 {
335            write!(f, "Locator(UDPv6, [...]:{})", self.port)
336        } else {
337            write!(f, "Locator(Invalid)")
338        }
339    }
340}
341
342#[cfg(test)]
343mod tests {
344    use super::*;
345
346    #[test]
347    fn test_protocol_version() {
348        let v = ProtocolVersion::RTPS_2_5;
349        assert_eq!(v.major, 2);
350        assert_eq!(v.minor, 5);
351    }
352
353    #[test]
354    fn test_guid_prefix() {
355        let prefix = GuidPrefix::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]);
356        assert_eq!(prefix.as_bytes()[0], 1);
357        assert_eq!(prefix.as_bytes()[11], 12);
358    }
359
360    #[test]
361    fn test_entity_id_builtin() {
362        assert!(EntityId::PARTICIPANT.is_builtin());
363        assert!(EntityId::SEDP_BUILTIN_PUBLICATIONS_WRITER.is_builtin());
364        assert!(!EntityId::UNKNOWN.is_builtin());
365    }
366
367    #[test]
368    fn test_entity_id_writer_reader() {
369        assert!(EntityId::SEDP_BUILTIN_PUBLICATIONS_WRITER.is_writer());
370        assert!(EntityId::SEDP_BUILTIN_PUBLICATIONS_READER.is_reader());
371    }
372
373    #[test]
374    fn test_guid_conversion() {
375        let guid = GUID::new(
376            GuidPrefix::new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
377            EntityId::new([13, 14, 15, 16]),
378        );
379
380        let bytes = guid.to_bytes();
381        assert_eq!(bytes[0], 1);
382        assert_eq!(bytes[11], 12);
383        assert_eq!(bytes[12], 13);
384        assert_eq!(bytes[15], 16);
385
386        let restored = GUID::from_bytes(bytes);
387        assert_eq!(restored, guid);
388    }
389
390    #[test]
391    fn test_sequence_number() {
392        let mut seq = SequenceNumber::new(1);
393        assert_eq!(seq.value(), 1);
394
395        seq.increment();
396        assert_eq!(seq.value(), 2);
397
398        let next = seq.next();
399        assert_eq!(next.value(), 3);
400        assert_eq!(seq.value(), 2); // Original unchanged
401    }
402
403    #[test]
404    fn test_locator_udpv4() {
405        let loc = Locator::udpv4([192, 168, 1, 100], 7400);
406        assert_eq!(loc.kind, Locator::KIND_UDPV4);
407        assert_eq!(loc.port, 7400);
408        assert_eq!(loc.address[12], 192);
409        assert_eq!(loc.address[13], 168);
410        assert_eq!(loc.address[14], 1);
411        assert_eq!(loc.address[15], 100);
412        assert!(loc.is_valid());
413    }
414
415    #[test]
416    fn test_locator_invalid() {
417        let loc = Locator::INVALID;
418        assert!(!loc.is_valid());
419    }
420}