Skip to main content

phoxal_model/component/
capability.rs

1//! Canonical component capabilities normalized from versioned source documents.
2
3#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
4#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
5pub enum Capability {
6    Motor(Motor),
7    Encoder(Encoder),
8    Accelerometer(Accelerometer),
9    Gyroscope(Gyroscope),
10    Magnetometer(Magnetometer),
11    Imu(Imu),
12    Gnss(Gnss),
13    Camera(Camera),
14    Depth(Depth),
15    EmergencyStop(EmergencyStop),
16    Range(Range),
17    Lidar(Lidar),
18    Mmwave(Mmwave),
19    Microphone(Microphone),
20    Speaker(Speaker),
21    Battery(Battery),
22    Led(Led),
23}
24
25#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
26#[serde(rename_all = "snake_case")]
27pub enum EncoderType {
28    Incremental,
29    Absolute,
30}
31
32#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
33#[serde(rename_all = "snake_case")]
34pub enum MotorCommand {
35    Position,
36    Velocity,
37    Torque,
38}
39
40#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)]
41#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
42pub enum StructuralTarget {
43    Joint { id: String },
44    Link { id: String },
45}
46
47pub const MODULE_INSTANCE_SEPARATOR: &str = "__";
48
49impl StructuralTarget {
50    #[must_use]
51    pub fn namespaced(&self, component_id: &str) -> Self {
52        match self {
53            Self::Joint { id } => Self::Joint {
54                id: format!("{component_id}{MODULE_INSTANCE_SEPARATOR}{id}"),
55            },
56            Self::Link { id } => Self::Link {
57                id: format!("{component_id}{MODULE_INSTANCE_SEPARATOR}{id}"),
58            },
59        }
60    }
61}
62
63#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
64#[serde(rename_all = "snake_case")]
65pub enum LidarOutput {
66    Ranges,
67    Points,
68}
69
70#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
71#[serde(rename_all = "snake_case")]
72pub enum CameraMode {
73    Mono,
74    Rgb,
75}
76
77#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, Default, PartialEq, Eq)]
78#[serde(rename_all = "snake_case")]
79pub enum GnssCoordinateSystem {
80    #[default]
81    Local,
82    Wgs84,
83}
84
85impl Capability {
86    #[must_use]
87    pub const fn kind_name(&self) -> &'static str {
88        match self {
89            Self::Motor(_) => "motor",
90            Self::Encoder(_) => "encoder",
91            Self::Accelerometer(_) => "accelerometer",
92            Self::Gyroscope(_) => "gyroscope",
93            Self::Magnetometer(_) => "magnetometer",
94            Self::Imu(_) => "imu",
95            Self::Gnss(_) => "gnss",
96            Self::Camera(_) => "camera",
97            Self::Depth(_) => "depth",
98            Self::EmergencyStop(_) => "emergency_stop",
99            Self::Range(_) => "range",
100            Self::Lidar(_) => "lidar",
101            Self::Mmwave(_) => "mmwave",
102            Self::Microphone(_) => "microphone",
103            Self::Speaker(_) => "speaker",
104            Self::Battery(_) => "battery",
105            Self::Led(_) => "led",
106        }
107    }
108
109    #[must_use]
110    pub const fn target(&self) -> &StructuralTarget {
111        match self {
112            Self::Motor(value) => &value.target,
113            Self::Encoder(value) => &value.target,
114            Self::Accelerometer(value) => &value.target,
115            Self::Gyroscope(value) => &value.target,
116            Self::Magnetometer(value) => &value.target,
117            Self::Imu(value) => &value.target,
118            Self::Gnss(value) => &value.target,
119            Self::Camera(value) => &value.target,
120            Self::Depth(value) => &value.target,
121            Self::EmergencyStop(value) => &value.target,
122            Self::Range(value) => &value.target,
123            Self::Lidar(value) => &value.target,
124            Self::Mmwave(value) => &value.target,
125            Self::Microphone(value) => &value.target,
126            Self::Speaker(value) => &value.target,
127            Self::Battery(value) => &value.target,
128            Self::Led(value) => &value.target,
129        }
130    }
131}
132
133#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
134#[serde(deny_unknown_fields)]
135pub struct Motor {
136    pub target: StructuralTarget,
137    pub command: MotorCommand,
138    pub gear_ratio: f64,
139    pub max_torque_nm: Option<f64>,
140    pub max_velocity_radps: Option<f64>,
141}
142
143#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
144#[serde(deny_unknown_fields)]
145pub struct Encoder {
146    pub target: StructuralTarget,
147    pub publish_rate_hz: f64,
148    pub gear_ratio: f64,
149    pub encoder_type: EncoderType,
150    pub counts_per_revolution: u32,
151}
152
153#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
154#[serde(deny_unknown_fields)]
155pub struct Accelerometer {
156    pub target: StructuralTarget,
157    pub publish_rate_hz: f64,
158    pub axes: Option<[bool; 3]>,
159}
160
161#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
162#[serde(deny_unknown_fields)]
163pub struct Gyroscope {
164    pub target: StructuralTarget,
165    pub publish_rate_hz: f64,
166    pub axes: Option<[bool; 3]>,
167}
168
169#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
170#[serde(deny_unknown_fields)]
171pub struct Magnetometer {
172    pub target: StructuralTarget,
173    pub publish_rate_hz: f64,
174    pub axes: Option<[bool; 3]>,
175}
176
177#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
178#[serde(deny_unknown_fields)]
179pub struct Imu {
180    pub target: StructuralTarget,
181    pub publish_rate_hz: f64,
182    pub axes: Option<[bool; 3]>,
183}
184
185#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
186#[serde(deny_unknown_fields)]
187pub struct Gnss {
188    pub target: StructuralTarget,
189    pub publish_rate_hz: f64,
190    pub coordinate_system: GnssCoordinateSystem,
191}
192
193#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
194#[serde(deny_unknown_fields)]
195pub struct Camera {
196    pub target: StructuralTarget,
197    pub mode: CameraMode,
198    pub publish_rate_hz: f64,
199    pub width_px: u32,
200    pub height_px: u32,
201    pub field_of_view_rad: Option<f64>,
202}
203
204#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
205#[serde(deny_unknown_fields)]
206pub struct Depth {
207    pub target: StructuralTarget,
208    pub publish_rate_hz: f64,
209    pub width_px: u32,
210    pub height_px: u32,
211    pub field_of_view_rad: Option<f64>,
212    pub min_range_m: Option<f64>,
213    pub max_range_m: Option<f64>,
214}
215
216#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
217#[serde(deny_unknown_fields)]
218pub struct Range {
219    pub target: StructuralTarget,
220    pub publish_rate_hz: f64,
221    pub min_range_m: f64,
222    pub max_range_m: f64,
223    pub field_of_view_rad: f64,
224}
225
226#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
227#[serde(deny_unknown_fields)]
228pub struct EmergencyStop {
229    pub target: StructuralTarget,
230}
231
232#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
233#[serde(deny_unknown_fields)]
234pub struct Lidar {
235    pub target: StructuralTarget,
236    pub publish_rate_hz: f64,
237    pub output: LidarOutput,
238    pub min_range_m: Option<f64>,
239    pub max_range_m: Option<f64>,
240    pub horizontal_fov_rad: Option<f64>,
241    pub horizontal_resolution_rad: Option<f64>,
242    pub vertical_fov_rad: Option<f64>,
243    pub vertical_resolution_rad: Option<f64>,
244}
245
246#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
247#[serde(deny_unknown_fields)]
248pub struct Mmwave {
249    pub target: StructuralTarget,
250    pub publish_rate_hz: f64,
251}
252
253#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
254#[serde(deny_unknown_fields)]
255pub struct Microphone {
256    pub target: StructuralTarget,
257    pub publish_rate_hz: f64,
258}
259
260#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
261#[serde(deny_unknown_fields)]
262pub struct Speaker {
263    pub target: StructuralTarget,
264}
265
266#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
267#[serde(deny_unknown_fields)]
268pub struct Battery {
269    pub target: StructuralTarget,
270    pub publish_rate_hz: f64,
271    pub voltage_v: f64,
272    pub capacity_ah: f64,
273}
274
275#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
276#[serde(deny_unknown_fields)]
277pub struct Led {
278    pub target: StructuralTarget,
279}