darra-ethercat-master 2.0.0

商业 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
//! EMCY 紧急消息记录扩展
//!
//! 对应 C# Master/Events_EmcyRecorder.cs
//! 将 EMCY 事件记录到对应从站的 CoE 历史中。

use std::collections::VecDeque;
use std::sync::Mutex;
use std::time::SystemTime;

/// 紧急消息数据结构 (CiA 301)
///
/// 对应 C# EmergencyMessage
#[derive(Clone, Debug)]
pub struct EmergencyMessage {
    /// 紧急错误代码 (CiA 301 Table 24)
    pub error_code: u16,
    /// 错误寄存器 (对象 0x1001)
    pub error_register: u8,
    /// 厂商特定数据 (5 字节)
    pub data: [u8; 5],
    /// 来源从站编号
    pub slave_index: u16,
    /// 接收时间
    pub timestamp: SystemTime,
}

impl EmergencyMessage {
    /// 错误代码的文本描述 (CiA 301 标准错误分类)
    pub fn error_description(&self) -> &str {
        match self.error_code >> 8 {
            0x00 => "已复位/无错误",
            0x10 => "通用错误",
            0x20 => "电流错误",
            0x21 => "设备输入侧电流错误",
            0x22 => "设备内部电流错误",
            0x23 => "设备输出侧电流错误",
            0x30 => "电压错误",
            0x31 => "主电源电压错误",
            0x32 => "设备内部电压错误",
            0x33 => "输出电压错误",
            0x40 => "温度错误",
            0x41 => "环境温度错误",
            0x42 => "设备温度错误",
            0x50 => "设备硬件错误",
            0x60 => "设备软件错误",
            0x61 => "内部软件错误",
            0x62 => "用户软件错误",
            0x63 => "数据集错误",
            0x70 => "附加模块错误",
            0x80 => "监控错误",
            0x81 => "通信错误",
            0x82 => "协议错误",
            0x90 => "外部错误",
            0xF0 => "附加功能错误",
            0xFF => "设备特定错误",
            _ => "未知错误类别",
        }
    }
}

impl std::fmt::Display for EmergencyMessage {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "EMCY[从站{}] 0x{:04X} ({}) 寄存器=0x{:02X}",
            self.slave_index, self.error_code,
            self.error_description(), self.error_register
        )
    }
}

/// EMCY 历史记录管理器
///
/// 对应 C# CoEInstance 的 EMCY 历史记录部分
pub struct EmcyRecorder {
    history: Mutex<VecDeque<EmergencyMessage>>,
    max_size: usize,
}

/// 最大 EMCY 历史记录数
const MAX_EMCY_HISTORY_SIZE: usize = 256;

impl EmcyRecorder {
    /// 创建新的 EMCY 记录器
    pub fn new() -> Self {
        Self {
            history: Mutex::new(VecDeque::with_capacity(MAX_EMCY_HISTORY_SIZE)),
            max_size: MAX_EMCY_HISTORY_SIZE,
        }
    }

    /// 记录一条 EMCY 紧急消息
    pub fn record(
        &self,
        slave_index: u16,
        error_code: u16,
        error_reg: u16,
        b1: u8,
        w1: u16,
        w2: u16,
    ) {
        let msg = EmergencyMessage {
            error_code,
            error_register: (error_reg & 0xFF) as u8,
            data: [
                b1,
                (w1 & 0xFF) as u8,
                (w1 >> 8) as u8,
                (w2 & 0xFF) as u8,
                (w2 >> 8) as u8,
            ],
            slave_index,
            timestamp: SystemTime::now(),
        };

        if let Ok(mut history) = self.history.lock() {
            if history.len() >= self.max_size {
                history.pop_front();
            }
            history.push_back(msg);
        }
    }

    /// EMCY 历史记录(副本)
    pub fn history(&self) -> Vec<EmergencyMessage> {
        self.history
            .lock()
            .map(|h| h.iter().cloned().collect())
            .unwrap_or_default()
    }

    /// 清除 EMCY 历史记录
    pub fn clear_history(&self) {
        if let Ok(mut history) = self.history.lock() {
            history.clear();
        }
    }

    /// 获取历史记录数量
    pub fn count(&self) -> usize {
        self.history.lock().map(|h| h.len()).unwrap_or(0)
    }
}

impl Default for EmcyRecorder {
    fn default() -> Self {
        Self::new()
    }
}