Skip to main content

mqd_core/
event.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4use crate::ids::{ComponentName, EntityPath, Timeline};
5
6#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
7pub enum ComponentKind {
8    Transform3D,
9    Points3D,
10    Scalar,
11    ImageRef,
12    Annotation,
13}
14
15impl ComponentKind {
16    pub const ALL: [Self; 5] = [
17        Self::Transform3D,
18        Self::Points3D,
19        Self::Scalar,
20        Self::ImageRef,
21        Self::Annotation,
22    ];
23
24    pub fn as_str(&self) -> &'static str {
25        match self {
26            Self::Transform3D => "Transform3D",
27            Self::Points3D => "Points3D",
28            Self::Scalar => "Scalar",
29            Self::ImageRef => "ImageRef",
30            Self::Annotation => "Annotation",
31        }
32    }
33
34    pub fn parse(s: &str) -> Option<Self> {
35        match s {
36            "Transform3D" => Some(Self::Transform3D),
37            "Points3D" => Some(Self::Points3D),
38            "Scalar" => Some(Self::Scalar),
39            "ImageRef" => Some(Self::ImageRef),
40            "Annotation" => Some(Self::Annotation),
41            _ => None,
42        }
43    }
44}
45
46impl fmt::Display for ComponentKind {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        f.write_str(self.as_str())
49    }
50}
51
52#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
53#[serde(tag = "kind")]
54pub enum Payload {
55    Transform3D {
56        tx: f64,
57        ty: f64,
58        tz: f64,
59        qx: f64,
60        qy: f64,
61        qz: f64,
62        qw: f64,
63    },
64    Points3D {
65        points: Vec<[f64; 3]>,
66    },
67    Scalar {
68        value: f64,
69        #[serde(default, skip_serializing_if = "Option::is_none")]
70        unit: Option<String>,
71    },
72    ImageRef {
73        uri: String,
74        width: i32,
75        height: i32,
76        format: String,
77    },
78    Annotation {
79        label: String,
80    },
81}
82
83impl Payload {
84    pub fn kind(&self) -> ComponentKind {
85        match self {
86            Self::Transform3D { .. } => ComponentKind::Transform3D,
87            Self::Points3D { .. } => ComponentKind::Points3D,
88            Self::Scalar { .. } => ComponentKind::Scalar,
89            Self::ImageRef { .. } => ComponentKind::ImageRef,
90            Self::Annotation { .. } => ComponentKind::Annotation,
91        }
92    }
93}
94
95#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
96pub struct Event {
97    pub entity_path: EntityPath,
98    pub timeline: Timeline,
99    pub t_ns: i64,
100    pub component: ComponentName,
101    pub payload: Payload,
102}
103
104impl Event {
105    pub fn kind(&self) -> ComponentKind {
106        self.payload.kind()
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn component_kind_roundtrip() {
116        for kind in ComponentKind::ALL {
117            assert_eq!(ComponentKind::parse(kind.as_str()), Some(kind));
118        }
119        assert_eq!(ComponentKind::parse("Nope"), None);
120    }
121
122    #[test]
123    fn event_json_roundtrip() {
124        let event = Event {
125            entity_path: EntityPath::new("/robot/base").unwrap(),
126            timeline: Timeline::wall(),
127            t_ns: 1_000_000_000,
128            component: ComponentName::new("pose"),
129            payload: Payload::Transform3D {
130                tx: 1.0,
131                ty: 2.0,
132                tz: 3.0,
133                qx: 0.0,
134                qy: 0.0,
135                qz: 0.0,
136                qw: 1.0,
137            },
138        };
139        let json = serde_json::to_string(&event).unwrap();
140        let back: Event = serde_json::from_str(&json).unwrap();
141        assert_eq!(event, back);
142    }
143}