darra-ethercat-master 2.7.0

Commercial EtherCAT master protocol stack, real-time kernel driver integration, Windows and Linux support, multi-language SDKs, complex topology and hot-plug support.
Documentation

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

pub struct SlaveTopology {

    nodes: Vec<TopologyNode>,

    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)
    }

    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),
    {
        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);
            }
        }
    }
}