1#[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}