darra-ethercat-master 1.99.7

商业 EtherCAT 主站协议栈 · 实时内核驱动 · 抖动 1µs · Windows + Linux · 多编程语言 · 全协议 · 支持复杂拓扑 + 热插拔 · ethercat.darra.xyz · Commercial EtherCAT Master protocol stack · Real-time kernel driver · 1µs jitter · Multi-platform · Multi-language · Complex topology + hot-plug.
Documentation
//! 错误传播语法糖: `?` 友好的从站访问
//!
//! 主 API `master.slave(i)` 永远成功 (内部不做边界检查, 拿到的可能是悬空句柄,
//! 在后续访问时才报错). 这对追求 `?` 一行返回的代码不友好. 本模块给
//! [`EtherCATMaster`] 加两个安全访问入口:
//!
//! - [`MasterTrySlaveExt::try_slave`] — 越界返回 [`crate::DarraError::InvalidParameter`],
//!   可直接 `?` 传播.
//! - [`MasterTrySlaveExt::slave_opt`] — 越界返回 `None`, 适合 `if let Some(s) = ...`.
//!
//! 同时为 EtherCAT 状态切换补一组 [`MasterStateTryExt`], 把成功/失败映射到
//! `Result<(), DarraError>`, 让 `master.try_set_state(EcState::Op)?` 成立.
//!
//! # 示例
//!
//! ```no_run
//! use darra_ethercat::sugar::prelude::*;
//! use darra_ethercat::{EtherCATMaster, EcState, Result};
//!
//! fn run(m: &EtherCATMaster) -> Result<()> {
//!     m.try_set_state(EcState::Operational, 5000)?;  // 不会启动 PDO 线程, 仅切状态
//!     let s = m.try_slave(1)?;
//!     println!("{}", s);
//!     Ok(())
//! }
//! ```

use crate::data::error::{DarraError, EcState, Result};
use crate::master::core::EtherCATMaster;
use crate::slave::core::Slave;

/// 安全的从站访问扩展
pub trait MasterTrySlaveExt {
    /// 越界返回错误的从站访问
    fn try_slave(&self, slave_index: u16) -> Result<Slave>;
    /// 越界返回 `None`
    fn slave_opt(&self, slave_index: u16) -> Option<Slave>;
}

impl MasterTrySlaveExt for EtherCATMaster {
    fn try_slave(&self, slave_index: u16) -> Result<Slave> {
        if slave_index == 0 {
            return Err(DarraError::InvalidParameter(
                "从站索引必须 >= 1 (0 表示主站本身)".into(),
            ));
        }
        let count = self.slave_count();
        if slave_index > count {
            return Err(DarraError::InvalidParameter(format!(
                "从站索引 {} 越界 (实际从站数 = {})",
                slave_index, count
            )));
        }
        Ok(self.slave(slave_index))
    }

    fn slave_opt(&self, slave_index: u16) -> Option<Slave> {
        let count = self.slave_count();
        if slave_index >= 1 && slave_index <= count {
            Some(self.slave(slave_index))
        } else {
            None
        }
    }
}

// ============================================================
// 状态切换的 Result 包装 (`&self` 版)
// ============================================================

/// 状态切换的 `?` 友好包装
///
/// 主类型 [`EtherCATMaster::set_state`] 需要 `&mut self`, 不利于在共享场景下传播.
/// 本扩展直接走 FFI `SetStateWithTimeout`, 仅查询不修改 Rust 侧 owned 状态,
/// 因此用 `&self` 即可. 注意: 本方法**不会**在 SafeOp/OP 进入时自动 Start PDO 线程,
/// 该副作用只能由 `&mut self` 版的 [`EtherCATMaster::set_state`] 触发.
pub trait MasterStateTryExt {
    /// 切换主站到目标状态; 失败返回 [`DarraError::StateChangeFailed`]
    fn try_set_state(&self, target: EcState, timeout_ms: u32) -> Result<()>;
}

impl MasterStateTryExt for EtherCATMaster {
    fn try_set_state(&self, target: EcState, timeout_ms: u32) -> Result<()> {
        use crate::utils::ffi;
        use std::os::raw::c_int;
        let mi = self.index();
        let ok = unsafe { ffi::SetStateWithTimeout(mi, target as c_int, timeout_ms) } != 0;
        if ok { Ok(()) } else { Err(DarraError::StateChangeFailed(target as u8)) }
    }
}