phoxal 0.67.0

Phoxal - production-oriented autonomous robot framework: the one framework library, holding the runtime engine, the api contract tree, the typed bus, the canonical model, and the bundle.
Documentation
crate::endpoints! {
    tree: State<Tree>;
    static_transforms: State<StaticTransforms>;
    lookup: Query<LookupRequest, LookupResponse>;
}

const MAX_FRAME_ID_LEN: usize = 128;

fn finite(value: f64) -> bool {
    value.is_finite()
}

fn valid_frame_id(value: &str) -> bool {
    !value.is_empty()
        && value.len() <= MAX_FRAME_ID_LEN
        && value
            .bytes()
            .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-' | b'.'))
}

#[derive(
    phoxal_macros::DescribeWire, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize,
)]
#[serde(try_from = "FrameTransformWire")]
pub struct FrameTransform {
    pub parent_frame_id: String,
    pub child_frame_id: String,
    pub translation_m: [f64; 3],
    pub rotation_quat_xyzw: [f64; 4],
    pub stamp: Option<::phoxal::bus::RobotInstant>,
}

impl FrameTransform {
    pub fn try_new(
        parent_frame_id: String,
        child_frame_id: String,
        translation_m: [f64; 3],
        rotation_quat_xyzw: [f64; 4],
        stamp: Option<::phoxal::bus::RobotInstant>,
    ) -> Result<Self, FrameTransformError> {
        if !valid_frame_id(&parent_frame_id) || !valid_frame_id(&child_frame_id) {
            return Err(FrameTransformError::InvalidFrameId);
        }
        if !translation_m.into_iter().all(finite) {
            return Err(FrameTransformError::NonFiniteTranslation);
        }
        if !rotation_quat_xyzw.into_iter().all(finite) {
            return Err(FrameTransformError::NonFiniteQuaternion);
        }
        let norm = rotation_quat_xyzw
            .into_iter()
            .map(|value| value * value)
            .sum::<f64>()
            .sqrt();
        if !norm.is_finite() || norm <= f64::EPSILON || (norm - 1.0).abs() > 1.0e-6 {
            return Err(FrameTransformError::QuaternionNotNormalized);
        }
        Ok(Self {
            parent_frame_id,
            child_frame_id,
            translation_m,
            rotation_quat_xyzw,
            stamp,
        })
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FrameTransformError {
    InvalidFrameId,
    NonFiniteTranslation,
    NonFiniteQuaternion,
    QuaternionNotNormalized,
}

impl std::fmt::Display for FrameTransformError {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        formatter.write_str(match self {
            Self::InvalidFrameId => "frame ids must be non-empty bounded key segments",
            Self::NonFiniteTranslation => "frame translation must be finite",
            Self::NonFiniteQuaternion => "frame quaternion must be finite",
            Self::QuaternionNotNormalized => "frame quaternion must be normalized and nonzero",
        })
    }
}
impl std::error::Error for FrameTransformError {}

#[derive(
    phoxal_macros::DescribeWire, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize,
)]
pub struct StaticTransforms {
    pub transforms: Vec<FrameTransform>,
}

#[derive(
    phoxal_macros::DescribeWire, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize,
)]
pub struct Tree {
    pub transforms: Vec<FrameTransform>,
}

#[derive(
    phoxal_macros::DescribeWire, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize,
)]
#[serde(try_from = "LookupRequestWire")]
pub struct LookupRequest {
    pub target_frame_id: String,
    pub source_frame_id: String,
    pub at: Option<::phoxal::bus::RobotInstant>,
}

#[derive(
    phoxal_macros::DescribeWire, Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize,
)]
pub struct LookupResponse {
    pub transform: Option<FrameTransform>,
}

#[derive(Clone, Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct FrameTransformWire {
    parent_frame_id: String,
    child_frame_id: String,
    translation_m: [f64; 3],
    rotation_quat_xyzw: [f64; 4],
    stamp: Option<::phoxal::bus::RobotInstant>,
}

impl TryFrom<FrameTransformWire> for FrameTransform {
    type Error = FrameTransformError;
    fn try_from(value: FrameTransformWire) -> Result<Self, Self::Error> {
        Self::try_new(
            value.parent_frame_id,
            value.child_frame_id,
            value.translation_m,
            value.rotation_quat_xyzw,
            value.stamp,
        )
    }
}

#[derive(Clone, Debug, serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct LookupRequestWire {
    target_frame_id: String,
    source_frame_id: String,
    at: Option<::phoxal::bus::RobotInstant>,
}

impl TryFrom<LookupRequestWire> for LookupRequest {
    type Error = FrameTransformError;
    fn try_from(value: LookupRequestWire) -> Result<Self, Self::Error> {
        if !valid_frame_id(&value.target_frame_id) || !valid_frame_id(&value.source_frame_id) {
            return Err(FrameTransformError::InvalidFrameId);
        }
        Ok(Self {
            target_frame_id: value.target_frame_id,
            source_frame_id: value.source_frame_id,
            at: value.at,
        })
    }
}