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
//! 从站诊断信息
//!
//! 提供 SlaveDiagnostics 和 SlaveDCDiagnostics 结构体,
//! 封装 ESC 端口错误计数器和 DC 同步诊断。

use crate::data::error::SyncWindowStatus;
use crate::utils::ffi;

/// ESC 端口错误计数器 (ETG.1000 5.3, 寄存器 0x0300-0x0313)
#[derive(Debug, Clone)]
pub struct EscPortErrors {
    /// 各端口 RX 错误计数 [Port0..Port3]
    pub rx_error: [u8; 4],
    /// 各端口无效帧计数 [Port0..Port3]
    pub invalid_frame: [u8; 4],
    /// 各端口链路丢失计数 [Port0..Port3]
    pub lost_link: [u8; 4],
}

impl EscPortErrors {
    /// 是否存在任何错误
    pub fn has_errors(&self) -> bool {
        self.rx_error.iter().any(|&b| b > 0)
            || self.invalid_frame.iter().any(|&b| b > 0)
            || self.lost_link.iter().any(|&b| b > 0)
    }
}

/// 从站 DC 同步诊断
pub struct SlaveDCDiagnostics {
    master_index: u16,
    slave_index: u16,
}

impl SlaveDCDiagnostics {
    /// 创建 DC 诊断实例 (内部使用)
    pub(crate) fn new(master_index: u16, slave_index: u16) -> Self {
        Self { master_index, slave_index }
    }

    /// 是否在同步窗口内
    pub fn is_in_sync(&self) -> bool {
        let mut diff_ns: i32 = 0;
        let mut max_diff_ns: i32 = 0;
        let mut min_diff_ns: i32 = 0;
        let mut in_sync: i32 = 0;
        let mut out_of_sync_count: u32 = 0;
        let ret = unsafe {
            ffi::GetSlaveSyncWindowStatus(
                self.master_index, self.slave_index,
                &mut diff_ns, &mut max_diff_ns, &mut min_diff_ns,
                &mut in_sync, &mut out_of_sync_count,
            )
        };
        ret != 0 && in_sync != 0
    }

    /// 当前时间差 (纳秒) - 0x092C System Time Difference 寄存器
    pub fn sync_time_difference(&self) -> i32 {
        let mut diff_ns: i32 = 0;
        let mut max_diff_ns: i32 = 0;
        let mut min_diff_ns: i32 = 0;
        let mut in_sync: i32 = 0;
        let mut out_of_sync_count: u32 = 0;
        let ret = unsafe {
            ffi::GetSlaveSyncWindowStatus(
                self.master_index, self.slave_index,
                &mut diff_ns, &mut max_diff_ns, &mut min_diff_ns,
                &mut in_sync, &mut out_of_sync_count,
            )
        };
        if ret != 0 { diff_ns } else { 0 }
    }

    /// 获取完整的同步窗口状态
    pub fn sync_window_status(&self) -> Option<SyncWindowStatus> {
        let mut diff_ns: i32 = 0;
        let mut max_diff_ns: i32 = 0;
        let mut min_diff_ns: i32 = 0;
        let mut in_sync: i32 = 0;
        let mut out_of_sync_count: u32 = 0;
        let ret = unsafe {
            ffi::GetSlaveSyncWindowStatus(
                self.master_index, self.slave_index,
                &mut diff_ns, &mut max_diff_ns, &mut min_diff_ns,
                &mut in_sync, &mut out_of_sync_count,
            )
        };
        if ret != 0 {
            Some(SyncWindowStatus {
                diff_ns,
                max_diff_ns,
                min_diff_ns,
                in_sync: in_sync != 0,
                out_of_sync_count,
            })
        } else {
            None
        }
    }

    /// 重置同步窗口统计
    pub fn reset_stats(&self) {
        unsafe { ffi::ResetSlaveSyncWindowStats(self.master_index, self.slave_index) };
    }
}

/// 从站诊断信息
pub struct SlaveDiagnostics {
    master_index: u16,
    slave_index: u16,
}

impl SlaveDiagnostics {
    /// 创建从站诊断实例 (内部使用)
    pub(crate) fn new(master_index: u16, slave_index: u16) -> Self {
        Self { master_index, slave_index }
    }

    /// 获取 DC 同步诊断
    pub fn dc(&self) -> SlaveDCDiagnostics {
        SlaveDCDiagnostics::new(self.master_index, self.slave_index)
    }

    /// 读取 ESC 端口错误计数器 (寄存器 0x0300-0x0313)
    pub fn read_port_errors(&self) -> Option<EscPortErrors> {
        let mut rx_error = [0u8; 4];
        let mut invalid_frame = [0u8; 4];
        let mut lost_link = [0u8; 4];
        let ret = unsafe {
            ffi::ReadSlavePortErrorCounters(
                self.master_index, self.slave_index,
                rx_error.as_mut_ptr(), invalid_frame.as_mut_ptr(), lost_link.as_mut_ptr(),
            )
        };
        if ret != 0 {
            Some(EscPortErrors { rx_error, invalid_frame, lost_link })
        } else {
            None
        }
    }

    /// 重置端口错误计数器
    pub fn reset_port_errors(&self) -> bool {
        unsafe { ffi::ResetSlavePortErrorCounters(self.master_index, self.slave_index) != 0 }
    }

    /// 获取从站链路质量 (0-100%)
    pub fn link_quality(&self) -> i16 {
        unsafe { ffi::GetSlaveLinkQuality(self.master_index, self.slave_index) }
    }

    /// 冗余是否已激活 (需要双网卡配置)
    pub fn redundancy_activated(&self) -> bool {
        // 通过主站级冗余状态判断
        let mode = unsafe { ffi::GetRingMode(self.master_index) };
        mode == 1 // Dual 模式
    }

    /// 主链路是否断开 (基于端口错误检测)
    pub fn primary_link_broken(&self) -> bool {
        if let Some(errors) = self.read_port_errors() {
            // Port 0 通常为上行链路 (主链路)
            errors.lost_link[0] > 0
        } else {
            false
        }
    }

    /// 副链路是否断开 (基于端口错误检测)
    pub fn secondary_link_broken(&self) -> bool {
        if let Some(errors) = self.read_port_errors() {
            // Port 1 通常为下行链路 (副链路/冗余链路)
            errors.lost_link[1] > 0
        } else {
            false
        }
    }
}