autocore-std 3.3.66

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,
    /// Set the present position as the reference (no motor movement) —
    /// CiA 402 "homing on the current position", **method 35** (the original,
    /// IEC 61800-7-201 Ed.1). The reference value is taken from the home offset
    /// (0x607C). This is the **Kollmorgen AKD's only** current-position method
    /// (the AKD lists codes 36–127 as reserved).
    CurrentPosition35,
    /// Set the present position as the reference (no motor movement) —
    /// CiA 402 "homing on the current position", **method 37** (the Ed.2
    /// replacement for 35, with standardized home-offset handling). Used by most
    /// non-AKD drives. Same operation as 35, different code.
    CurrentPosition37,
    /// 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::CurrentPosition35
            | Self::CurrentPosition37
            | 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::CurrentPosition35 => 35,
            Self::CurrentPosition37 => 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::CurrentPosition35 | Self::CurrentPosition37)
    }
}

#[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::CurrentPosition35.is_integrated());
        assert!(HomingMethod::CurrentPosition37.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::CurrentPosition35.cia402_code(), 35);
        assert_eq!(HomingMethod::CurrentPosition37.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::CurrentPosition35.requires_motion());
        assert!(!HomingMethod::CurrentPosition37.requires_motion());
        assert!(HomingMethod::LimitSwitchPosPnp.requires_motion());
        assert!(HomingMethod::HomeSensorPosPnp.requires_motion());
        assert!(HomingMethod::Integrated(35).requires_motion());
    }
}