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