darra-ethercat-master 2.0.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.
//! 日志管理器 - 接收 DLL 回调, 推送到 LogView
//!
//! 对应 C# LogManager / LogView / LogEntry。
//!
//! # 使用方式
//! ```no_run
//! use ethercat::log_manager::{LogManager, LogCategory};
//!
//! let log_mgr = LogManager::instance();
//! log_mgr.add_log(LogCategory::Message, "主站已初始化");
//!
//! let view = log_mgr.default_view();
//! println!("日志数: {}", view.count());
//!
//! for entry in view.get_all() {
//!     println!("{}", entry);
//! }
//! ```

use std::fmt;
use std::sync::{Mutex, OnceLock};
use std::time::SystemTime;

// ===================== 日志类别 =====================

/// 日志类别 - 与 DLL 中的 LogCategory 对应
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LogCategory {
    /// 错误 - 系统错误和异常
    Error = 0,
    /// 警告 - 潜在问题
    Warning = 1,
    /// 消息 - 一般信息
    Message = 2,
    /// Mailbox - CoE/SoE/FoE/EoE 通信
    Mailbox = 3,
    /// PDO - 过程数据对象交换
    PDO = 4,
    /// 调试 - 详细调试信息
    Debug = 5,
    /// 本地 - 捕获的控制台输出
    Local = 6,
}

impl LogCategory {
    /// 所有类别
    pub fn all() -> &'static [LogCategory] {
        &[
            LogCategory::Error,
            LogCategory::Warning,
            LogCategory::Message,
            LogCategory::Mailbox,
            LogCategory::PDO,
            LogCategory::Debug,
            LogCategory::Local,
        ]
    }

    /// 从原始值创建
    pub fn from_value(v: i32) -> Option<Self> {
        match v {
            0 => Some(Self::Error),
            1 => Some(Self::Warning),
            2 => Some(Self::Message),
            3 => Some(Self::Mailbox),
            4 => Some(Self::PDO),
            5 => Some(Self::Debug),
            6 => Some(Self::Local),
            _ => None,
        }
    }
}

impl fmt::Display for LogCategory {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let name = match self {
            Self::Error => "ERROR",
            Self::Warning => "WARNING",
            Self::Message => "MESSAGE",
            Self::Mailbox => "MAILBOX",
            Self::PDO => "PDO",
            Self::Debug => "DEBUG",
            Self::Local => "LOCAL",
        };
        write!(f, "{}", name)
    }
}

// ===================== 日志条目 =====================

/// 日志条目 - 包含时间戳、类别和消息
#[derive(Debug, Clone)]
pub struct LogEntry {
    /// 日志时间戳
    pub timestamp: SystemTime,
    /// 日志类别
    pub category: LogCategory,
    /// 日志消息内容
    pub message: String,
}

impl LogEntry {
    /// 创建新日志条目 (自动记录当前时间)
    pub fn new(category: LogCategory, message: &str) -> Self {
        Self {
            timestamp: SystemTime::now(),
            category,
            message: message.to_string(),
        }
    }
}

impl fmt::Display for LogEntry {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // 格式化时间戳
        let duration = self.timestamp
            .duration_since(SystemTime::UNIX_EPOCH)
            .unwrap_or_default();
        let secs = duration.as_secs();
        let millis = duration.subsec_millis();

        // 简化时间格式 (Unix 时间戳, 因为不依赖 chrono)
        write!(f, "[{}.{:03}] [{}] {}", secs, millis, self.category, self.message)
    }
}

// ===================== 日志视图 =====================

/// 条目上限
const MAX_ENTRIES: usize = 10000;
/// 超过上限时移除最旧条目数
const TRIM_COUNT: usize = 2000;

/// 日志视图 - 只读列表, 由日志系统内部自动维护
///
/// 特点:
/// 1. 支持按日志类别过滤
/// 2. 超过上限自动移除最旧条目
/// 3. 线程安全 (内部 Mutex)
/// 日志更新回调类型
pub type LogUpdatedCallback = Box<dyn Fn(&LogEntry) + Send + Sync>;

pub struct LogView {
    /// 所有日志条目
    entries: Mutex<Vec<LogEntry>>,
    /// 过滤器 (允许的类别集合)
    filter: Mutex<Vec<LogCategory>>,
    /// 最大保留条数 (0 = 使用默认上限)
    retain: Mutex<usize>,
    /// 更新回调
    on_updated: Mutex<Option<LogUpdatedCallback>>,
}

impl LogView {
    /// 创建新视图 (默认显示所有类别)
    fn new() -> Self {
        Self {
            entries: Mutex::new(Vec::new()),
            filter: Mutex::new(LogCategory::all().to_vec()),
            retain: Mutex::new(0),
            on_updated: Mutex::new(None),
        }
    }

    /// 设置日志更新回调 (每次新增日志时触发)
    pub fn set_on_updated<F>(&self, callback: F)
    where
        F: Fn(&LogEntry) + Send + Sync + 'static,
    {
        let mut cb = self.on_updated.lock().unwrap();
        *cb = Some(Box::new(callback));
    }

    /// 清除日志更新回调
    pub fn clear_on_updated(&self) {
        let mut cb = self.on_updated.lock().unwrap();
        *cb = None;
    }

    /// 获取最大保留条数 (0 = 使用默认上限)
    pub fn retain_count(&self) -> usize {
        *self.retain.lock().unwrap()
    }

    /// 设置最大保留条数 (0 = 使用默认上限)
    pub fn set_retain_count(&self, count: usize) {
        let mut r = self.retain.lock().unwrap();
        *r = count;
    }

    /// 当前过滤后的日志条数
    pub fn count(&self) -> usize {
        let entries = self.entries.lock().unwrap();
        let filter = self.filter.lock().unwrap();
        entries.iter().filter(|e| filter.contains(&e.category)).count()
    }

    /// 获取所有日志条目 (无视过滤器, 用于导出)
    pub fn get_all(&self) -> Vec<LogEntry> {
        self.entries.lock().unwrap().clone()
    }

    /// 获取过滤后的日志条目
    pub fn get_filtered(&self) -> Vec<LogEntry> {
        let entries = self.entries.lock().unwrap();
        let filter = self.filter.lock().unwrap();
        entries.iter()
            .filter(|e| filter.contains(&e.category))
            .cloned()
            .collect()
    }

    /// 设置过滤器 - 只显示指定类别的日志
    pub fn set_filter(&self, categories: &[LogCategory]) {
        let mut filter = self.filter.lock().unwrap();
        *filter = categories.to_vec();
    }

    /// 重置过滤器 (显示所有类别)
    pub fn reset_filter(&self) {
        let mut filter = self.filter.lock().unwrap();
        *filter = LogCategory::all().to_vec();
    }

    /// 添加日志条目 (内部使用)
    fn add(&self, entry: LogEntry) {
        // 触发更新回调
        if let Ok(cb) = self.on_updated.lock() {
            if let Some(ref callback) = *cb {
                callback(&entry);
            }
        }

        let mut entries = self.entries.lock().unwrap();
        entries.push(entry);

        // 计算上限: 自定义 retain 或默认上限
        let max = {
            let r = self.retain.lock().unwrap();
            if *r > 0 { *r } else { MAX_ENTRIES }
        };
        let trim = if max == MAX_ENTRIES { TRIM_COUNT } else { max / 5 }; // 移除 20%
        if entries.len() > max {
            entries.drain(..trim.max(1));
        }
    }

    /// 清空所有条目 (内部使用)
    fn clear_inner(&self) {
        self.entries.lock().unwrap().clear();
    }
}

// ===================== 日志管理器 (全局单例) =====================

/// 日志管理器 - 接收 DLL 回调, 推送到 LogView
///
/// 通过 `LogManager::instance()` 获取全局单例。
pub struct LogManager {
    /// 默认日志视图
    default_view: LogView,
}

/// 全局单例
static INSTANCE: OnceLock<LogManager> = OnceLock::new();

impl LogManager {
    /// 获取全局单例
    pub fn instance() -> &'static LogManager {
        INSTANCE.get_or_init(|| LogManager {
            default_view: LogView::new(),
        })
    }

    /// 获取默认日志视图
    pub fn default_view(&self) -> &LogView {
        &self.default_view
    }

    /// 添加日志条目 (由 DLL 回调或内部调用)
    pub fn add_log(&self, category: LogCategory, message: &str) {
        let entry = LogEntry::new(category, message);
        self.default_view.add(entry);
    }

    /// 清空所有日志
    pub fn clear(&self) {
        self.default_view.clear_inner();
    }
}

// LogManager 只包含线程安全字段,Send/Sync 由编译器自动推导。