mecha10-core 0.7.0

Mecha10 core runtime and foundational types
Documentation
//! 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(())
    }
}