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