autocore-std 3.3.30

Standard library for AutoCore control programs - shared memory, IPC, and logging utilities
Documentation
//! Homing methods for CiA 402 servo drives.
//!
//! [`HomingMethod`] covers both hardware-delegated (drive-internal) homing
//! and software-implemented homing where the [`Axis`](super::Axis) monitors
//! [`AxisView`](super::AxisView) sensor signals.

/// Homing methods for CiA 402 servo drives.
///
/// **Integrated** methods delegate to the drive's built-in CiA 402 homing
/// mode (SDO 0x6098). The drive uses its own sensor inputs.
///
/// **Software** methods (no `Integrated` prefix) are implemented by the
/// [`Axis`](super::Axis) struct, which monitors [`AxisView`](super::AxisView)
/// sensor signals and captures the home position when triggered.
///
/// Limit switch variants use the sensor type convention:
/// - **Pnp**: sensor reads `true` when object detected (normally open)
/// - **Npn**: sensor reads `false` when object detected (normally closed)
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HomingMethod {
    // ── Hardware-delegated (drive handles it) ──

    /// Hard stop in positive direction (torque foldback). CiA 402 code: -1.
    HardStopPos,
    /// Hard stop in negative direction (torque foldback). CiA 402 code: -2.
    HardStopNeg,
    /// Drive's integrated positive limit switch. CiA 402 code: 18.
    IntegratedLimitSwitchPos,
    /// Drive's integrated negative limit switch. CiA 402 code: 17.
    IntegratedLimitSwitchNeg,
    /// Drive's integrated home sensor, positive direction, rising edge. CiA 402 code: 19.
    IntegratedHomeSensorPosRt,
    /// Drive's integrated home sensor, negative direction, rising edge. CiA 402 code: 21.
    IntegratedHomeSensorNegRt,
    /// Drive's integrated home sensor, positive direction, falling edge. CiA 402 code: 22.
    IntegratedHomeSensorPosFt,
    /// Drive's integrated home sensor, negative direction, falling edge. CiA 402 code: 20.
    IntegratedHomeSensorNegFt,
    /// Current position as home (no motor movement). CiA 402 code: 37.
    CurrentPosition,
    /// Arbitrary CiA 402 homing method code (vendor-specific or non-standard).
    Integrated(i8),

    // ── Software-implemented (Axis monitors AxisView sensors) ──

    /// Move positive, home on positive limit switch (PNP: true = detected).
    LimitSwitchPosPnp,
    /// Move negative, home on negative limit switch (PNP: true = detected).
    LimitSwitchNegPnp,
    /// Move positive, home on positive limit switch (NPN: false = detected).
    LimitSwitchPosNpn,
    /// Move negative, home on negative limit switch (NPN: false = detected).
    LimitSwitchNegNpn,
    /// Move positive, home on home sensor (PNP: true = detected).
    HomeSensorPosPnp,
    /// Move negative, home on home sensor (PNP: true = detected).
    HomeSensorNegPnp,
    /// Move positive, home on home sensor (NPN: false = detected).
    HomeSensorPosNpn,
    /// Move negative, home on home sensor (NPN: false = detected).
    HomeSensorNegNpn,
}

impl HomingMethod {
    /// True if the drive handles this method internally (hardware-delegated).
    pub fn is_integrated(&self) -> bool {
        match self {
            Self::HardStopPos
            | Self::HardStopNeg
            | Self::IntegratedLimitSwitchPos
            | Self::IntegratedLimitSwitchNeg
            | Self::IntegratedHomeSensorPosRt
            | Self::IntegratedHomeSensorNegRt
            | Self::IntegratedHomeSensorPosFt
            | Self::IntegratedHomeSensorNegFt
            | Self::CurrentPosition
            | Self::Integrated(_) => true,

            Self::LimitSwitchPosPnp
            | Self::LimitSwitchNegPnp
            | Self::LimitSwitchPosNpn
            | Self::LimitSwitchNegNpn
            | Self::HomeSensorPosPnp
            | Self::HomeSensorNegPnp
            | Self::HomeSensorPosNpn
            | Self::HomeSensorNegNpn => false,
        }
    }

    /// CiA 402 method code for hardware-delegated methods.
    ///
    /// # Panics
    ///
    /// Panics if called on a software-implemented method.
    /// Check [`is_integrated()`](Self::is_integrated) first.
    pub fn cia402_code(&self) -> i8 {
        match self {
            Self::HardStopPos => -1,
            Self::HardStopNeg => -2,
            Self::IntegratedLimitSwitchPos => 18,
            Self::IntegratedLimitSwitchNeg => 17,
            Self::IntegratedHomeSensorPosRt => 19,
            Self::IntegratedHomeSensorNegRt => 21,
            Self::IntegratedHomeSensorPosFt => 22,
            Self::IntegratedHomeSensorNegFt => 20,
            Self::CurrentPosition => 37,
            Self::Integrated(code) => *code,
            _ => panic!("cia402_code() called on software homing method {:?}", self),
        }
    }

    /// True if this method involves motor movement.
    pub fn requires_motion(&self) -> bool {
        !matches!(self, Self::CurrentPosition)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn integrated_methods_are_integrated() {
        assert!(HomingMethod::HardStopPos.is_integrated());
        assert!(HomingMethod::HardStopNeg.is_integrated());
        assert!(HomingMethod::IntegratedLimitSwitchPos.is_integrated());
        assert!(HomingMethod::IntegratedLimitSwitchNeg.is_integrated());
        assert!(HomingMethod::IntegratedHomeSensorPosRt.is_integrated());
        assert!(HomingMethod::IntegratedHomeSensorNegRt.is_integrated());
        assert!(HomingMethod::IntegratedHomeSensorPosFt.is_integrated());
        assert!(HomingMethod::IntegratedHomeSensorNegFt.is_integrated());
        assert!(HomingMethod::CurrentPosition.is_integrated());
        assert!(HomingMethod::Integrated(35).is_integrated());
    }

    #[test]
    fn software_methods_are_not_integrated() {
        assert!(!HomingMethod::LimitSwitchPosPnp.is_integrated());
        assert!(!HomingMethod::LimitSwitchNegPnp.is_integrated());
        assert!(!HomingMethod::LimitSwitchPosNpn.is_integrated());
        assert!(!HomingMethod::LimitSwitchNegNpn.is_integrated());
        assert!(!HomingMethod::HomeSensorPosPnp.is_integrated());
        assert!(!HomingMethod::HomeSensorNegPnp.is_integrated());
        assert!(!HomingMethod::HomeSensorPosNpn.is_integrated());
        assert!(!HomingMethod::HomeSensorNegNpn.is_integrated());
    }

    #[test]
    fn cia402_codes_correct() {
        assert_eq!(HomingMethod::HardStopPos.cia402_code(), -1);
        assert_eq!(HomingMethod::HardStopNeg.cia402_code(), -2);
        assert_eq!(HomingMethod::IntegratedLimitSwitchPos.cia402_code(), 18);
        assert_eq!(HomingMethod::IntegratedLimitSwitchNeg.cia402_code(), 17);
        assert_eq!(HomingMethod::IntegratedHomeSensorPosRt.cia402_code(), 19);
        assert_eq!(HomingMethod::IntegratedHomeSensorNegRt.cia402_code(), 21);
        assert_eq!(HomingMethod::IntegratedHomeSensorPosFt.cia402_code(), 22);
        assert_eq!(HomingMethod::IntegratedHomeSensorNegFt.cia402_code(), 20);
        assert_eq!(HomingMethod::CurrentPosition.cia402_code(), 37);
        assert_eq!(HomingMethod::Integrated(35).cia402_code(), 35);
    }

    #[test]
    #[should_panic]
    fn cia402_code_panics_on_software_method() {
        HomingMethod::LimitSwitchPosPnp.cia402_code();
    }

    #[test]
    fn requires_motion() {
        assert!(HomingMethod::HardStopPos.requires_motion());
        assert!(HomingMethod::IntegratedLimitSwitchPos.requires_motion());
        assert!(HomingMethod::IntegratedHomeSensorPosRt.requires_motion());
        assert!(!HomingMethod::CurrentPosition.requires_motion());
        assert!(HomingMethod::LimitSwitchPosPnp.requires_motion());
        assert!(HomingMethod::HomeSensorPosPnp.requires_motion());
        assert!(HomingMethod::Integrated(35).requires_motion());
    }
}