openigtlink_rust/protocol/
header.rs1use crate::error::{IgtlError, Result};
6use bytes::{Buf, BufMut, BytesMut};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct TypeName([u8; 12]);
11
12impl TypeName {
13 pub fn new(name: &str) -> Result<Self> {
15 if name.len() > 12 {
16 return Err(IgtlError::InvalidHeader(format!(
17 "Type name too long: {} bytes (max: 12)",
18 name.len()
19 )));
20 }
21 let mut bytes = [0u8; 12];
22 bytes[..name.len()].copy_from_slice(name.as_bytes());
23 Ok(TypeName(bytes))
24 }
25
26 pub fn as_str(&self) -> Result<&str> {
28 let len = self.0.iter().position(|&b| b == 0).unwrap_or(12);
29 std::str::from_utf8(&self.0[..len])
30 .map_err(|_| IgtlError::InvalidHeader("Invalid UTF-8 in type name".to_string()))
31 }
32}
33
34impl From<[u8; 12]> for TypeName {
35 fn from(bytes: [u8; 12]) -> Self {
36 TypeName(bytes)
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct DeviceName([u8; 20]);
43
44impl DeviceName {
45 pub fn new(name: &str) -> Result<Self> {
47 if name.len() > 20 {
48 return Err(IgtlError::InvalidHeader(format!(
49 "Device name too long: {} bytes (max: 20)",
50 name.len()
51 )));
52 }
53 let mut bytes = [0u8; 20];
54 bytes[..name.len()].copy_from_slice(name.as_bytes());
55 Ok(DeviceName(bytes))
56 }
57
58 pub fn as_str(&self) -> Result<&str> {
60 let len = self.0.iter().position(|&b| b == 0).unwrap_or(20);
61 std::str::from_utf8(&self.0[..len])
62 .map_err(|_| IgtlError::InvalidHeader("Invalid UTF-8 in device name".to_string()))
63 }
64}
65
66impl From<[u8; 20]> for DeviceName {
67 fn from(bytes: [u8; 20]) -> Self {
68 DeviceName(bytes)
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub struct Timestamp {
82 pub seconds: u32,
84 pub fraction: u32,
86}
87
88impl Timestamp {
89 pub fn new(seconds: u32, fraction: u32) -> Self {
95 Timestamp { seconds, fraction }
96 }
97
98 pub fn now() -> Self {
109 let now = std::time::SystemTime::now()
110 .duration_since(std::time::UNIX_EPOCH)
111 .unwrap();
112
113 let seconds = now.as_secs() as u32;
114 let nanos = now.subsec_nanos();
116 let fraction = ((nanos as u64) * 0x1_0000_0000 / 1_000_000_000) as u32;
117
118 Timestamp { seconds, fraction }
119 }
120
121 pub fn zero() -> Self {
132 Timestamp {
133 seconds: 0,
134 fraction: 0,
135 }
136 }
137
138 pub fn to_u64(self) -> u64 {
142 ((self.seconds as u64) << 32) | (self.fraction as u64)
143 }
144
145 pub fn from_u64(value: u64) -> Self {
149 Timestamp {
150 seconds: (value >> 32) as u32,
151 fraction: (value & 0xFFFFFFFF) as u32,
152 }
153 }
154
155 pub fn to_nanos(self) -> u64 {
166 let sec_nanos = (self.seconds as u64) * 1_000_000_000;
167 let frac_nanos = ((self.fraction as u64) * 1_000_000_000) / 0x1_0000_0000;
168 sec_nanos + frac_nanos
169 }
170
171 pub fn from_nanos(nanos: u64) -> Self {
184 let seconds = (nanos / 1_000_000_000) as u32;
185 let remaining_nanos = (nanos % 1_000_000_000) as u32;
186 let fraction = ((remaining_nanos as u64) * 0x1_0000_0000 / 1_000_000_000) as u32;
187
188 Timestamp { seconds, fraction }
189 }
190
191 pub fn to_f64(self) -> f64 {
202 let frac_f64 = (self.fraction as f64) / (u32::MAX as f64 + 1.0);
203 (self.seconds as f64) + frac_f64
204 }
205}
206
207#[derive(Debug, Clone)]
217pub struct Header {
218 pub version: u16,
220 pub type_name: TypeName,
222 pub device_name: DeviceName,
224 pub timestamp: Timestamp,
226 pub body_size: u64,
228 pub crc: u64,
230}
231
232impl Header {
233 pub const SIZE: usize = 58;
235
236 pub fn decode(buf: &[u8]) -> Result<Self> {
244 if buf.len() < Self::SIZE {
245 return Err(IgtlError::InvalidSize {
246 expected: Self::SIZE,
247 actual: buf.len(),
248 });
249 }
250
251 let mut cursor = std::io::Cursor::new(buf);
252
253 let version = cursor.get_u16();
255
256 let mut type_bytes = [0u8; 12];
258 cursor.copy_to_slice(&mut type_bytes);
259 let type_name = TypeName::from(type_bytes);
260
261 let mut device_bytes = [0u8; 20];
263 cursor.copy_to_slice(&mut device_bytes);
264 let device_name = DeviceName::from(device_bytes);
265
266 let timestamp_u64 = cursor.get_u64();
268 let timestamp = Timestamp::from_u64(timestamp_u64);
269
270 let body_size = cursor.get_u64();
272
273 let crc = cursor.get_u64();
275
276 Ok(Header {
277 version,
278 type_name,
279 device_name,
280 timestamp,
281 body_size,
282 crc,
283 })
284 }
285
286 pub fn encode(&self) -> Vec<u8> {
291 let mut buf = BytesMut::with_capacity(Self::SIZE);
292
293 buf.put_u16(self.version);
295
296 buf.put_slice(&self.type_name.0);
298
299 buf.put_slice(&self.device_name.0);
301
302 buf.put_u64(self.timestamp.to_u64());
304
305 buf.put_u64(self.body_size);
307
308 buf.put_u64(self.crc);
310
311 buf.to_vec()
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn test_type_name_creation() {
321 let name = TypeName::new("TRANSFORM").unwrap();
322 assert_eq!(name.as_str().unwrap(), "TRANSFORM");
323 }
324
325 #[test]
326 fn test_type_name_too_long() {
327 let result = TypeName::new("VERY_LONG_TYPE_NAME");
328 assert!(result.is_err());
329 }
330
331 #[test]
332 fn test_device_name_creation() {
333 let name = DeviceName::new("TestDevice").unwrap();
334 assert_eq!(name.as_str().unwrap(), "TestDevice");
335 }
336
337 #[test]
338 fn test_header_size() {
339 assert_eq!(Header::SIZE, 58);
340 }
341
342 #[test]
343 fn test_timestamp_now() {
344 let ts = Timestamp::now();
345 assert!(ts.seconds > 0);
346 assert!(ts.to_f64() > 0.0);
347 }
348
349 #[test]
350 fn test_timestamp_zero() {
351 let ts = Timestamp::zero();
352 assert_eq!(ts.seconds, 0);
353 assert_eq!(ts.fraction, 0);
354 assert_eq!(ts.to_u64(), 0);
355 }
356
357 #[test]
358 fn test_timestamp_conversion() {
359 let ts = Timestamp::new(1000, 0x80000000); assert_eq!(ts.seconds, 1000);
361 assert_eq!(ts.fraction, 0x80000000);
362
363 let nanos = ts.to_nanos();
365 assert_eq!(nanos, 1_000_500_000_000);
366
367 let ts2 = Timestamp::from_nanos(nanos);
369 assert_eq!(ts2.seconds, ts.seconds);
370 assert!((ts2.fraction as i64 - ts.fraction as i64).abs() < 100);
372
373 let f = ts.to_f64();
375 assert!((f - 1000.5).abs() < 0.0001);
376 }
377
378 #[test]
379 fn test_timestamp_u64_roundtrip() {
380 let original = Timestamp::new(1234567890, 0xABCDEF12);
381 let u64_val = original.to_u64();
382 let restored = Timestamp::from_u64(u64_val);
383
384 assert_eq!(restored.seconds, original.seconds);
385 assert_eq!(restored.fraction, original.fraction);
386 }
387
388 #[test]
389 fn test_header_roundtrip() {
390 let original = Header {
391 version: 2,
392 type_name: TypeName::new("TRANSFORM").unwrap(),
393 device_name: DeviceName::new("TestDevice").unwrap(),
394 timestamp: Timestamp::new(1234567890, 0x12345678),
395 body_size: 48,
396 crc: 0xDEADBEEFCAFEBABE,
397 };
398
399 let encoded = original.encode();
400 assert_eq!(encoded.len(), Header::SIZE);
401
402 let decoded = Header::decode(&encoded).unwrap();
403 assert_eq!(decoded.version, original.version);
404 assert_eq!(decoded.type_name, original.type_name);
405 assert_eq!(decoded.device_name, original.device_name);
406 assert_eq!(decoded.timestamp, original.timestamp);
407 assert_eq!(decoded.body_size, original.body_size);
408 assert_eq!(decoded.crc, original.crc);
409 }
410
411 #[test]
412 fn test_header_decode_short_buffer() {
413 let short_buf = vec![0u8; 30];
414 let result = Header::decode(&short_buf);
415 assert!(matches!(result, Err(IgtlError::InvalidSize { .. })));
416 }
417
418 #[test]
419 fn test_big_endian_encoding() {
420 let header = Header {
421 version: 0x0102,
422 type_name: TypeName::new("TEST").unwrap(),
423 device_name: DeviceName::new("DEV").unwrap(),
424 timestamp: Timestamp::from_u64(0x0102030405060708),
425 body_size: 0x090A0B0C0D0E0F10,
426 crc: 0x1112131415161718,
427 };
428
429 let encoded = header.encode();
430
431 assert_eq!(encoded[0], 0x01);
433 assert_eq!(encoded[1], 0x02);
434
435 assert_eq!(encoded[34], 0x01);
437 assert_eq!(encoded[35], 0x02);
438 assert_eq!(encoded[36], 0x03);
439 assert_eq!(encoded[37], 0x04);
440 }
441}