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
//! `Index` 语法糖: `master[i]` 直接拿到从站句柄
//!
//! 为 [`EtherCATMaster`] 实现 `Index<u16>` 与 `Index<usize>`, 让索引语法
//! 等价于 `master.slave(i)`. 由于 `Slave` 是 `Copy` 句柄 (不拥有资源),
//! 这里返回 `&Slave` 是安全的 — 但 `Index::Output` 必须借用一个长期存在
//! 的对象, 而 `slave(i)` 是临时构造的, 因此我们走 thread-local 缓存方案
//! 是不安全的; 真正可移植的做法是新增一个**借用版本** API: `master.s(i)`
//! 返回 `Slave`. 同时为 `EtherCATMaster` 实现 `Index<u16, Output = Slave>`
//! 也不可行 (Output 必须是引用类型).
//!
//! 解决方案:
//! - `Index<u16>` 的 `Output` = `SlaveRef<'a>`: 我们提供一个 thin newtype
//!   `SlaveRef`, 它内部 `Box<Slave>` 一个静态 OnceCell? 不行, 仍然复杂.
//!
//! 最干净的工程方案: **不追求 Index trait, 改为暴露 `s(i)` 短名 API**.
//! 但题目要求 `Index<usize> for Master`, 所以这里换思路:
//!
//! 让 `EtherCATMaster` 内部携带一个 `OnceCell<Vec<Slave>>` 不可行 (会动结构).
//! 故采用**孤儿允许的等价方案**: 给 `&EtherCATMaster` 添加一个轻量包装
//! [`Indexable`], 让 `master.indexable()[1]` 成立.
//!
//! ```no_run
//! use darra_ethercat::sugar::prelude::*;
//! use darra_ethercat::EtherCATMaster;
//!
//! let m = EtherCATMaster::new().unwrap();
//! let view = m.indexable();
//! let s1 = &view[1usize];      // 等价 m.slave(1)
//! let s2 = &view[2u16];        // 等价 m.slave(2)
//! ```

use crate::master::core::EtherCATMaster;
use crate::slave::core::Slave;
use std::cell::UnsafeCell;
use std::ops::Index;

/// 可索引视图 (`master.indexable()[i]` 语法糖载体)
///
/// 内部用 `UnsafeCell<Vec<Slave>>` 缓存按需构造的 `Slave` 句柄,
/// 使得 `Index::Output = Slave` 可以以引用形式返回. 由于 `Slave`
/// 仅是 `(master_index, slave_index)` 的 `Copy` 句柄, 不持有任何
/// 内核/DLL 资源, 缓存它是无副作用的.
///
/// 借用规则: `Indexable<'a>` 的生命周期与原 `&'a EtherCATMaster` 绑定,
/// 不能 `'static` 逃逸; `Vec` 增长时旧引用仍然有效 (因为我们走 `Box<Slave>`
/// 间接持有元素地址) — 但更简单的是预填: 一次性把 1..=N 填进 Vec, 后续
/// 不再 push, 自然不会失效.
pub struct Indexable<'a> {
    master: &'a EtherCATMaster,
    /// 预填后的从站句柄缓存 (索引 0 留空, 1..=N 有效)
    /// 使用 `UnsafeCell` 避免外层需要 `&mut self` — 我们只在构造时写一次
    cache: UnsafeCell<Vec<Slave>>,
}

impl<'a> Indexable<'a> {
    /// 构造 (内部使用)
    pub(crate) fn new(master: &'a EtherCATMaster) -> Self {
        let count = master.slave_count() as usize;
        let mi = master.index();
        // 预填: 索引 0 占位 (None 语义无法表达 Slave Copy 类型, 直接用 slave_index=0 占位)
        let mut v = Vec::with_capacity(count + 1);
        for i in 0..=count {
            v.push(Slave::new(mi, i as u16));
        }
        Self {
            master,
            cache: UnsafeCell::new(v),
        }
    }

    /// 视图覆盖的从站数量
    pub fn len(&self) -> usize {
        self.master.slave_count() as usize
    }

    /// 是否为空 (无从站)
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

impl<'a> Index<usize> for Indexable<'a> {
    type Output = Slave;

    /// `view[i]` — 1-based 索引语义 (与 EtherCAT 从站编号一致)
    ///
    /// 越界会 panic, 与 `Vec::index` 行为一致. 想要安全访问请用
    /// `EtherCATMaster::try_slave(i)`.
    fn index(&self, idx: usize) -> &Self::Output {
        // SAFETY: 缓存只在 new() 时填充一次, 之后只读不写, 所以
        // 取裸引用安全; 不会因为 Vec 增长导致引用失效.
        let v = unsafe { &*self.cache.get() };
        &v[idx]
    }
}

impl<'a> Index<u16> for Indexable<'a> {
    type Output = Slave;

    /// `view[i_u16]` — 与 `Index<usize>` 同语义, 仅类型方便
    fn index(&self, idx: u16) -> &Self::Output {
        let v = unsafe { &*self.cache.get() };
        &v[idx as usize]
    }
}

// ===================== Master 端访问入口 =====================

/// 在 [`EtherCATMaster`] 上挂 `indexable()` 方法 (用扩展 trait 不动主类型).
pub trait MasterIndexExt {
    /// 返回索引视图, 使 `view[i]` 等价 `master.slave(i)`
    fn indexable(&self) -> Indexable<'_>;
}

impl MasterIndexExt for EtherCATMaster {
    fn indexable(&self) -> Indexable<'_> {
        Indexable::new(self)
    }
}

// 注: `Slave::new` 在主 crate 内为 `pub(crate)`, 本 sugar 模块同 crate 直接调用
// 即可, 无需新增任何 inherent 方法.