darra-ethercat-master 2.0.6

商业 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.
//! 主站诊断信息扩展
//!
//! 对应 C# Master/MasterDiagnosticsInfo.cs
//! 提供从站错误计数器读取、PDO丢帧诊断等功能。
//! 注: 滑动窗口采样逻辑在 diagnostics.rs 中已实现,
//! 此文件补充 C# 中独立的辅助结构和方法。

use crate::utils::ffi;

/// 从站错误计数器
///
/// 对应 C# SlaveErrorCounters
#[derive(Clone, Debug, Default)]
pub struct SlaveErrorCounters {
    /// 从站索引
    pub slave_index: u16,
    /// 端口0 CRC 错误数
    pub port0_crc_errors: u8,
    /// 端口1 CRC 错误数
    pub port1_crc_errors: u8,
    /// 端口2 CRC 错误数
    pub port2_crc_errors: u8,
    /// 端口3 CRC 错误数
    pub port3_crc_errors: u8,
    /// 帧错误数
    pub frame_errors: u8,
    /// 丢失帧数
    pub lost_frames: u8,
}

/// 读取指定从站的错误计数器 (寄存器 0x0300-0x030B)
///
/// 对应 C# MasterDiagnosticsInfo.ReadSlaveErrorCounters
pub fn read_slave_error_counters(master_index: u16, slave_index: u16) -> SlaveErrorCounters {
    let mut counters = SlaveErrorCounters {
        slave_index,
        ..Default::default()
    };

    // 读取 CRC 错误寄存器 (0x0300-0x0307, 每端口 2 字节)
    let mut crc_data = [0u8; 8];
    unsafe {
        if ffi::ReadSlaveRegister(
            master_index, slave_index, 0x0300,
            crc_data.as_mut_ptr(), 8,
        ) != 0 {
            counters.port0_crc_errors = crc_data[0];
            counters.port1_crc_errors = crc_data[2];
            counters.port2_crc_errors = crc_data[4];
            counters.port3_crc_errors = crc_data[6];
        }

        // 读取帧错误寄存器 (0x0308-0x030B)
        let mut frame_data = [0u8; 4];
        if ffi::ReadSlaveRegister(
            master_index, slave_index, 0x0308,
            frame_data.as_mut_ptr(), 4,
        ) != 0 {
            counters.frame_errors = frame_data[0];
            counters.lost_frames = frame_data[2];
        }
    }

    counters
}

/// PDO 丢帧统计
///
/// 对应 C# PDOFrameLossStats
#[derive(Clone, Debug, Default)]
pub struct PdoFrameLossInfo {
    /// 累计丢帧数
    pub total_lost: u32,
    /// 当前连续丢帧数
    pub consecutive_lost: u32,
    /// 历史最大连续丢帧数
    pub max_consecutive_lost: u32,
}

/// 获取指定组的 PDO 丢帧统计
pub fn get_pdo_frame_loss_stats(master_index: u16, group: u8) -> PdoFrameLossInfo {
    let mut total = 0u32;
    let mut consecutive = 0u32;
    let mut max_consecutive = 0u32;
    unsafe {
        ffi::GetPDOFrameLossStats(
            master_index, group,
            &mut total, &mut consecutive, &mut max_consecutive,
        );
    }
    PdoFrameLossInfo {
        total_lost: total,
        consecutive_lost: consecutive,
        max_consecutive_lost: max_consecutive,
    }
}

/// 获取所有组的 PDO 丢帧统计汇总
pub fn get_all_pdo_frame_loss_stats(master_index: u16) -> PdoFrameLossInfo {
    let mut total_sum = 0u32;
    let mut consecutive_max = 0u32;
    let mut max_consecutive_max = 0u32;

    for g in 0..8u8 {
        let stats = get_pdo_frame_loss_stats(master_index, g);
        total_sum += stats.total_lost;
        if stats.consecutive_lost > consecutive_max {
            consecutive_max = stats.consecutive_lost;
        }
        if stats.max_consecutive_lost > max_consecutive_max {
            max_consecutive_max = stats.max_consecutive_lost;
        }
    }

    PdoFrameLossInfo {
        total_lost: total_sum,
        consecutive_lost: consecutive_max,
        max_consecutive_lost: max_consecutive_max,
    }
}

/// 重置 PDO 丢帧统计
pub fn reset_pdo_frame_loss_stats(master_index: u16, group: u8) {
    unsafe { ffi::ResetPDOFrameLossStats(master_index, group); }
}

/// 重置所有组的 PDO 丢帧统计
pub fn reset_all_pdo_frame_loss_stats(master_index: u16) {
    for g in 0..8u8 {
        reset_pdo_frame_loss_stats(master_index, g);
    }
}