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;
use crate::data::error::EcState;

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

pub fn get_cpu_cores() -> i32 {
    unsafe { ffi::GetAvailableCpuCores() }
}

pub fn set_cpu_affinity(master_index: u16, base_cpu_core: i32) -> bool {
    unsafe { ffi::SetMasterCpuAffinity(master_index, base_cpu_core) != 0 }
}

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

pub fn set_all_slave_watchdog(master_index: u16, timeout_ms: u32) -> i32 {
    unsafe { ffi::SetAllSlaveWatchdog(master_index, timeout_ms) }
}

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

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;

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

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 {

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

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

pub fn verify_startup_configuration(master_index: u16) -> StartupVerifyResult {
    let mut issues = Vec::new();
    let mut slave_count: u16 = 0;

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

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

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

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

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AlStatusClass {

    NoError,

    Configuration,

    Communication,

    Watchdog,

    VendorSpecific,

    Unknown(i32),
}

impl AlStatusClass {

    fn from_raw(v: i32) -> Self {
        match v {
            0 => Self::NoError,
            1 => Self::Configuration,
            2 => Self::Communication,
            3 => Self::Watchdog,
            4 => Self::VendorSpecific,
            other => Self::Unknown(other),
        }
    }
}

pub fn esm_is_legal_transition(from: EcState, to: EcState) -> bool {
    unsafe { ffi::EsmIsLegalTransition(from as i32, to as i32) != 0 }
}

pub fn esm_legal_transitions(from: EcState) -> Vec<EcState> {
    const MAX: usize = 8;
    let mut buf = [0u8; MAX];
    let n = unsafe {
        ffi::EsmGetLegalTransitions(from as i32, buf.as_mut_ptr(), MAX as i32)
    };
    if n <= 0 { return Vec::new(); }
    buf[..(n as usize).min(MAX)]
        .iter()
        .filter_map(|&raw| match raw {
            0x00 => Some(EcState::None),
            0x01 => Some(EcState::Init),
            0x02 => Some(EcState::PreOp),
            0x03 => Some(EcState::Boot),
            0x04 => Some(EcState::SafeOp),
            0x08 => Some(EcState::Operational),
            _ => None,
        })
        .collect()
}

pub fn esm_default_timeout_ms(from: EcState, to: EcState) -> u32 {
    unsafe { ffi::EsmGetDefaultTimeoutMs(from as i32, to as i32) }
}

pub fn esm_is_known_al_status_code(al_status_code: u16) -> bool {
    unsafe { ffi::EsmIsKnownAlStatusCode(al_status_code) != 0 }
}

pub fn esm_classify_al_status_code(al_status_code: u16) -> AlStatusClass {
    AlStatusClass::from_raw(unsafe { ffi::EsmClassifyAlStatusCode(al_status_code) })
}

pub fn esm_master_class(master_index: u16) -> u8 {
    unsafe { ffi::EsmGetMasterClass(master_index) }
}