phoxal 0.4.0

Phoxal — production-oriented autonomous robot framework (engine, model, typed bus, contracts).
Documentation
use crate::bus::zenoh::TypedSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum Scan {
    Ranges(Ranges),
    Points(Points),
}

impl TypedSchema for Scan {
    const SCHEMA_NAME: &'static str = "component/capability/lidar";
    const SCHEMA_VERSION: u32 = 1;
}

pub const KIND: &str = "lidar";

crate::bus::topic_leaf! {
    pubsub(component_id: &str, capability_id: &str) {
        path: "component/{}/{}/profile/default",
        payload: Scan
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Ranges {
    pub ranges: Vec<f32>,
    pub geometry: Option<ScanGeometry>,
    pub limits: Option<RangeLimits>,
    pub measured_at_ns: Option<u64>,
    pub quality: Option<ScanQuality>,
    pub health: SensorHealth,
}

impl Ranges {
    pub fn new(ranges: impl Into<Vec<f32>>) -> Self {
        Self {
            ranges: ranges.into(),
            geometry: None,
            limits: None,
            measured_at_ns: None,
            quality: None,
            health: SensorHealth::Nominal,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Points {
    pub points: Vec<[f32; 3]>,
    pub limits: Option<RangeLimits>,
    pub measured_at_ns: Option<u64>,
    pub quality: Option<ScanQuality>,
    pub health: SensorHealth,
}

impl Points {
    pub fn new(points: impl Into<Vec<[f32; 3]>>) -> Self {
        Self {
            points: points.into(),
            limits: None,
            measured_at_ns: None,
            quality: None,
            health: SensorHealth::Nominal,
        }
    }
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct ScanGeometry {
    pub angle_min_rad: f32,
    pub angle_increment_rad: f32,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct RangeLimits {
    pub min_m: f32,
    pub max_m: f32,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct ScanQuality {
    pub valid_points: u32,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SensorHealth {
    Nominal,
    Degraded,
    Fault,
}

#[cfg(test)]
mod tests {
    use crate::bus::zenoh::TypedSchema;

    use super::Scan;

    #[test]
    fn schema_contract_does_not_drift() {
        assert_eq!(Scan::SCHEMA_NAME, "component/capability/lidar");
        assert_eq!(Scan::SCHEMA_VERSION, 1);
    }

    #[test]
    fn path_is_stable() {
        assert_eq!(
            super::path("front_lidar", "scan"),
            "component/front_lidar/scan/profile/default"
        );
    }
}