openigtlink_rust/protocol/types/
tdata.rs1use crate::protocol::message::Message;
84use crate::error::{IgtlError, Result};
85use bytes::{Buf, BufMut};
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89#[repr(u8)]
90pub enum TrackingInstrumentType {
91 Tracker = 1,
92 Instrument6D = 2,
93 Instrument3D = 3,
94 Instrument5D = 4,
95}
96
97impl TrackingInstrumentType {
98 fn from_u8(value: u8) -> Result<Self> {
99 match value {
100 1 => Ok(TrackingInstrumentType::Tracker),
101 2 => Ok(TrackingInstrumentType::Instrument6D),
102 3 => Ok(TrackingInstrumentType::Instrument3D),
103 4 => Ok(TrackingInstrumentType::Instrument5D),
104 _ => Err(IgtlError::InvalidHeader(format!(
105 "Invalid tracking instrument type: {}",
106 value
107 ))),
108 }
109 }
110}
111
112#[derive(Debug, Clone, PartialEq)]
114pub struct TrackingDataElement {
115 pub name: String,
117 pub instrument_type: TrackingInstrumentType,
119 pub matrix: [[f32; 4]; 3],
122}
123
124impl TrackingDataElement {
125 pub fn new(
127 name: impl Into<String>,
128 instrument_type: TrackingInstrumentType,
129 matrix: [[f32; 4]; 3],
130 ) -> Self {
131 TrackingDataElement {
132 name: name.into(),
133 instrument_type,
134 matrix,
135 }
136 }
137
138 pub fn identity(name: impl Into<String>, instrument_type: TrackingInstrumentType) -> Self {
140 TrackingDataElement {
141 name: name.into(),
142 instrument_type,
143 matrix: [
144 [1.0, 0.0, 0.0, 0.0],
145 [0.0, 1.0, 0.0, 0.0],
146 [0.0, 0.0, 1.0, 0.0],
147 ],
148 }
149 }
150
151 pub fn with_translation(
153 name: impl Into<String>,
154 instrument_type: TrackingInstrumentType,
155 x: f32,
156 y: f32,
157 z: f32,
158 ) -> Self {
159 TrackingDataElement {
160 name: name.into(),
161 instrument_type,
162 matrix: [
163 [1.0, 0.0, 0.0, x],
164 [0.0, 1.0, 0.0, y],
165 [0.0, 0.0, 1.0, z],
166 ],
167 }
168 }
169}
170
171#[derive(Debug, Clone, PartialEq)]
178pub struct TDataMessage {
179 pub elements: Vec<TrackingDataElement>,
181}
182
183impl TDataMessage {
184 pub fn new(elements: Vec<TrackingDataElement>) -> Self {
186 TDataMessage { elements }
187 }
188
189 pub fn empty() -> Self {
191 TDataMessage {
192 elements: Vec::new(),
193 }
194 }
195
196 pub fn add_element(&mut self, element: TrackingDataElement) {
198 self.elements.push(element);
199 }
200
201 pub fn len(&self) -> usize {
203 self.elements.len()
204 }
205
206 pub fn is_empty(&self) -> bool {
208 self.elements.is_empty()
209 }
210}
211
212impl Message for TDataMessage {
213 fn message_type() -> &'static str {
214 "TDATA"
215 }
216
217 fn encode_content(&self) -> Result<Vec<u8>> {
218 let mut buf = Vec::with_capacity(self.elements.len() * 70);
219
220 for element in &self.elements {
221 let mut name_bytes = [0u8; 20];
223 let name_str = element.name.as_bytes();
224 let copy_len = name_str.len().min(19);
225 name_bytes[..copy_len].copy_from_slice(&name_str[..copy_len]);
226 buf.extend_from_slice(&name_bytes);
227
228 buf.put_u8(element.instrument_type as u8);
230
231 buf.put_u8(0);
233
234 for row in &element.matrix {
236 for &val in row {
237 buf.put_f32(val);
238 }
239 }
240 }
241
242 Ok(buf)
243 }
244
245 fn decode_content(mut data: &[u8]) -> Result<Self> {
246 let mut elements = Vec::new();
247
248 while data.len() >= 70 {
249 let name_bytes = &data[..20];
251 data.advance(20);
252
253 let name_len = name_bytes.iter().position(|&b| b == 0).unwrap_or(20);
254 let name = String::from_utf8(name_bytes[..name_len].to_vec())?;
255
256 let instrument_type = TrackingInstrumentType::from_u8(data.get_u8())?;
258
259 let _reserved = data.get_u8();
261
262 let mut matrix = [[0.0f32; 4]; 3];
264 for row in &mut matrix {
265 for val in row {
266 *val = data.get_f32();
267 }
268 }
269
270 elements.push(TrackingDataElement {
271 name,
272 instrument_type,
273 matrix,
274 });
275 }
276
277 if !data.is_empty() {
278 return Err(IgtlError::InvalidSize {
279 expected: 0,
280 actual: data.len(),
281 });
282 }
283
284 Ok(TDataMessage { elements })
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291
292 #[test]
293 fn test_message_type() {
294 assert_eq!(TDataMessage::message_type(), "TDATA");
295 }
296
297 #[test]
298 fn test_instrument_type() {
299 assert_eq!(TrackingInstrumentType::Tracker as u8, 1);
300 assert_eq!(TrackingInstrumentType::Instrument6D as u8, 2);
301 assert_eq!(TrackingInstrumentType::Instrument3D as u8, 3);
302 assert_eq!(TrackingInstrumentType::Instrument5D as u8, 4);
303 }
304
305 #[test]
306 fn test_empty() {
307 let msg = TDataMessage::empty();
308 assert!(msg.is_empty());
309 assert_eq!(msg.len(), 0);
310 }
311
312 #[test]
313 fn test_identity() {
314 let elem = TrackingDataElement::identity("Tool1", TrackingInstrumentType::Instrument6D);
315 assert_eq!(elem.matrix[0], [1.0, 0.0, 0.0, 0.0]);
316 assert_eq!(elem.matrix[1], [0.0, 1.0, 0.0, 0.0]);
317 assert_eq!(elem.matrix[2], [0.0, 0.0, 1.0, 0.0]);
318 }
319
320 #[test]
321 fn test_with_translation() {
322 let elem = TrackingDataElement::with_translation(
323 "Tool1",
324 TrackingInstrumentType::Tracker,
325 10.0,
326 20.0,
327 30.0,
328 );
329 assert_eq!(elem.matrix[0][3], 10.0);
330 assert_eq!(elem.matrix[1][3], 20.0);
331 assert_eq!(elem.matrix[2][3], 30.0);
332 }
333
334 #[test]
335 fn test_add_element() {
336 let mut msg = TDataMessage::empty();
337 msg.add_element(TrackingDataElement::identity(
338 "Tool1",
339 TrackingInstrumentType::Tracker,
340 ));
341 assert_eq!(msg.len(), 1);
342 }
343
344 #[test]
345 fn test_encode_single_element() {
346 let elem = TrackingDataElement::identity("Test", TrackingInstrumentType::Instrument6D);
347 let msg = TDataMessage::new(vec![elem]);
348 let encoded = msg.encode_content().unwrap();
349
350 assert_eq!(encoded.len(), 70);
351 assert_eq!(encoded[20], 2); assert_eq!(encoded[21], 0);
355 }
356
357 #[test]
358 fn test_roundtrip_single() {
359 let matrix = [
360 [1.0, 0.0, 0.0, 10.0],
361 [0.0, 1.0, 0.0, 20.0],
362 [0.0, 0.0, 1.0, 30.0],
363 ];
364
365 let original = TDataMessage::new(vec![TrackingDataElement::new(
366 "Tracker1",
367 TrackingInstrumentType::Tracker,
368 matrix,
369 )]);
370
371 let encoded = original.encode_content().unwrap();
372 let decoded = TDataMessage::decode_content(&encoded).unwrap();
373
374 assert_eq!(decoded.elements.len(), 1);
375 assert_eq!(decoded.elements[0].name, "Tracker1");
376 assert_eq!(
377 decoded.elements[0].instrument_type,
378 TrackingInstrumentType::Tracker
379 );
380 assert_eq!(decoded.elements[0].matrix, matrix);
381 }
382
383 #[test]
384 fn test_roundtrip_multiple() {
385 let original = TDataMessage::new(vec![
386 TrackingDataElement::identity("Tool1", TrackingInstrumentType::Instrument6D),
387 TrackingDataElement::with_translation(
388 "Tool2",
389 TrackingInstrumentType::Instrument3D,
390 5.0,
391 10.0,
392 15.0,
393 ),
394 ]);
395
396 let encoded = original.encode_content().unwrap();
397 let decoded = TDataMessage::decode_content(&encoded).unwrap();
398
399 assert_eq!(decoded.elements.len(), 2);
400 assert_eq!(decoded.elements[0].name, "Tool1");
401 assert_eq!(decoded.elements[1].name, "Tool2");
402 }
403
404 #[test]
405 fn test_name_truncation() {
406 let long_name = "ThisIsAVeryLongNameThatExceedsTwentyCharacters";
407 let elem = TrackingDataElement::identity(long_name, TrackingInstrumentType::Tracker);
408 let msg = TDataMessage::new(vec![elem]);
409
410 let encoded = msg.encode_content().unwrap();
411 let decoded = TDataMessage::decode_content(&encoded).unwrap();
412
413 assert!(decoded.elements[0].name.len() <= 19);
414 }
415
416 #[test]
417 fn test_empty_message() {
418 let msg = TDataMessage::empty();
419 let encoded = msg.encode_content().unwrap();
420 let decoded = TDataMessage::decode_content(&encoded).unwrap();
421
422 assert_eq!(decoded.elements.len(), 0);
423 assert_eq!(encoded.len(), 0);
424 }
425
426 #[test]
427 fn test_decode_invalid_size() {
428 let data = vec![0u8; 69]; let result = TDataMessage::decode_content(&data);
430 assert!(result.is_err());
431 }
432}