openigtlink_rust/protocol/
any_message.rs

1//! Dynamic message dispatching for OpenIGTLink
2//!
3//! This module provides the `AnyMessage` enum which can hold any message type,
4//! allowing for runtime message type detection and handling.
5
6use crate::error::Result;
7use crate::protocol::header::Header;
8use crate::protocol::message::IgtlMessage;
9use crate::protocol::types::*;
10
11/// Enum holding any OpenIGTLink message type
12///
13/// This allows receiving messages without knowing the type at compile time.
14/// The message type is determined at runtime from the header's type_name field.
15///
16/// # Examples
17///
18/// ```no_run
19/// # use openigtlink_rust::io::builder::ClientBuilder;
20/// # use openigtlink_rust::protocol::AnyMessage;
21/// # fn example() -> Result<(), openigtlink_rust::error::IgtlError> {
22/// let mut client = ClientBuilder::new().tcp("127.0.0.1:18944").sync().build()?;
23///
24/// let msg = client.receive_any()?;
25/// match msg {
26///     AnyMessage::Transform(transform_msg) => {
27///         println!("Received transform from {}", transform_msg.header.device_name.as_str()?);
28///     }
29///     AnyMessage::Status(status_msg) => {
30///         println!("Status: {}", status_msg.content.status_string);
31///     }
32///     _ => println!("Other message type"),
33/// }
34/// # Ok(())
35/// # }
36/// ```
37#[derive(Debug)]
38pub enum AnyMessage {
39    /// TRANSFORM message
40    Transform(IgtlMessage<TransformMessage>),
41    /// STATUS message
42    Status(IgtlMessage<StatusMessage>),
43    /// CAPABILITY message
44    Capability(IgtlMessage<CapabilityMessage>),
45    /// IMAGE message
46    Image(IgtlMessage<ImageMessage>),
47    /// POSITION message
48    Position(IgtlMessage<PositionMessage>),
49    /// STRING message
50    String(IgtlMessage<StringMessage>),
51    /// QTDATA message (quaternion tracking data)
52    QtData(IgtlMessage<QtDataMessage>),
53    /// TDATA message (tracking data)
54    TData(IgtlMessage<TDataMessage>),
55    /// SENSOR message
56    Sensor(IgtlMessage<SensorMessage>),
57    /// POINT message
58    Point(IgtlMessage<PointMessage>),
59    /// TRAJECTORY message
60    Trajectory(IgtlMessage<TrajectoryMessage>),
61    /// NDARRAY message (n-dimensional array)
62    NdArray(IgtlMessage<NdArrayMessage>),
63    /// BIND message
64    Bind(IgtlMessage<BindMessage>),
65    /// COLORTABLE message
66    ColorTable(IgtlMessage<ColorTableMessage>),
67    /// IMGMETA message (image metadata)
68    ImgMeta(IgtlMessage<ImgMetaMessage>),
69    /// LBMETA message (label metadata)
70    LbMeta(IgtlMessage<LbMetaMessage>),
71    /// POLYDATA message
72    PolyData(IgtlMessage<PolyDataMessage>),
73    /// VIDEO message
74    Video(IgtlMessage<VideoMessage>),
75    /// VIDEOMETA message
76    VideoMeta(IgtlMessage<VideoMetaMessage>),
77    /// COMMAND message
78    Command(IgtlMessage<CommandMessage>),
79
80    // Query messages (GET_*)
81    /// GET_TRANSFORM query message
82    GetTransform(IgtlMessage<GetTransformMessage>),
83    /// GET_STATUS query message
84    GetStatus(IgtlMessage<GetStatusMessage>),
85    /// GET_CAPABILITY query message
86    GetCapability(IgtlMessage<GetCapabilityMessage>),
87    /// GET_IMAGE query message
88    GetImage(IgtlMessage<GetImageMessage>),
89    /// GET_IMGMETA query message
90    GetImgMeta(IgtlMessage<GetImgMetaMessage>),
91    /// GET_LBMETA query message
92    GetLbMeta(IgtlMessage<GetLbMetaMessage>),
93    /// GET_POINT query message
94    GetPoint(IgtlMessage<GetPointMessage>),
95    /// GET_TDATA query message
96    GetTData(IgtlMessage<GetTDataMessage>),
97
98    // Response messages (RTS_*)
99    /// RTS_TRANSFORM response message
100    RtsTransform(IgtlMessage<RtsTransformMessage>),
101    /// RTS_STATUS response message
102    RtsStatus(IgtlMessage<RtsStatusMessage>),
103    /// RTS_CAPABILITY response message
104    RtsCapability(IgtlMessage<RtsCapabilityMessage>),
105    /// RTS_IMAGE response message
106    RtsImage(IgtlMessage<RtsImageMessage>),
107    /// RTS_TDATA response message
108    RtsTData(IgtlMessage<RtsTDataMessage>),
109
110    // Streaming control messages (STT_*, STP_*)
111    /// STT_TDATA start streaming message
112    StartTData(IgtlMessage<StartTDataMessage>),
113    /// STP_TRANSFORM stop streaming message
114    StopTransform(IgtlMessage<StopTransformMessage>),
115    /// STP_POSITION stop streaming message
116    StopPosition(IgtlMessage<StopPositionMessage>),
117    /// STP_QTDATA stop streaming message
118    StopQtData(IgtlMessage<StopQtDataMessage>),
119    /// STP_TDATA stop streaming message
120    StopTData(IgtlMessage<StopTDataMessage>),
121    /// STP_IMAGE stop streaming message
122    StopImage(IgtlMessage<StopImageMessage>),
123    /// STP_NDARRAY stop streaming message
124    StopNdArray(IgtlMessage<StopNdArrayMessage>),
125
126    /// Unknown message type (unrecognized or custom message)
127    ///
128    /// Contains the header and raw body bytes for manual processing.
129    Unknown {
130        /// Message header
131        header: Header,
132        /// Raw message body bytes
133        body: Vec<u8>,
134    },
135}
136
137impl AnyMessage {
138    /// Get the message type name as a string
139    ///
140    /// # Examples
141    ///
142    /// ```no_run
143    /// # use openigtlink_rust::protocol::AnyMessage;
144    /// # use openigtlink_rust::protocol::types::TransformMessage;
145    /// # use openigtlink_rust::protocol::message::IgtlMessage;
146    /// # fn example() -> Result<(), openigtlink_rust::error::IgtlError> {
147    /// # let transform = TransformMessage::identity();
148    /// # let msg = IgtlMessage::new(transform, "Device")?;
149    /// # let any_msg = AnyMessage::Transform(msg);
150    /// assert_eq!(any_msg.message_type(), "TRANSFORM");
151    /// # Ok(())
152    /// # }
153    /// ```
154    pub fn message_type(&self) -> &str {
155        match self {
156            AnyMessage::Transform(_) => "TRANSFORM",
157            AnyMessage::Status(_) => "STATUS",
158            AnyMessage::Capability(_) => "CAPABILITY",
159            AnyMessage::Image(_) => "IMAGE",
160            AnyMessage::Position(_) => "POSITION",
161            AnyMessage::String(_) => "STRING",
162            AnyMessage::QtData(_) => "QTDATA",
163            AnyMessage::TData(_) => "TDATA",
164            AnyMessage::Sensor(_) => "SENSOR",
165            AnyMessage::Point(_) => "POINT",
166            AnyMessage::Trajectory(_) => "TRAJECTORY",
167            AnyMessage::NdArray(_) => "NDARRAY",
168            AnyMessage::Bind(_) => "BIND",
169            AnyMessage::ColorTable(_) => "COLORTABLE",
170            AnyMessage::ImgMeta(_) => "IMGMETA",
171            AnyMessage::LbMeta(_) => "LBMETA",
172            AnyMessage::PolyData(_) => "POLYDATA",
173            AnyMessage::Video(_) => "VIDEO",
174            AnyMessage::VideoMeta(_) => "VIDEOMETA",
175            AnyMessage::Command(_) => "COMMAND",
176            AnyMessage::GetTransform(_) => "GET_TRANSFORM",
177            AnyMessage::GetStatus(_) => "GET_STATUS",
178            AnyMessage::GetCapability(_) => "GET_CAPABILITY",
179            AnyMessage::GetImage(_) => "GET_IMAGE",
180            AnyMessage::GetImgMeta(_) => "GET_IMGMETA",
181            AnyMessage::GetLbMeta(_) => "GET_LBMETA",
182            AnyMessage::GetPoint(_) => "GET_POINT",
183            AnyMessage::GetTData(_) => "GET_TDATA",
184            AnyMessage::RtsTransform(_) => "RTS_TRANSFORM",
185            AnyMessage::RtsStatus(_) => "RTS_STATUS",
186            AnyMessage::RtsCapability(_) => "RTS_CAPABILITY",
187            AnyMessage::RtsImage(_) => "RTS_IMAGE",
188            AnyMessage::RtsTData(_) => "RTS_TDATA",
189            AnyMessage::StartTData(_) => "STT_TDATA",
190            AnyMessage::StopTransform(_) => "STP_TRANSFORM",
191            AnyMessage::StopPosition(_) => "STP_POSITION",
192            AnyMessage::StopQtData(_) => "STP_QTDATA",
193            AnyMessage::StopTData(_) => "STP_TDATA",
194            AnyMessage::StopImage(_) => "STP_IMAGE",
195            AnyMessage::StopNdArray(_) => "STP_NDARRAY",
196            AnyMessage::Unknown { header, .. } => header.type_name.as_str().unwrap_or("UNKNOWN"),
197        }
198    }
199
200    /// Get the device name from the message header
201    ///
202    /// # Examples
203    ///
204    /// ```no_run
205    /// # use openigtlink_rust::protocol::AnyMessage;
206    /// # use openigtlink_rust::protocol::types::TransformMessage;
207    /// # use openigtlink_rust::protocol::message::IgtlMessage;
208    /// # fn example() -> Result<(), openigtlink_rust::error::IgtlError> {
209    /// # let transform = TransformMessage::identity();
210    /// # let msg = IgtlMessage::new(transform, "MyDevice")?;
211    /// # let any_msg = AnyMessage::Transform(msg);
212    /// assert_eq!(any_msg.device_name()?, "MyDevice");
213    /// # Ok(())
214    /// # }
215    /// ```
216    pub fn device_name(&self) -> Result<&str> {
217        let header = match self {
218            AnyMessage::Transform(msg) => &msg.header,
219            AnyMessage::Status(msg) => &msg.header,
220            AnyMessage::Capability(msg) => &msg.header,
221            AnyMessage::Image(msg) => &msg.header,
222            AnyMessage::Position(msg) => &msg.header,
223            AnyMessage::String(msg) => &msg.header,
224            AnyMessage::QtData(msg) => &msg.header,
225            AnyMessage::TData(msg) => &msg.header,
226            AnyMessage::Sensor(msg) => &msg.header,
227            AnyMessage::Point(msg) => &msg.header,
228            AnyMessage::Trajectory(msg) => &msg.header,
229            AnyMessage::NdArray(msg) => &msg.header,
230            AnyMessage::Bind(msg) => &msg.header,
231            AnyMessage::ColorTable(msg) => &msg.header,
232            AnyMessage::ImgMeta(msg) => &msg.header,
233            AnyMessage::LbMeta(msg) => &msg.header,
234            AnyMessage::PolyData(msg) => &msg.header,
235            AnyMessage::Video(msg) => &msg.header,
236            AnyMessage::VideoMeta(msg) => &msg.header,
237            AnyMessage::Command(msg) => &msg.header,
238            AnyMessage::GetTransform(msg) => &msg.header,
239            AnyMessage::GetStatus(msg) => &msg.header,
240            AnyMessage::GetCapability(msg) => &msg.header,
241            AnyMessage::GetImage(msg) => &msg.header,
242            AnyMessage::GetImgMeta(msg) => &msg.header,
243            AnyMessage::GetLbMeta(msg) => &msg.header,
244            AnyMessage::GetPoint(msg) => &msg.header,
245            AnyMessage::GetTData(msg) => &msg.header,
246            AnyMessage::RtsTransform(msg) => &msg.header,
247            AnyMessage::RtsStatus(msg) => &msg.header,
248            AnyMessage::RtsCapability(msg) => &msg.header,
249            AnyMessage::RtsImage(msg) => &msg.header,
250            AnyMessage::RtsTData(msg) => &msg.header,
251            AnyMessage::StartTData(msg) => &msg.header,
252            AnyMessage::StopTransform(msg) => &msg.header,
253            AnyMessage::StopPosition(msg) => &msg.header,
254            AnyMessage::StopQtData(msg) => &msg.header,
255            AnyMessage::StopTData(msg) => &msg.header,
256            AnyMessage::StopImage(msg) => &msg.header,
257            AnyMessage::StopNdArray(msg) => &msg.header,
258            AnyMessage::Unknown { header, .. } => header,
259        };
260        header.device_name.as_str()
261    }
262
263    /// Get reference to the message header
264    pub fn header(&self) -> &Header {
265        match self {
266            AnyMessage::Transform(msg) => &msg.header,
267            AnyMessage::Status(msg) => &msg.header,
268            AnyMessage::Capability(msg) => &msg.header,
269            AnyMessage::Image(msg) => &msg.header,
270            AnyMessage::Position(msg) => &msg.header,
271            AnyMessage::String(msg) => &msg.header,
272            AnyMessage::QtData(msg) => &msg.header,
273            AnyMessage::TData(msg) => &msg.header,
274            AnyMessage::Sensor(msg) => &msg.header,
275            AnyMessage::Point(msg) => &msg.header,
276            AnyMessage::Trajectory(msg) => &msg.header,
277            AnyMessage::NdArray(msg) => &msg.header,
278            AnyMessage::Bind(msg) => &msg.header,
279            AnyMessage::ColorTable(msg) => &msg.header,
280            AnyMessage::ImgMeta(msg) => &msg.header,
281            AnyMessage::LbMeta(msg) => &msg.header,
282            AnyMessage::PolyData(msg) => &msg.header,
283            AnyMessage::Video(msg) => &msg.header,
284            AnyMessage::VideoMeta(msg) => &msg.header,
285            AnyMessage::Command(msg) => &msg.header,
286            AnyMessage::GetTransform(msg) => &msg.header,
287            AnyMessage::GetStatus(msg) => &msg.header,
288            AnyMessage::GetCapability(msg) => &msg.header,
289            AnyMessage::GetImage(msg) => &msg.header,
290            AnyMessage::GetImgMeta(msg) => &msg.header,
291            AnyMessage::GetLbMeta(msg) => &msg.header,
292            AnyMessage::GetPoint(msg) => &msg.header,
293            AnyMessage::GetTData(msg) => &msg.header,
294            AnyMessage::RtsTransform(msg) => &msg.header,
295            AnyMessage::RtsStatus(msg) => &msg.header,
296            AnyMessage::RtsCapability(msg) => &msg.header,
297            AnyMessage::RtsImage(msg) => &msg.header,
298            AnyMessage::RtsTData(msg) => &msg.header,
299            AnyMessage::StartTData(msg) => &msg.header,
300            AnyMessage::StopTransform(msg) => &msg.header,
301            AnyMessage::StopPosition(msg) => &msg.header,
302            AnyMessage::StopQtData(msg) => &msg.header,
303            AnyMessage::StopTData(msg) => &msg.header,
304            AnyMessage::StopImage(msg) => &msg.header,
305            AnyMessage::StopNdArray(msg) => &msg.header,
306            AnyMessage::Unknown { header, .. } => header,
307        }
308    }
309
310    /// Try to extract as a Transform message
311    pub fn as_transform(&self) -> Option<&IgtlMessage<TransformMessage>> {
312        match self {
313            AnyMessage::Transform(msg) => Some(msg),
314            _ => None,
315        }
316    }
317
318    /// Try to extract as a Status message
319    pub fn as_status(&self) -> Option<&IgtlMessage<StatusMessage>> {
320        match self {
321            AnyMessage::Status(msg) => Some(msg),
322            _ => None,
323        }
324    }
325
326    /// Try to extract as an Image message
327    pub fn as_image(&self) -> Option<&IgtlMessage<ImageMessage>> {
328        match self {
329            AnyMessage::Image(msg) => Some(msg),
330            _ => None,
331        }
332    }
333
334    /// Try to extract as a Position message
335    pub fn as_position(&self) -> Option<&IgtlMessage<PositionMessage>> {
336        match self {
337            AnyMessage::Position(msg) => Some(msg),
338            _ => None,
339        }
340    }
341
342    /// Try to extract as a String message
343    pub fn as_string(&self) -> Option<&IgtlMessage<StringMessage>> {
344        match self {
345            AnyMessage::String(msg) => Some(msg),
346            _ => None,
347        }
348    }
349
350    /// Try to extract as a Capability message
351    pub fn as_capability(&self) -> Option<&IgtlMessage<CapabilityMessage>> {
352        match self {
353            AnyMessage::Capability(msg) => Some(msg),
354            _ => None,
355        }
356    }
357
358    /// Check if this is an unknown message type
359    pub fn is_unknown(&self) -> bool {
360        matches!(self, AnyMessage::Unknown { .. })
361    }
362
363    /// Decode a message from raw bytes with optional CRC verification
364    ///
365    /// This is a lower-level method that attempts to decode the message
366    /// based on its type_name field in the header.
367    ///
368    /// # Arguments
369    ///
370    /// * `data` - Raw message bytes (header + body)
371    /// * `verify_crc` - Whether to verify CRC checksum
372    ///
373    /// # Errors
374    ///
375    /// - [`IgtlError::InvalidHeader`](crate::error::IgtlError::InvalidHeader) - Malformed header
376    /// - [`IgtlError::CrcMismatch`](crate::error::IgtlError::CrcMismatch) - CRC verification failed
377    ///
378    /// # Examples
379    ///
380    /// ```no_run
381    /// # use openigtlink_rust::protocol::AnyMessage;
382    /// # fn example() -> Result<(), openigtlink_rust::error::IgtlError> {
383    /// # let data: Vec<u8> = vec![];
384    /// let msg = AnyMessage::decode_with_options(&data, true)?;
385    /// # Ok(())
386    /// # }
387    /// ```
388    pub fn decode_with_options(data: &[u8], verify_crc: bool) -> Result<Self> {
389        use crate::error::IgtlError;
390
391        // Decode header first to determine message type
392        let header = Header::decode(&data[..Header::SIZE])?;
393        let type_name = header.type_name.as_str()?;
394
395        // Try to decode as specific message types based on type_name
396        match type_name {
397            "TRANSFORM" => {
398                if let Ok(msg) =
399                    IgtlMessage::<TransformMessage>::decode_with_options(data, verify_crc)
400                {
401                    return Ok(AnyMessage::Transform(msg));
402                }
403            }
404            "STATUS" => {
405                if let Ok(msg) = IgtlMessage::<StatusMessage>::decode_with_options(data, verify_crc)
406                {
407                    return Ok(AnyMessage::Status(msg));
408                }
409            }
410            "CAPABILITY" => {
411                if let Ok(msg) =
412                    IgtlMessage::<CapabilityMessage>::decode_with_options(data, verify_crc)
413                {
414                    return Ok(AnyMessage::Capability(msg));
415                }
416            }
417            "IMAGE" => {
418                if let Ok(msg) = IgtlMessage::<ImageMessage>::decode_with_options(data, verify_crc)
419                {
420                    return Ok(AnyMessage::Image(msg));
421                }
422            }
423            "POSITION" => {
424                if let Ok(msg) =
425                    IgtlMessage::<PositionMessage>::decode_with_options(data, verify_crc)
426                {
427                    return Ok(AnyMessage::Position(msg));
428                }
429            }
430            "STRING" => {
431                if let Ok(msg) = IgtlMessage::<StringMessage>::decode_with_options(data, verify_crc)
432                {
433                    return Ok(AnyMessage::String(msg));
434                }
435            }
436            "QTDATA" => {
437                if let Ok(msg) = IgtlMessage::<QtDataMessage>::decode_with_options(data, verify_crc)
438                {
439                    return Ok(AnyMessage::QtData(msg));
440                }
441            }
442            "TDATA" => {
443                if let Ok(msg) = IgtlMessage::<TDataMessage>::decode_with_options(data, verify_crc)
444                {
445                    return Ok(AnyMessage::TData(msg));
446                }
447            }
448            "SENSOR" => {
449                if let Ok(msg) = IgtlMessage::<SensorMessage>::decode_with_options(data, verify_crc)
450                {
451                    return Ok(AnyMessage::Sensor(msg));
452                }
453            }
454            "POINT" => {
455                if let Ok(msg) = IgtlMessage::<PointMessage>::decode_with_options(data, verify_crc)
456                {
457                    return Ok(AnyMessage::Point(msg));
458                }
459            }
460            _ => {
461                // Unknown message type - store header and body
462                let body = data[Header::SIZE..].to_vec();
463                return Ok(AnyMessage::Unknown { header, body });
464            }
465        }
466
467        // If we get here, the message type matched but decode failed
468        Err(IgtlError::UnknownMessageType(type_name.to_string()))
469    }
470}