darra-ethercat-master 2.0.2

商业 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/State.cs
//! 包含状态切换、中止操作、CPU亲和性等功能。

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

/// 主站状态管理扩展方法
///
/// 这些方法作为独立函数提供,与 EtherCATMaster 配合使用。
/// 对应 C# DarraEtherCAT 的状态管理 partial class。

/// 中断 DLL 层所有阻塞操作
///
/// 可安全地从任意线程调用,用于打断正在进行的 SetState/SetNetwork 等。
/// 对应 C# Abort()
pub fn abort() {
    unsafe { ffi::AbortNetwork(); }
}

/// 重置中止标志
///
/// 在中止后需要恢复操作时调用。
pub fn reset_abort() {
    unsafe { ffi::ResetAbortNetwork(); }
}

/// 中止网络扫描
pub fn abort_scan() {
    unsafe { ffi::AbortScan(); }
}

/// 重置扫描中止标志
pub fn reset_scan_abort() {
    unsafe { ffi::ResetScanAbort(); }
}

/// 紧急关闭所有网卡
pub fn emergency_close_nics() {
    unsafe { ffi::EmergencyCloseNics(); }
}

/// 获取系统可用的 CPU 核心数
pub fn get_cpu_cores() -> i32 {
    unsafe { ffi::GetAvailableCpuCores() }
}

/// 设置主站线程 CPU 亲和性
///
/// 以基准核心为起点,向低核心号分配:
/// PDO(基准) -> DC诊断(基准-1) -> StateGuard(基准-2)
pub fn set_cpu_affinity(master_index: u16, base_cpu_core: i32) -> bool {
    unsafe { ffi::SetMasterCpuAffinity(master_index, base_cpu_core) != 0 }
}

/// 设置进程 CPU 亲和性
pub fn set_process_cpu_affinity(cpu_core: i32) -> bool {
    unsafe { ffi::SetProcessCpuAffinity(cpu_core) != 0 }
}

/// 应用默认实时优化(高精度定时器、内存锁定、进程高优先级)
pub fn apply_realtime_optimizations() -> bool {
    unsafe { ffi::ApplyRealtimeOptimizations() != 0 }
}

/// 移除实时优化
pub fn remove_realtime_optimizations() -> bool {
    unsafe { ffi::RemoveRealtimeOptimizations() != 0 }
}

/// 获取实时优化状态
pub fn get_realtime_optimizations_status() -> bool {
    unsafe { ffi::GetRealtimeOptimizationsStatus() != 0 }
}

/// 批量设置所有从站的过程数据看门狗超时
///
/// 返回成功设置的从站数量。0 = 禁用。
pub fn set_all_slave_watchdog(master_index: u16, timeout_ms: u32) -> i32 {
    unsafe { ffi::SetAllSlaveWatchdog(master_index, timeout_ms) }
}

/// 批量设置所有从站的PDI看门狗超时
///
/// 返回成功设置的从站数量。0 = 禁用。
pub fn set_all_slave_pdi_watchdog(master_index: u16, timeout_ms: u32) -> i32 {
    unsafe { ffi::SetAllSlavePdiWatchdog(master_index, timeout_ms) }
}

/// 获取主站最大实例数
pub fn get_max_master_instances() -> i32 {
    unsafe { ffi::GetMaxMasterInstances() }
}

/// 自动选择 CPU 核心并设置亲和性
///
/// 策略:PDO 独占最高核心,DcDiag+StateGuard 共用次高核心。
/// 每个实例仅占用 2 个核心,从最高核心向下分配。
pub fn apply_auto_cpu_affinity(master_index: u16) -> bool {
    let cpu_cores = get_cpu_cores();
    if cpu_cores < 2 {
        return false;
    }

    let cores_per_instance = 2;
    let instance_offset = (master_index as i32 - 1) * cores_per_instance;
    let mut base_cpu_core = (cpu_cores - 1) - instance_offset;

    // 确保基准核心至少留出 1 个低位核心给监控线程
    if base_cpu_core < 1 {
        base_cpu_core = std::cmp::min(1, cpu_cores - 1);
    }
    if base_cpu_core >= cpu_cores {
        base_cpu_core = cpu_cores - 1;
    }
    if base_cpu_core < 0 {
        base_cpu_core = 0;
    }

    set_cpu_affinity(master_index, base_cpu_core)
}

/// 初始化控制台信息
pub fn init_console_info(_log_level: i32) {
    // 日志级别设置(当前为存根实现)
}

/// 验证启动配置结果
#[derive(Debug, Clone)]
pub struct StartupVerifyResult {
    /// 整体是否有效
    pub valid: bool,
    /// 在线从站数
    pub slave_count: u16,
    /// 问题描述列表
    pub issues: Vec<String>,
}

/// 轮询等待主站到达指定状态 (对齐 C# WaitForState / C++ master_state::WaitForState)
///
/// 不发起状态切换, 仅等待已请求的转换完成 / PDO 流稳定确认。
///
/// # 参数
/// - `master_index`:    主站编号
/// - `target`:          目标状态
/// - `timeout_ms`:      最大等待 (毫秒)
/// - `poll_interval_ms`: 轮询间隔 (毫秒)
///
/// # 返回
/// 是否到达目标状态
pub fn wait_for_state(master_index: u16, target: EcState,
                      timeout_ms: u32, poll_interval_ms: u32) -> bool {
    use std::time::{Duration, Instant};
    let deadline = Instant::now() + Duration::from_millis(timeout_ms as u64);
    let interval = Duration::from_millis(poll_interval_ms.max(1) as u64);
    let target_raw = target as u8;
    loop {
        // 主站状态: 头部第一个字段 (uint16) 即 主站状态.State
        let ptr = unsafe { ffi::GetMasterState(master_index) };
        if !ptr.is_null() {
            let cur = unsafe { *(ptr as *const u16) } as u8;
            if (cur & 0x0F) == (target_raw & 0x0F) {
                return true;
            }
        }
        if Instant::now() >= deadline {
            // 最后一次确认
            let ptr = unsafe { ffi::GetMasterState(master_index) };
            if !ptr.is_null() {
                let cur = unsafe { *(ptr as *const u16) } as u8;
                return (cur & 0x0F) == (target_raw & 0x0F);
            }
            return false;
        }
        std::thread::sleep(interval);
    }
}

/// 轮询等待单个从站到达指定状态 (PDO 启动后状态确认场景)
///
/// # 参数
/// - `master_index`:     主站编号
/// - `slave_index`:      从站编号 (1-based)
/// - `target`:           目标状态
/// - `timeout_ms`:       最大等待 (毫秒)
/// - `poll_interval_ms`: 轮询间隔 (毫秒)
pub fn wait_for_slave_state(master_index: u16, slave_index: u16, target: EcState,
                             timeout_ms: u32, poll_interval_ms: u32) -> bool {
    use std::time::{Duration, Instant};
    let deadline = Instant::now() + Duration::from_millis(timeout_ms as u64);
    let interval = Duration::from_millis(poll_interval_ms.max(1) as u64);
    let target_raw = (target as u8) & 0x0F;
    loop {
        let cur = unsafe { ffi::GetSlaveState(master_index, slave_index) } & 0x0F;
        if cur == target_raw {
            return true;
        }
        if Instant::now() >= deadline {
            let final_state = unsafe { ffi::GetSlaveState(master_index, slave_index) } & 0x0F;
            return final_state == target_raw;
        }
        std::thread::sleep(interval);
    }
}

/// 验证启动配置 (对应 C# VerifyStartupConfiguration)
///
/// 检查从站状态、PDO 映射、DC 配置是否正确。
pub fn verify_startup_configuration(master_index: u16) -> StartupVerifyResult {
    let mut issues = Vec::new();
    let mut slave_count: u16 = 0;

    // 1. 检查链路状态
    let link = unsafe { ffi::GetLinkStatus(master_index) };
    if link == 0 {
        issues.push("链路未连接".to_string());
    }

    // 2. 逐从站检查
    for si in 1u16..256 {
        let s_state = unsafe { ffi::GetSlaveState(master_index, si) };
        if s_state == 0 {
            break;
        }
        slave_count += 1;
        let base = s_state & 0x0F;
        if base < 2 {
            issues.push(format!("从站 {} 状态异常: 0x{:02X}", si, s_state));
        }
        if (s_state & 0x10) != 0 {
            let al_code = unsafe { ffi::GetSlaveALStatusCode(master_index, si) };
            issues.push(format!("从站 {} 有 AL 错误: 0x{:04X}", si, al_code));
        }
    }

    // 3. 配置验证
    let result = unsafe { ffi::ec_validate_config(master_index) };
    if result != 0 {
        issues.push(format!("配置验证失败: 错误码 {}", result));
    }

    StartupVerifyResult {
        valid: issues.is_empty(),
        slave_count,
        issues,
    }
}