darra-ethercat-master 2.1.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.
//! 网络信息与扫描
//!
//! 提供网络适配器信息、从站扫描信息等静态查询功能。
//! 对应 C# `Static/Network.cs`。

use crate::utils::ffi;
#[allow(unused_imports)]
use std::ffi::{CStr, CString};
use std::os::raw::c_char;

/// 网络适配器信息
#[derive(Debug, Clone)]
pub struct NetworkInfo {
    /// 适配器名称 (设备路径)
    pub name: String,
    /// 适配器描述
    pub description: String,
    /// MAC 地址
    pub mac: [u8; 6],
}

impl NetworkInfo {
    /// 获取 MAC 地址的格式化字符串
    pub fn mac_string(&self) -> String {
        format!("{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
            self.mac[0], self.mac[1], self.mac[2],
            self.mac[3], self.mac[4], self.mac[5])
    }
}

/// 获取所有可用的网络适配器信息
///
/// 注意: DLL 未导出 GetAdapterCount/GetAdapterInfo,
/// 请通过 GetNetworksPointer 获取网络信息。
/// 当前返回空列表。
pub fn get_network_adapters() -> Vec<NetworkInfo> {
    // DLL 未导出 GetAdapterCount/GetAdapterInfo, 暂返回空列表
    Vec::new()
}

/// 物理网卡条目 (含从站数), 与 C/C# `ec_network_info_t` 二进制对齐
/// 结构: name[128] + desc[128] + slaveNum(int16) + redundantSlaveNum(int16), 共 260 字节
#[derive(Debug, Clone)]
pub struct EnumeratedNetworkInfo {
    /// 适配器设备路径 (\Device\NPF_{GUID})
    pub name: String,
    /// 适配器描述
    pub description: String,
    /// 该网卡上发现的从站数
    pub slave_num: i16,
    /// 该网卡参与冗余对时受冗余保护的从站数
    pub redundant_slave_num: i16,
}

/// 调用 DLL `GetNetworkInfo` + `GetNetworksPointer`, 与 C# `DarraEtherCAT.GetNetworkInfo` 等价
///
/// 参数:
/// - `is_redundant`: 是否启用 QFB 冗余探测 (建议 false, 单口枚举)
/// - `need_slaves_num`: 是否扫描从站数 (建议 true, 否则 slave_num/redundant 都为 0)
///
/// 返回所有物理 + 虚拟网卡条目 (调用方自行过滤虚拟网卡).
pub fn enumerate_network_info(is_redundant: bool, need_slaves_num: bool) -> Vec<EnumeratedNetworkInfo> {
    let count = unsafe { ffi::GetNetworkInfo(is_redundant as i32, need_slaves_num as i32) };
    if count <= 0 {
        return Vec::new();
    }
    let ptr = unsafe { ffi::GetNetworksPointer() } as *const u8;
    if ptr.is_null() {
        return Vec::new();
    }
    // 与 C# ec_networkInfo Pack=1 对齐: 128 + 128 + int16 + int16 = 260 字节
    const STRIDE: usize = 128 + 128 + 2 + 2;
    let mut result = Vec::with_capacity(count as usize);
    for i in 0..count as usize {
        unsafe {
            let base = ptr.add(i * STRIDE);
            let name_slice = std::slice::from_raw_parts(base, 128);
            let desc_slice = std::slice::from_raw_parts(base.add(128), 128);
            let slave_num = std::ptr::read_unaligned(base.add(256) as *const i16);
            let red_num = std::ptr::read_unaligned(base.add(258) as *const i16);
            let name_end = name_slice.iter().position(|&b| b == 0).unwrap_or(128);
            let desc_end = desc_slice.iter().position(|&b| b == 0).unwrap_or(128);
            let name = String::from_utf8_lossy(&name_slice[..name_end]).to_string();
            let desc = String::from_utf8_lossy(&desc_slice[..desc_end]).to_string();
            result.push(EnumeratedNetworkInfo {
                name,
                description: desc,
                slave_num,
                redundant_slave_num: red_num,
            });
        }
    }
    result
}

/// 扫描到的从站信息
#[derive(Debug, Clone)]
pub struct ScannedSlaveInfo {
    /// 从站索引 (1-based)
    pub index: u16,
    /// 厂商 ID
    pub vendor_id: u32,
    /// 产品代码
    pub product_code: u32,
    /// 修订号
    pub revision: u32,
    /// 序列号
    pub serial: u32,
    /// 从站名称
    pub name: String,
    /// 配置地址
    pub config_addr: u16,
    /// 别名地址
    pub alias_addr: u16,
    /// 父节点索引
    pub parent_index: u16,
    /// 拓扑类型
    pub topology: u8,
    /// 活跃端口
    pub active_ports: u8,
    /// 入口端口
    pub entry_port: u8,
    /// 父端口
    pub parent_port: u8,
    /// 物理类型
    pub physical_type: u8,
}

impl ScannedSlaveInfo {
    /// 别名地址十六进制字符串 (对应 C# AliasAddrHex)
    pub fn alias_addr_hex(&self) -> String {
        format!("0x{:04X}", self.alias_addr)
    }

    /// 厂商ID十六进制字符串 (对应 C# VendorIDHex)
    pub fn vendor_id_hex(&self) -> String {
        format!("0x{:08X}", self.vendor_id)
    }

    /// 是否为耦合器设备 (静态设备, 无IO映射, 如 EK1100)
    pub fn is_coupler(&self) -> bool {
        // 耦合器通常没有PDO映射, 是静态设备
        self.physical_type == 1
    }

    /// 获取设备类型
    pub fn device_type(&self) -> u8 {
        self.physical_type
    }

    /// 获取拓扑深度 (到主站的距离)
    pub fn depth(&self) -> u16 {
        // 简单估算: 基于从站索引和父节点关系
        if self.parent_index == 0 || self.parent_index == self.index {
            0
        } else {
            1 // 实际深度需要通过 SlaveTopology 计算
        }
    }
}

/// 扫描从站并返回详细信息列表
///
/// 必须先调用 `EtherCATMaster::read_slave_info()` 扫描从站
pub fn get_scanned_slaves() -> Vec<ScannedSlaveInfo> {
    let count = unsafe { ffi::GetScannedSlaveCount() };
    if count <= 0 {
        return Vec::new();
    }

    let mut slaves = Vec::with_capacity(count as usize);
    for i in 0..count {
        let mut vendor_id: u32 = 0;
        let mut product_code: u32 = 0;
        let mut revision: u32 = 0;
        let mut serial: u32 = 0;
        let mut name_buf = vec![0u8; 256];
        let mut config_addr: u16 = 0;
        let mut alias_addr: u16 = 0;
        let mut parent: u16 = 0;
        let mut topology: u8 = 0;
        let mut active_ports: u8 = 0;
        let mut entry_port: u8 = 0;
        let mut parent_port: u8 = 0;
        let mut ptype: u8 = 0;

        let ret = unsafe {
            ffi::GetScannedSlaveInfo(
                i,
                &mut vendor_id, &mut product_code, &mut revision, &mut serial,
                name_buf.as_mut_ptr() as *mut c_char, name_buf.len() as i32,
                &mut config_addr, &mut alias_addr,
                &mut parent, &mut topology, &mut active_ports,
                &mut entry_port, &mut parent_port, &mut ptype,
            )
        };

        if ret != 0 {
            let null_pos = name_buf.iter().position(|&b| b == 0).unwrap_or(name_buf.len());
            let name = String::from_utf8_lossy(&name_buf[..null_pos]).to_string();
            slaves.push(ScannedSlaveInfo {
                index: (i + 1) as u16,
                vendor_id,
                product_code,
                revision,
                serial,
                name,
                config_addr,
                alias_addr,
                parent_index: parent,
                topology,
                active_ports,
                entry_port,
                parent_port,
                physical_type: ptype,
            });
        }
    }
    slaves
}

/// 获取环形拓扑从站数量
pub fn ring_slave_count() -> i32 {
    unsafe { ffi::GetRingSlaveCount() }
}

/// 获取已扫描的从站数量
pub fn scanned_slave_count() -> i32 {
    unsafe { ffi::GetScannedSlaveCount() }
}

/// 耦合器状态信息
#[derive(Debug, Clone)]
pub struct CouplerStatus {
    /// 从站索引
    pub index: u16,
    /// 从站名称
    pub name: String,
    /// 下挂子模块数量
    pub child_count: usize,
}

/// 检测 EtherCAT 耦合器状态 (对应 C# DetermineCouplerStatus)
///
/// 遍历从站列表, 识别耦合器并统计子模块数量。
pub fn determine_coupler_status(slaves: &[ScannedSlaveInfo]) -> Vec<CouplerStatus> {
    let mut results = Vec::new();
    for s in slaves {
        if !s.is_coupler() {
            continue;
        }
        let child_count = slaves.iter().filter(|c| c.parent_index == s.index).count();
        results.push(CouplerStatus {
            index: s.index,
            name: s.name.clone(),
            child_count,
        });
    }
    results
}