1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
//! Convenience helpers for well-known topic categories (sensors, actuators, cameras, etc).
#![cfg(feature = "messaging")]
use super::Context;
use crate::error::Result;
impl Context {
/// Subscribe to all camera topics
///
/// Convenience method for `subscribe_aggregated::<Image>("/sensor/camera/*")`
///
/// # Example
///
/// ```rust,no_run
/// use mecha10_core::prelude::*;
///
/// # async fn example(ctx: &Context) -> Result<()> {
/// let mut cameras = ctx.subscribe_all_cameras().await?;
/// while let Some((topic, image)) = cameras.recv().await {
/// let camera_name = topic.rsplit('/').next().unwrap_or("unknown");
/// println!("Camera {}: {}x{}", camera_name, image.width, image.height);
/// }
/// # Ok(())
/// # }
/// ```
pub async fn subscribe_all_cameras(&self) -> Result<crate::aggregated::AggregatedReceiver<crate::types::Image>> {
self.subscribe_aggregated("/sensor/camera/*").await
}
/// Subscribe to all sensor topics
///
/// Subscribes to all sensor data including cameras, LiDAR, IMU, odometry, etc.
///
/// # Example
///
/// ```rust,no_run
/// use mecha10_core::prelude::*;
///
/// # async fn example(ctx: &Context) -> Result<()> {
/// // Subscribe to all sensors (returns untyped messages)
/// let topics = ctx.discover_topics("/sensor/**").await?;
/// println!("Found {} sensor topics", topics.len());
/// # Ok(())
/// # }
/// ```
pub async fn discover_all_sensors(&self) -> Result<Vec<String>> {
self.discover_topics("/sensor/**").await
}
/// Subscribe to all LiDAR topics
///
/// Convenience method for `subscribe_aggregated::<LaserScan>("/sensor/lidar/*")`
pub async fn subscribe_all_lidars(&self) -> Result<crate::aggregated::AggregatedReceiver<crate::types::LaserScan>> {
self.subscribe_aggregated("/sensor/lidar/*").await
}
/// Subscribe to all IMU topics
///
/// Convenience method for `subscribe_aggregated::<IMU>("/sensor/imu/*")`
pub async fn subscribe_all_imus(&self) -> Result<crate::aggregated::AggregatedReceiver<crate::types::IMU>> {
self.subscribe_aggregated("/sensor/imu/*").await
}
/// Subscribe to all motor topics
///
/// Convenience method for `subscribe_aggregated::<MotorStatus>("/actuator/motor/*")`
pub async fn subscribe_all_motors(
&self,
) -> Result<crate::aggregated::AggregatedReceiver<crate::actuator::MotorStatus>> {
self.subscribe_aggregated("/actuator/motor/*").await
}
/// List all sensor topics currently active in the system
///
/// Returns all topics under `/sensor/*` including cameras, LiDAR, IMU, GPS, etc.
///
/// # Example
///
/// ```rust,no_run
/// use mecha10_core::prelude::*;
///
/// # async fn example(ctx: &Context) -> Result<()> {
/// let sensors = ctx.list_sensor_topics().await?;
/// println!("Available sensors:");
/// for topic in sensors {
/// println!(" - {}", topic);
/// }
/// # Ok(())
/// # }
/// ```
pub async fn list_sensor_topics(&self) -> Result<Vec<String>> {
self.discover_topics("/sensor/*").await
}
/// List all actuator topics currently active in the system
///
/// Returns all topics under `/actuator/*` including motors, servos, grippers, etc.
///
/// # Example
///
/// ```rust,no_run
/// use mecha10_core::prelude::*;
///
/// # async fn example(ctx: &Context) -> Result<()> {
/// let actuators = ctx.list_actuator_topics().await?;
/// println!("Available actuators:");
/// for topic in actuators {
/// println!(" - {}", topic);
/// }
/// # Ok(())
/// # }
/// ```
pub async fn list_actuator_topics(&self) -> Result<Vec<String>> {
self.discover_topics("/actuator/*").await
}
/// List all topics in the system
///
/// Returns all active topics regardless of category.
///
/// # Example
///
/// ```rust,no_run
/// use mecha10_core::prelude::*;
///
/// # async fn example(ctx: &Context) -> Result<()> {
/// let all_topics = ctx.list_all_topics().await?;
/// println!("All active topics ({} total):", all_topics.len());
/// for topic in all_topics {
/// println!(" - {}", topic);
/// }
/// # Ok(())
/// # }
/// ```
pub async fn list_all_topics(&self) -> Result<Vec<String>> {
self.discover_topics("*").await
}
/// List all camera topics
///
/// Returns all topics under `/sensor/camera/*`.
///
/// # Example
///
/// ```rust,no_run
/// use mecha10_core::prelude::*;
///
/// # async fn example(ctx: &Context) -> Result<()> {
/// let cameras = ctx.list_camera_topics().await?;
/// println!("Available cameras:");
/// for topic in cameras {
/// println!(" - {}", topic);
/// }
/// # Ok(())
/// # }
/// ```
pub async fn list_camera_topics(&self) -> Result<Vec<String>> {
self.discover_topics("/sensor/camera/*").await
}
/// List all LiDAR topics
///
/// Returns all topics under `/sensor/lidar/*`.
pub async fn list_lidar_topics(&self) -> Result<Vec<String>> {
self.discover_topics("/sensor/lidar/*").await
}
/// List all IMU topics
///
/// Returns all topics under `/sensor/imu/*`.
pub async fn list_imu_topics(&self) -> Result<Vec<String>> {
self.discover_topics("/sensor/imu/*").await
}
/// List all motor topics
///
/// Returns all topics under `/actuator/motor/*`.
pub async fn list_motor_topics(&self) -> Result<Vec<String>> {
self.discover_topics("/actuator/motor/*").await
}
/// Print a formatted summary of all active topics
///
/// Useful for debugging and introspection. Groups topics by category
/// and displays them in a human-readable format.
///
/// # Example
///
/// ```rust,no_run
/// use mecha10_core::prelude::*;
///
/// # async fn example(ctx: &Context) -> Result<()> {
/// ctx.print_topic_summary().await?;
/// // Output:
/// // === Active Topics ===
/// // Sensors (3):
/// // - /sensor/camera/front
/// // - /sensor/lidar/main
/// // - /sensor/imu/base
/// // Actuators (2):
/// // - /actuator/motor/left
/// // - /actuator/motor/right
/// // =====================
/// # Ok(())
/// # }
/// ```
#[allow(
clippy::print_stdout,
reason = "this method's entire purpose is printing a human-readable topic summary to stdout"
)]
pub async fn print_topic_summary(&self) -> Result<()> {
let sensors = self.list_sensor_topics().await.unwrap_or_default();
let actuators = self.list_actuator_topics().await.unwrap_or_default();
let all_topics = self.list_all_topics().await?;
let other_topics: Vec<String> = all_topics
.into_iter()
.filter(|t| !t.starts_with("/sensor/") && !t.starts_with("/actuator/"))
.collect();
println!("=== Active Topics ===");
if !sensors.is_empty() {
println!("Sensors ({}):", sensors.len());
for topic in sensors {
println!(" - {}", topic);
}
}
if !actuators.is_empty() {
println!("Actuators ({}):", actuators.len());
for topic in actuators {
println!(" - {}", topic);
}
}
if !other_topics.is_empty() {
println!("Other ({}):", other_topics.len());
for topic in other_topics {
println!(" - {}", topic);
}
}
println!("=====================");
Ok(())
}
}