darra-ethercat-master 1.99.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.
Documentation
//! 网络拓扑管理
//!
//! 提供 SlaveTopology 结构体,封装从站网络拓扑的构建与遍历。
//! 对应 C# `Slave/Topology.cs`。

use crate::utils::ffi::{self, TopologyNode};
use std::collections::HashMap;

/// 从站拓扑管理器
pub struct SlaveTopology {
    /// 所有拓扑节点
    nodes: Vec<TopologyNode>,
    /// 子节点映射 (parent_index -> children)
    children_map: HashMap<u16, Vec<u16>>,
    /// 根节点列表 (无父节点的从站)
    root_slaves: Vec<u16>,
}

impl SlaveTopology {
    /// 构建网络拓扑
    pub fn build(master_index: u16) -> Self {
        let mut buf = vec![TopologyNode {
            slave_index: 0, config_addr: 0, parent_index: 0,
            entry_port: 0, active_ports: 0, topology: 0, port_type: 0,
        }; 512];

        let count = unsafe { ffi::TopologyBuild(master_index, buf.as_mut_ptr(), 512) };
        let count = count.max(0) as usize;
        buf.truncate(count);

        let mut children_map: HashMap<u16, Vec<u16>> = HashMap::new();
        let mut root_slaves = Vec::new();

        for node in &buf {
            if node.parent_index == 0 || node.parent_index == node.slave_index {
                root_slaves.push(node.slave_index);
            } else {
                children_map.entry(node.parent_index)
                    .or_insert_with(Vec::new)
                    .push(node.slave_index);
            }
        }

        Self {
            nodes: buf,
            children_map,
            root_slaves,
        }
    }

    /// 获取所有拓扑节点
    pub fn nodes(&self) -> &[TopologyNode] {
        &self.nodes
    }

    /// 获取节点数量
    pub fn count(&self) -> usize {
        self.nodes.len()
    }

    /// 获取根节点列表 (无父节点的从站)
    pub fn root_slaves(&self) -> &[u16] {
        &self.root_slaves
    }

    /// 获取指定从站的子节点
    pub fn children(&self, parent_index: u16) -> &[u16] {
        self.children_map.get(&parent_index).map(|v| v.as_slice()).unwrap_or(&[])
    }

    /// 获取指定从站的子节点数量
    pub fn child_count(&self, parent_index: u16) -> usize {
        self.children_map.get(&parent_index).map(|v| v.len()).unwrap_or(0)
    }

    /// 按索引获取拓扑节点
    pub fn get(&self, slave_index: u16) -> Option<&TopologyNode> {
        self.nodes.iter().find(|n| n.slave_index == slave_index)
    }

    /// 按配置地址查找拓扑节点
    ///
    /// 支持十六进制字符串 (如 "0x1001") 或十进制数字。
    pub fn get_by_address(&self, address: &str) -> Option<&TopologyNode> {
        let addr = if let Some(hex) = address.strip_prefix("0x").or_else(|| address.strip_prefix("0X")) {
            u16::from_str_radix(hex, 16).ok()?
        } else {
            address.parse::<u16>().ok()?
        };
        self.nodes.iter().find(|n| n.config_addr == addr)
    }

    /// 获取从站的父节点索引
    pub fn parent_of(&self, slave_index: u16) -> Option<u16> {
        self.get(slave_index).map(|n| n.parent_index)
    }

    /// 获取从站的深度 (到根节点的距离)
    pub fn depth(&self, slave_index: u16) -> u32 {
        let mut depth = 0u32;
        let mut current = slave_index;
        let mut visited = std::collections::HashSet::new();
        while let Some(node) = self.get(current) {
            if node.parent_index == 0 || node.parent_index == current || visited.contains(&current) {
                break;
            }
            visited.insert(current);
            current = node.parent_index;
            depth += 1;
        }
        depth
    }

    /// 遍历拓扑树 (深度优先)
    pub fn walk_dfs<F>(&self, mut visitor: F)
    where
        F: FnMut(u16, u32), // (slave_index, depth)
    {
        for &root in &self.root_slaves {
            self.walk_dfs_recursive(root, 0, &mut visitor);
        }
    }

    fn walk_dfs_recursive<F>(&self, slave_index: u16, depth: u32, visitor: &mut F)
    where
        F: FnMut(u16, u32),
    {
        visitor(slave_index, depth);
        if let Some(children) = self.children_map.get(&slave_index) {
            for &child in children {
                self.walk_dfs_recursive(child, depth + 1, visitor);
            }
        }
    }
}