use crate::data::error::{DarraError, EcState, LinkState, Result};
use crate::utils::ffi::{self, TopologyNode, MasterIdentity, MasterDiagData, SlaveIdentity};
use crate::slave::core::Slave;
use crate::statics::network::NetworkInfo;
use std::ffi::{CStr, CString};
use std::os::raw::c_int;
use std::ops::Deref;
use std::ptr::NonNull;
pub struct EtherCATMaster {
index: u16,
started: bool,
owned: bool,
}
impl EtherCATMaster {
pub fn new() -> Result<Self> {
let index = unsafe { ffi::Initialize() };
if index == 0xFFFF {
return Err(DarraError::AlreadyInitialized);
}
Ok(Self { index, started: false, owned: true })
}
pub fn with_index(master_index: u16) -> Result<Self> {
let index = unsafe { ffi::InitializeSpecificMaster(master_index) };
if index == 0xFFFF {
return Err(DarraError::AlreadyInitialized);
}
Ok(Self { index, started: false, owned: true })
}
pub fn from_json(json_config: &str) -> Result<Self> {
let c_json = CString::new(json_config)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("JSON 包含空字节").into()))?;
let index = unsafe { ffi::EcInit(c_json.as_ptr()) };
if index == 0xFFFF {
return Err(DarraError::ConfigLoadFailed(-1));
}
Ok(Self { index, started: true, owned: true })
}
pub fn from_json_file(path: &str) -> Result<Self> {
let c_path = CString::new(path)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("路径包含空字节").into()))?;
let index = unsafe { ffi::EcInitFromFile(c_path.as_ptr()) };
if index == 0xFFFF {
return Err(DarraError::ConfigLoadFailed(-1));
}
Ok(Self { index, started: true, owned: true })
}
pub fn builder() -> MasterBuilder {
MasterBuilder::new()
}
pub fn index(&self) -> u16 {
self.index
}
pub fn master_number(&self) -> u16 {
self.index
}
pub fn state(&self) -> EcState {
let raw = unsafe { ffi::GetMasterState(self.index) };
if raw.is_null() { return EcState::None; }
let state_raw = unsafe { *(raw as *const u16) };
EcState::from_raw(state_raw as u8).unwrap_or(EcState::None)
}
pub fn obits(&self) -> u16 {
let ptr = unsafe { ffi::GetSlave(self.index, 0) };
if ptr.is_null() { return 0; }
unsafe { (*(ptr as *const crate::utils::ffi::EcSlaveBlittable)).output.bits }
}
pub fn obytes(&self) -> u32 {
let ptr = unsafe { ffi::GetSlave(self.index, 0) };
if ptr.is_null() { return 0; }
unsafe { (*(ptr as *const crate::utils::ffi::EcSlaveBlittable)).output.bytes }
}
pub fn ibits(&self) -> u16 {
let ptr = unsafe { ffi::GetSlave(self.index, 0) };
if ptr.is_null() { return 0; }
unsafe { (*(ptr as *const crate::utils::ffi::EcSlaveBlittable)).input.bits }
}
pub fn ibytes(&self) -> u32 {
let ptr = unsafe { ffi::GetSlave(self.index, 0) };
if ptr.is_null() { return 0; }
unsafe { (*(ptr as *const crate::utils::ffi::EcSlaveBlittable)).input.bytes }
}
pub fn has_dc(&self) -> bool {
let ptr = unsafe { ffi::GetSlave(self.index, 0) };
if ptr.is_null() { return false; }
unsafe { (*(ptr as *const crate::utils::ffi::EcSlaveBlittable)).topo.has_dc != 0 }
}
pub fn link_state(&self) -> LinkState {
self.link_status()
}
pub fn loop_cycle(&self) -> u32 {
unsafe { ffi::GetTimingMode(self.index) }
}
pub fn set_loop_cycle(&self, time_ns: u32) {
unsafe { ffi::SetMasterLoopCycleTime(self.index, time_ns) };
}
pub fn dispose(&mut self) {
if self.owned {
if self.started {
unsafe { ffi::Stop(self.index) };
self.started = false;
}
unsafe { ffi::Dispose(self.index) };
self.owned = false;
}
}
pub fn set_network(&self, adapter: &str, redundant: &str) -> Result<()> {
let c_adapter = CString::new(adapter)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("适配器名称包含空字节").into()))?;
let c_redundant = CString::new(redundant)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("冗余适配器名称包含空字节").into()))?;
let ret = unsafe { ffi::SetNetwork(self.index, c_adapter.as_ptr(), c_redundant.as_ptr()) };
if ret > 0 { Ok(()) } else { Err(DarraError::NetworkFailed(ret)) }
}
pub fn set_network_with_count(&self, adapter: &str, redundant: &str) -> Result<i32> {
let c_adapter = CString::new(adapter)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("适配器名称包含空字节").into()))?;
let c_redundant = CString::new(redundant)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("冗余适配器名称包含空字节").into()))?;
let ret = unsafe { ffi::SetNetwork(self.index, c_adapter.as_ptr(), c_redundant.as_ptr()) };
if ret > 0 { Ok(ret) } else { Err(DarraError::NetworkFailed(ret)) }
}
pub fn primary_network_info(&self) -> Option<NetworkInfo> {
None
}
pub fn redundant_network_info(&self) -> Option<NetworkInfo> {
None
}
pub fn quick_slave_count(adapter: &str) -> Result<i32> {
let c_adapter = CString::new(adapter)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("适配器名称包含空字节").into()))?;
let ret = unsafe { ffi::QuickSlaveCount(c_adapter.as_ptr()) };
if ret >= 0 { Ok(ret) } else { Err(DarraError::NetworkFailed(ret)) }
}
pub fn quick_slave_count_redundant(primary: &str, secondary: &str) -> Result<i32> {
let c_pri = CString::new(primary)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("主适配器名称包含空字节").into()))?;
let _c_sec = CString::new(secondary)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("副适配器名称包含空字节").into()))?;
let ret = unsafe { ffi::QuickSlaveCount(c_pri.as_ptr()) };
if ret >= 0 { Ok(ret) } else { Err(DarraError::NetworkFailed(ret)) }
}
pub fn scan_slave_count(adapter: &str) -> Result<i32> {
let c_adapter = CString::new(adapter)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("适配器名称包含空字节").into()))?;
let ret = unsafe { ffi::QuickSlaveCount(c_adapter.as_ptr()) };
if ret >= 0 { Ok(ret) } else { Err(DarraError::NetworkFailed(ret)) }
}
pub fn scan_slave_count_redundant(primary: &str, secondary: &str) -> Result<i32> {
let c_pri = CString::new(primary)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("主适配器名称包含空字节").into()))?;
let _c_sec = CString::new(secondary)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("副适配器名称包含空字节").into()))?;
let ret = unsafe { ffi::QuickSlaveCount(c_pri.as_ptr()) };
if ret >= 0 { Ok(ret) } else { Err(DarraError::NetworkFailed(ret)) }
}
pub fn read_slave_info(adapter: &str) -> Result<i32> {
let c_adapter = CString::new(adapter)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("适配器名称包含空字节").into()))?;
let ret = unsafe { ffi::ReadSlaveInfo(c_adapter.as_ptr()) };
if ret >= 0 { Ok(ret) } else { Err(DarraError::NetworkFailed(ret)) }
}
pub fn scanned_slave_count() -> i32 {
unsafe { ffi::GetScannedSlaveCount() }
}
pub fn ring_slave_count() -> i32 {
unsafe { ffi::GetRingSlaveCount() }
}
pub fn set_state(&mut self, state: EcState) -> Result<()> {
for attempt in 0..3 {
let ok = unsafe { ffi::SetStateSequence(self.index, state as c_int, 5000) } != 0;
if ok {
if matches!(state, EcState::SafeOp | EcState::Operational) && !self.started {
unsafe { ffi::Start(self.index) };
self.started = true;
}
return Ok(());
}
if attempt < 2 {
std::thread::sleep(std::time::Duration::from_millis(1500));
}
}
Err(DarraError::StateChangeFailed(state as u8))
}
pub fn set_state_async(&self, state: EcState) -> std::thread::JoinHandle<Result<()>> {
let mi = self.index;
std::thread::spawn(move || {
let ok = unsafe { ffi::SetStateSequence(mi, state as c_int, 5000) } != 0;
if !ok {
return Err(DarraError::StateChangeFailed(state as u8));
}
if matches!(state, EcState::SafeOp | EcState::Operational) {
unsafe { ffi::Start(mi) };
}
Ok(())
})
}
pub fn link_status(&self) -> LinkState {
LinkState::from_raw(unsafe { ffi::GetLinkStatus(self.index) })
}
pub fn start(&mut self) {
unsafe { ffi::Start(self.index) };
self.started = true;
}
pub fn stop(&mut self) {
if self.started {
unsafe { ffi::Stop(self.index) };
self.started = false;
}
}
pub fn slave(&self, slave_index: u16) -> Slave {
Slave::new(self.index, slave_index)
}
pub fn slave_count(&self) -> u16 {
let mut total: u16 = 0;
for g in 0..8u8 {
total += unsafe { ffi::GetGroupSlaveCount(self.index, g) };
}
total
}
pub fn slaves(&self) -> Vec<Slave> {
let count = self.slave_count();
(1..=count).map(|i| Slave::new(self.index, i)).collect()
}
pub fn load_config_json(&self, json: &str) -> Result<()> {
let c_json = CString::new(json)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("JSON 包含空字节").into()))?;
let ret = unsafe {
ffi::DarraCoreInvokeText(self.index, ffi::CORE_OP_23, c_json.as_ptr(), ffi::CORE_FLAG_01, 0, 0)
};
if ret >= 0 { Ok(()) } else { Err(DarraError::ConfigLoadFailed(ret)) }
}
pub fn auto_configure_sm(&self, slave_index: u16) -> Result<i32> {
let ret = if slave_index == 0 {
unsafe { ffi::DarraCoreInvoke(self.index, ffi::CORE_OP_24, 0, 0, 0) }
} else {
unsafe { ffi::DarraCoreInvoke(self.index, ffi::CORE_OP_25, slave_index as u32, 0, 0) }
};
if ret >= 0 { Ok(ret) } else { Err(DarraError::Other(format!("自动配置 SM 失败 (返回码: {})", ret))) }
}
pub fn clear_all_startup_parameters(&self) -> Result<()> {
let ret = unsafe { ffi::ClearStartupParameters(self.index, 0) };
if ret >= 0 { Ok(()) } else { Err(DarraError::Other("清除启动参数失败".into())) }
}
pub fn apply_startup_parameters_all(&self, transition: u8, timing: u8) -> Result<i32> {
let ret = unsafe {
ffi::DarraCoreInvoke(self.index, ffi::CORE_OP_26, 0, 1u32 << transition, 1u32 << timing)
};
if ret >= 0 { Ok(ret) } else { Err(DarraError::Other(format!("执行启动参数失败 (返回码: {})", ret))) }
}
pub fn set_dc_cycle_time(&self, time_ns: u32) {
unsafe { ffi::SetMasterDCCycleTime(self.index, time_ns) };
}
pub fn configure_dc_all(&self, sync0_ns: u32, sync1_ns: u32) -> Result<i32> {
let ret = unsafe { ffi::ConfigureDCAll(self.index, sync0_ns, sync1_ns) };
if ret >= 0 { Ok(ret) } else { Err(DarraError::Other("DC 配置失败".into())) }
}
pub fn update_propagation_delays(&self) -> Result<i32> {
let ret = unsafe { ffi::UpdatePropagationDelays(self.index) };
if ret >= 0 { Ok(ret) } else { Err(DarraError::Other("传播延迟更新失败".into())) }
}
pub fn auto_calculate_dc_shift(&self) -> Result<i32> {
let ret = unsafe { ffi::AutoCalculateDCShift(self.index) };
if ret >= 0 { Ok(ret) } else { Err(DarraError::Other("DC 偏移计算失败".into())) }
}
pub fn set_dc_auto_shift_enabled(&self, enable: bool) {
unsafe { ffi::SetDCAutoShiftEnabled(self.index, if enable { 1 } else { 0 }) };
}
pub fn dc_auto_shift_enabled(&self) -> bool {
(unsafe { ffi::GetDCAutoShiftEnabled(self.index) }) != 0
}
pub fn set_sync_window_threshold(&self, threshold_ns: i32) {
unsafe { ffi::SetSyncWindowThreshold(self.index, threshold_ns) };
}
pub fn sync_window_threshold(&self) -> i32 {
unsafe { ffi::GetSyncWindowThreshold(self.index) }
}
pub fn master_dc_time(&self) -> u64 {
let t = unsafe { ffi::GetMasterDCTime(self.index) };
if t < 0 { 0 } else { t as u64 }
}
pub fn reference_clock_slave_index(&self) -> i32 {
unsafe { ffi::GetReferenceClockSlaveIndex(self.index) as i32 }
}
pub fn max_sync_difference(&self) -> i32 {
unsafe { ffi::GetMaxSyncDifference(self.index) }
}
pub fn is_all_slaves_in_sync(&self) -> bool {
(unsafe { ffi::IsAllSlavesInSync(self.index) }) != 0
}
pub fn enable_continuous_measurement(&self, enable: bool, interval_sec: u32) {
unsafe { ffi::EnableContinuousMeasurement(self.index, if enable { 1 } else { 0 }, interval_sec) };
}
pub fn enable_drift_compensation(&self, enable: bool, threshold_ns: i32, gain: i32) {
unsafe { ffi::EnableDriftCompensation(self.index, if enable { 1 } else { 0 }, threshold_ns, gain) };
}
pub fn max_propagation_delay(&self) -> i32 {
unsafe { ffi::GetMaxPropagationDelay(self.index) }
}
pub fn set_cycle_time(&self, time_ns: u32) {
unsafe { ffi::SetMasterLoopCycleTime(self.index, time_ns) };
}
pub fn set_cpu_affinity(&self, cpu_core: i32) -> bool {
(unsafe { ffi::SetMasterCpuAffinity(self.index, cpu_core) }) != 0
}
pub fn set_pdo_thread_cpu_affinity(&self, cpu_core: i32) -> bool {
(unsafe { ffi::SetPDOThreadCpuAffinity(self.index, cpu_core) }) != 0
}
pub fn pdo_thread_cpu_affinity(&self) -> i32 {
unsafe { ffi::GetPDOThreadCpuAffinity(self.index) }
}
pub fn available_cpu_cores() -> i32 {
unsafe { ffi::GetAvailableCpuCores() }
}
pub fn apply_realtime_optimizations() -> bool {
(unsafe { ffi::ApplyRealtimeOptimizations() }) != 0
}
pub fn remove_realtime_optimizations() -> bool {
(unsafe { ffi::RemoveRealtimeOptimizations() }) != 0
}
#[allow(dead_code)]
pub(crate) fn realtime_optimizations_status() -> bool {
(unsafe { ffi::GetRealtimeOptimizationsStatus() }) != 0
}
pub(crate) fn timing_mode(&self) -> u32 {
unsafe { ffi::GetTimingMode(self.index) }
}
pub fn set_group_cycle_divider(&self, group: u8, divider: u8) -> bool {
(unsafe { ffi::SetGroupCycleDivider(self.index, group, divider) }) != 0
}
pub fn set_group_enabled(&self, group: u8, enabled: bool) -> bool {
(unsafe { ffi::SetGroupEnabled(self.index, group, if enabled { 1 } else { 0 }) }) != 0
}
pub fn get_group_enabled(&self, group: u8) -> bool {
(unsafe { ffi::GetGroupEnabled(self.index, group) }) != 0
}
pub fn active_group_count(&self) -> u8 {
unsafe { ffi::GetActiveGroupCount(self.index) }
}
pub fn group_expected_wkc(&self, group: u8) -> u16 {
unsafe { ffi::GetGroupExpectedWKC(self.index, group) }
}
pub fn group_slave_count(&self, group: u8) -> u16 {
unsafe { ffi::GetGroupSlaveCount(self.index, group) }
}
pub fn set_redundancy_mode(mode: i32) {
unsafe { ffi::SetRedProcessdata(mode) };
}
pub fn redundancy_mode() -> i32 {
unsafe { ffi::GetRedProcessdata() }
}
pub fn enable_redundancy(&self, enable: bool) -> Result<()> {
if unsafe { ffi::EnableRedundancy(self.index, if enable { 1 } else { 0 }) } != 0 {
Ok(())
} else {
Err(DarraError::RedundancyFailed)
}
}
pub fn redundancy_status_raw(&self) -> *const std::ffi::c_void {
unsafe { ffi::GetRedundancyStatus(self.index) }
}
pub fn force_redundancy_failover(&self) -> Result<()> {
if unsafe { ffi::ForceRedundancyFailover(self.index) } != 0 {
Ok(())
} else {
Err(DarraError::RedundancyFailed)
}
}
pub fn check_redundancy_health(&self) -> bool {
(unsafe { ffi::CheckRedundancyHealth(self.index) }) != 0
}
pub fn detailed_diagnostics_raw(&self) -> *const std::ffi::c_void {
unsafe { ffi::GetDetailedDiagnostics(self.index) }
}
pub fn reset_diagnostics(&self) {
unsafe { ffi::ResetDiagnostics(self.index) };
}
pub fn diagnostics_pointer(&self) -> *const std::ffi::c_void {
unsafe { ffi::GetDiagnosticsPointer(self.index) }
}
pub fn summary_pointer(&self) -> *const std::ffi::c_void {
unsafe { ffi::GetSummaryPointer(self.index) }
}
pub fn set_diagnostics_enabled(&self, enable: bool) {
unsafe { ffi::SetDiagnosticsEnabled(self.index, if enable { 1 } else { 0 }) };
}
pub fn diagnostics_enabled(&self) -> bool {
(unsafe { ffi::GetDiagnosticsEnabled(self.index) }) != 0
}
pub fn communication_stats_raw(&self) -> *const std::ffi::c_void {
unsafe { ffi::GetCommunicationStats(self.index) }
}
pub fn reset_communication_stats(&self) {
unsafe { ffi::ResetCommunicationStats(self.index) };
}
pub fn expected_wkc(&self) -> u16 {
unsafe { ffi::GetExpectedWKC(self.index) }
}
pub fn set_expected_wkc(&self, expected_wkc: u16) {
unsafe { ffi::SetExpectedWKC(self.index, expected_wkc) };
}
pub fn primary_wkc(&self) -> u16 {
unsafe { ffi::GetPrimaryWKC() }
}
pub fn secondary_wkc(&self) -> u16 {
unsafe { ffi::GetSecondaryWKC() }
}
pub fn slave_link_quality(&self, slave_index: u16) -> i16 {
unsafe { ffi::GetSlaveLinkQuality(self.index, slave_index) }
}
pub fn record_cycle_time(&self, cycle_time_us: u32) {
unsafe { ffi::RecordCycleTime(self.index, cycle_time_us) };
}
pub fn record_pdo_cycle_start(&self) {
unsafe { ffi::RecordPDOCycleStart(self.index) };
}
pub fn record_wkc(&self, wkc: u16) {
unsafe { ffi::RecordWKC(self.index, wkc) };
}
pub fn update_diagnostics_snapshot(&self) {
unsafe { ffi::UpdateDiagnosticsSnapshot(self.index) };
}
pub fn reset_slave_port_error_counters(&self, slave_index: u16) -> bool {
(unsafe { ffi::ResetSlavePortErrorCounters(self.index, slave_index) }) != 0
}
pub fn read_slave_port_error_counters(&self, slave_index: u16) -> Option<([u8; 4], [u8; 4], [u8; 4])> {
let mut rx_error = [0u8; 4];
let mut invalid_frame = [0u8; 4];
let mut lost_link = [0u8; 4];
let ok = unsafe {
ffi::ReadSlavePortErrorCounters(
self.index, slave_index,
rx_error.as_mut_ptr(), invalid_frame.as_mut_ptr(), lost_link.as_mut_ptr(),
)
};
if ok != 0 { Some((rx_error, invalid_frame, lost_link)) } else { None }
}
pub fn read_all_slave_port_error_counters(&self) -> i32 {
unsafe { ffi::ReadAllSlavePortErrorCounters(self.index) }
}
pub fn slave_port_error_stats(&self, slave_index: u16) -> *const std::ffi::c_void {
unsafe { ffi::GetSlavePortErrorStats(self.index, slave_index) }
}
pub fn update_diagnostics_with_esc_errors(&self) {
unsafe { ffi::UpdateDiagnosticsWithESCErrors(self.index) };
}
pub fn break_points(&self, max_results: u16) -> Vec<(u16, u8, u8)> {
let mut slaves = vec![0u16; max_results as usize];
let mut ports = vec![0u8; max_results as usize];
let mut types = vec![0u8; max_results as usize];
let count = unsafe {
ffi::GetBreakPoints(
self.index,
slaves.as_mut_ptr(), ports.as_mut_ptr(), types.as_mut_ptr(),
max_results,
)
};
let n = count.max(0) as usize;
(0..n).map(|i| (slaves[i], ports[i], types[i])).collect()
}
pub fn write_slave_output(&self, slave_index: u16, data: &[u8]) {
unsafe { ffi::WriteSlaveOutput(self.index, slave_index, data.as_ptr(), data.len() as u32) };
}
pub fn write_slave_output_byte(&self, slave_index: u16, offset: u32, value: u8) {
unsafe { ffi::WriteSlaveOutputByte(self.index, slave_index, offset, value) };
}
pub fn set_mutex_protection(&self, enable: bool) {
unsafe { ffi::SetMutexProtection(self.index, if enable { 1 } else { 0 }) };
}
pub fn mutex_protection(&self) -> bool {
(unsafe { ffi::GetMutexProtection(self.index) }) != 0
}
pub fn set_pdo_logging(enable: bool) {
unsafe { ffi::SetPDOLogging(if enable { 1 } else { 0 }) };
}
pub fn set_mailbox_logging(enable: bool) {
unsafe { ffi::SetMailboxLogging(if enable { 1 } else { 0 }) };
}
pub fn set_debug_logging(enable: bool) {
unsafe { ffi::SetDebugLogging(if enable { 1 } else { 0 }) };
}
pub fn close_debug_log() {
unsafe { ffi::CloseDebugLog() };
}
pub fn register_pdo_frame_loss_callback(callback: ffi::PDOFrameLossCallback) {
unsafe { ffi::RegisterPDOFrameLossCallback(callback) };
}
pub fn register_preop_reconfig_callback(callback: ffi::SlavePreOpReconfigCallback) {
unsafe { ffi::RegisterSlavePreOpReconfigCallback(callback) };
}
pub fn set_crash_callback(callback: ffi::CrashNotifyCallback) {
unsafe { ffi::SetCrashCallback(callback) };
}
pub fn set_dc_sync_lost_callback(callback: ffi::DCSyncLostCallback) {
unsafe { ffi::SetDCSyncLostCallback(callback) };
}
pub fn dll_version() -> Option<(u16, u16, u16, u16)> {
let ptr = unsafe { ffi::GetDllVersionInfo() };
if ptr.is_null() { return None; }
let info = unsafe { std::ptr::read_unaligned(ptr) };
Some((info.major, info.minor, info.patch, info.build))
}
pub fn lock_iomap(&self) {
unsafe { ffi::LockIOmap(self.index) };
}
pub fn unlock_iomap(&self) {
unsafe { ffi::UnlockIOmap(self.index) };
}
pub fn iomap_guard(&self) -> IomapGuard<'_> {
self.lock_iomap();
IomapGuard { master: self }
}
#[allow(dead_code)]
pub(crate) fn is_wdk_available(&self) -> bool {
(unsafe { ffi::IsWdkAvailable(self.index) }) != 0
}
#[allow(dead_code)]
pub(crate) fn set_wdk_mode(&self, enable: bool) -> Result<()> {
if unsafe { ffi::SetWdkMode(self.index, if enable { 1 } else { 0 }) } != 0 {
Ok(())
} else {
Err(DarraError::WdkFailed("设置 WDK 模式失败".into()))
}
}
#[allow(dead_code)]
pub(crate) fn wdk_mode(&self) -> bool {
(unsafe { ffi::GetWdkMode(self.index) }) != 0
}
#[allow(dead_code)]
pub(crate) fn start_wdk_rt(&self, cycle_us: u32, cpu_index: u32) -> Result<()> {
if unsafe { ffi::StartWdkRT(self.index, cycle_us, cpu_index) } != 0 {
Ok(())
} else {
Err(DarraError::WdkFailed("启动 WDK RT 失败".into()))
}
}
#[allow(dead_code)]
pub(crate) fn stop_wdk_rt(&self) -> Result<()> {
if unsafe { ffi::StopWdkRT(self.index) } != 0 {
Ok(())
} else {
Err(DarraError::WdkFailed("停止 WDK RT 失败".into()))
}
}
pub fn set_udp_mode(&self, enable: bool) -> Result<()> {
if unsafe { ffi::SetUdpMode(self.index, if enable { 1 } else { 0 }) } != 0 {
Ok(())
} else {
Err(DarraError::UdpFailed)
}
}
pub fn udp_mode(&self) -> bool {
(unsafe { ffi::GetUdpMode(self.index) }) != 0
}
pub fn is_udp_available(&self) -> bool {
(unsafe { ffi::IsUdpAvailable(self.index) }) != 0
}
pub fn topology(&self) -> Vec<TopologyNode> {
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,
}; 256];
let count = unsafe { ffi::GetTopology(self.index, buf.as_mut_ptr(), 256) };
if count > 0 {
buf.truncate(count as usize);
buf
} else {
Vec::new()
}
}
pub fn topology_build(&self) -> Vec<TopologyNode> {
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,
}; 256];
let count = unsafe { ffi::TopologyBuild(self.index, buf.as_mut_ptr(), 256) };
if count > 0 {
buf.truncate(count as usize);
buf
} else {
Vec::new()
}
}
pub fn topology_get_children(&self, parent_index: u16) -> Vec<u16> {
let mut buf = vec![0u16; 64];
let count = unsafe { ffi::TopologyGetChildren(self.index, parent_index, buf.as_mut_ptr(), 64) };
if count > 0 { buf.truncate(count as usize); buf }
else { Vec::new() }
}
pub fn topology_get_roots(&self) -> Vec<u16> {
let mut buf = vec![0u16; 64];
let count = unsafe { ffi::TopologyGetRoots(self.index, buf.as_mut_ptr(), 64) };
if count > 0 { buf.truncate(count as usize); buf }
else { Vec::new() }
}
pub fn verify_license() -> Result<bool> {
let ret = unsafe { ffi::VerifyLicense() };
Ok(ret != 0)
}
pub fn is_license_valid() -> bool {
unsafe { ffi::IsLicenseValid() != 0 }
}
pub fn invalidate_license() {
unsafe { ffi::InvalidateLicense() };
}
pub fn serial_number() -> String {
let ptr = unsafe { ffi::GetSerialNumber() };
if ptr.is_null() { return String::new(); }
unsafe { CStr::from_ptr(ptr) }.to_string_lossy().into_owned()
}
pub fn device_name() -> String {
let ptr = unsafe { ffi::GetDeviceName() };
if ptr.is_null() { return String::new(); }
unsafe { CStr::from_ptr(ptr) }.to_string_lossy().into_owned()
}
pub fn user_email() -> String {
let ptr = unsafe { ffi::GetUserEmail() };
if ptr.is_null() { return String::new(); }
unsafe { CStr::from_ptr(ptr) }.to_string_lossy().into_owned()
}
pub fn windows_product_key() -> String {
let ptr = unsafe { ffi::GetWindowsProductKey() };
if ptr.is_null() { return String::new(); }
unsafe { CStr::from_ptr(ptr) }.to_string_lossy().into_owned()
}
pub fn driver_list() -> String {
let ptr = unsafe { ffi::GetDriverList() };
if ptr.is_null() { return String::new(); }
unsafe { CStr::from_ptr(ptr) }.to_string_lossy().into_owned()
}
pub fn max_master_instances() -> i32 {
unsafe { ffi::GetMaxMasterInstances() }
}
#[allow(dead_code)]
pub(crate) fn dump_slave_struct_offsets() {
unsafe { ffi::DumpSlaveStructOffsets() };
}
pub fn set_state_cache_update_interval(&self, interval_ms: u32) -> Result<()> {
if ffi::dynamic_ffi::ffi_gap().set_state_cache_update_interval.is_none() {
return Err(DarraError::DiagnosticsFailed);
}
unsafe { ffi::SetStateCacheUpdateInterval(self.index, interval_ms) };
Ok(())
}
pub fn state_cache_update_interval(&self) -> Option<u32> {
if ffi::dynamic_ffi::ffi_gap().get_state_cache_update_interval.is_none() {
return None;
}
Some(unsafe { ffi::GetStateCacheUpdateInterval(self.index) })
}
pub fn set_all_slave_watchdog(&self, timeout_ms: u32) {
unsafe { ffi::SetAllSlaveWatchdog(self.index, timeout_ms) };
}
pub fn set_all_slave_pdi_watchdog(&self, timeout_ms: u32) {
unsafe { ffi::SetAllSlavePdiWatchdog(self.index, timeout_ms) };
}
pub fn slave_needs_startup_reconfig(&self, slave_index: u16) -> bool {
unsafe { ffi::GetSlaveNeedsStartupReconfig(self.index, slave_index) != 0 }
}
pub fn clear_slave_startup_reconfig(&self, slave_index: u16) {
unsafe { ffi::ClearSlaveNeedsStartupReconfig(self.index, slave_index) };
}
pub fn pdo_frame_loss_stats(&self, group: u8) -> (u32, u32, u32) {
let mut total: u32 = 0;
let mut consecutive: u32 = 0;
let mut max_consecutive: u32 = 0;
unsafe {
ffi::GetPDOFrameLossStats(
self.index, group,
&mut total, &mut consecutive, &mut max_consecutive,
);
}
(total, consecutive, max_consecutive)
}
pub fn reset_pdo_frame_loss_stats(&self, group: u8) {
unsafe { ffi::ResetPDOFrameLossStats(self.index, group) };
}
pub fn validate_timer_accuracy(expected_usec: u32, tolerance_usec: u32) -> bool {
unsafe { ffi::DarraValidateTimerAccuracy(expected_usec, tolerance_usec) == 0 }
}
pub fn set_pdo_monitoring(enable: bool) -> Result<()> {
if ffi::dynamic_ffi::ffi_gap().enable_pdo_monitoring.is_none() {
return Err(DarraError::PdoFailed("EnablePDOMonitoring 原生符号不可用".into()));
}
unsafe { ffi::EnablePDOMonitoring(if enable { 1 } else { 0 }) };
Ok(())
}
pub fn is_pdo_monitoring_enabled() -> Option<bool> {
if ffi::dynamic_ffi::ffi_gap().is_pdo_monitoring_enabled.is_none() {
return None;
}
Some(unsafe { ffi::IsPDOMonitoringEnabled() != 0 })
}
pub fn pdo_stats_raw(&self, slave_index: u16) -> Option<NonNull<std::ffi::c_void>> {
if ffi::dynamic_ffi::ffi_gap().get_pdo_stats.is_none() {
return None;
}
NonNull::new(unsafe { ffi::GetPDOStats(self.index, slave_index) as *mut std::ffi::c_void })
}
pub fn reset_pdo_stats(&self, slave_index: u16) -> Result<()> {
if ffi::dynamic_ffi::ffi_gap().reset_pdo_stats.is_none() {
return Err(DarraError::PdoFailed("ResetPDOStats 原生符号不可用".into()));
}
unsafe { ffi::ResetPDOStats(self.index, slave_index) };
Ok(())
}
pub fn master_identity(&self) -> Result<MasterIdentity> {
let mut identity = MasterIdentity {
vendor_id: 0, product_code: 0, revision_no: 0, serial_no: 0,
device_name: [0; 64], hw_version: [0; 32], sw_version: [0; 32],
};
if unsafe { ffi::GetMasterIdentity(self.index, &mut identity) } != 0 {
Ok(identity)
} else {
Err(DarraError::DiagnosticsFailed)
}
}
pub fn master_diag_data(&self) -> Result<MasterDiagData> {
let mut diag = MasterDiagData {
cyclic_lost_frames: 0, acyclic_lost_frames: 0,
cyclic_frames_per_sec: 0, acyclic_frames_per_sec: 0,
master_state: 0,
};
if unsafe { ffi::GetMasterDiagData(self.index, &mut diag) } != 0 {
Ok(diag)
} else {
Err(DarraError::DiagnosticsFailed)
}
}
pub fn ring_mode(&self) -> i32 {
unsafe { ffi::GetRingMode(self.index) }
}
pub fn secondary_link_status(&self) -> bool {
unsafe { ffi::GetSecondaryLinkStatus(self.index) != 0 }
}
pub fn validate_config(&self) -> Result<i32> {
let ret = unsafe { ffi::ec_validate_config(self.index) };
if ret >= 0 { Ok(ret) } else { Err(DarraError::Other(format!("配置验证失败 (返回码: {})", ret))) }
}
pub fn verify_all_slave_identities(
&self,
expected: &[SlaveIdentity],
check_revision: bool,
check_serial: bool,
) -> Result<u64> {
let mut mismatch_mask: u64 = 0;
let ret = unsafe {
ffi::VerifyAllSlaveIdentities(
self.index,
expected.as_ptr(),
expected.len() as u32,
if check_revision { 1 } else { 0 },
if check_serial { 1 } else { 0 },
&mut mismatch_mask,
)
};
if ret >= 0 {
Ok(mismatch_mask)
} else {
Err(DarraError::Other(format!("从站身份验证失败 (返回码: {})", ret)))
}
}
pub fn export_perf_csv(&self, filepath: &str) -> Result<()> {
let c_path = CString::new(filepath)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("路径包含空字节").into()))?;
let ret = unsafe { ffi::ec_perf_export_csv(self.index, c_path.as_ptr()) };
if ret >= 0 { Ok(()) } else { Err(DarraError::Other(format!("CSV 导出失败 (返回码: {})", ret))) }
}
pub fn aoe_start_notification_listener(&self) -> Result<()> {
if unsafe { ffi::AOEStartNotificationListener(self.index) } != 0 {
Ok(())
} else {
Err(DarraError::AoeFailed)
}
}
pub fn aoe_stop_notification_listener() -> Result<()> {
if unsafe { ffi::AOEStopNotificationListener() } != 0 {
Ok(())
} else {
Err(DarraError::AoeFailed)
}
}
pub fn aoe_is_notification_listening() -> bool {
unsafe { ffi::AOEIsNotificationListening() != 0 }
}
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 emergency_cleanup() {
unsafe { ffi::EmergencyCloseNics() };
}
pub fn diagnostics_info(&self) -> crate::master::diagnostics::MasterDiagnosticsInfo<'_> {
crate::master::diagnostics::MasterDiagnosticsInfo::new(self)
}
pub fn events(&self) -> crate::master::events::MasterEvents {
crate::master::events::MasterEvents::new(self.index)
}
pub fn acknowledge_slave_replacement(&self, slave_num: u16) -> Result<bool> {
if slave_num == 0 {
return Err(DarraError::InvalidParameter("slave_num 不能为 0".into()));
}
if ffi::dynamic_ffi::ffi_gap().acknowledge_slave_replacement.is_none() {
return Err(DarraError::Other("AcknowledgeSlaveReplacement 原生符号不可用".into()));
}
Ok(unsafe { ffi::AcknowledgeSlaveReplacement(self.index, slave_num) != 0 })
}
pub fn slave_topology(&self) -> crate::slave::topology::SlaveTopology {
crate::slave::topology::SlaveTopology::build(self.index)
}
pub fn rebuild_topology(&self) -> crate::slave::topology::SlaveTopology {
crate::slave::topology::SlaveTopology::build(self.index)
}
pub fn active_instance_count() -> i32 {
unsafe { ffi::GetMaxMasterInstances() }
}
pub fn max_instance_count() -> i32 {
unsafe { ffi::GetMaxMasterInstances() }
}
pub fn reset_all_sync_window_stats(&self) {
unsafe { ffi::ResetSlaveSyncWindowStats(self.index, 0) };
}
pub fn config(&self) -> crate::master::config::MasterConfig<'_> {
crate::master::config::MasterConfig::new(self)
}
pub fn master_od(&self) -> crate::master::master_od::MasterObjectDictionary<'_> {
crate::master::master_od::MasterObjectDictionary::new(self)
}
pub fn mailbox_gateway(&self) -> crate::master::mailbox_gateway::MailboxGatewayService {
crate::master::mailbox_gateway::MailboxGatewayService::new(self.index)
}
pub fn execute_with_state_management<F>(
&self,
slave_index: u16,
target_state: EcState,
operation: F,
timeout_ms: i32,
) -> Result<()>
where
F: FnOnce(&Slave) -> Result<()>,
{
let slave = self.slave(slave_index);
let current_state = slave.state().unwrap_or(EcState::None);
let target_raw = match target_state {
EcState::Init => 0x01,
EcState::PreOp => 0x02,
EcState::Boot => 0x03,
EcState::SafeOp => 0x04,
EcState::Operational => 0x08,
_ => return Err(DarraError::InvalidParameter(obfstr::obfstr!("无效的目标状态").into())),
};
if current_state != target_state {
let ret = unsafe {
ffi::SetSlaveStateWithTimeout(self.index, slave_index, target_raw as c_int, timeout_ms as u32)
};
if ret == 0 {
return Err(DarraError::StateChangeFailed(target_raw));
}
}
let result = operation(&slave);
if current_state != target_state {
let current_raw = match current_state {
EcState::Init => 0x01,
EcState::PreOp => 0x02,
EcState::Boot => 0x03,
EcState::SafeOp => 0x04,
EcState::Operational => 0x08,
_ => 0x01,
};
unsafe {
ffi::SetSlaveStateWithTimeout(self.index, slave_index, current_raw as c_int, timeout_ms as u32);
}
}
result
}
pub fn dll_checksum() -> Option<String> {
None
}
}
impl Drop for EtherCATMaster {
fn drop(&mut self) {
if self.owned {
if self.started {
unsafe { ffi::Stop(self.index) };
}
unsafe { ffi::Dispose(self.index) };
}
}
}
impl std::fmt::Display for EtherCATMaster {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "EtherCATMaster[{}] {} slaves", self.index, self.slave_count())
}
}
pub struct SlaveIterator {
master_index: u16,
current: u16,
total: u16,
}
impl Iterator for SlaveIterator {
type Item = Slave;
fn next(&mut self) -> Option<Self::Item> {
if self.current > self.total {
return None;
}
let slave = Slave::new(self.master_index, self.current);
self.current += 1;
Some(slave)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = if self.current > self.total {
0
} else {
(self.total - self.current + 1) as usize
};
(remaining, Some(remaining))
}
}
impl EtherCATMaster {
pub fn iter(&self) -> SlaveIterator {
SlaveIterator {
master_index: self.index,
current: 1,
total: self.slave_count(),
}
}
}
impl<'a> IntoIterator for &'a EtherCATMaster {
type Item = Slave;
type IntoIter = SlaveIterator;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct IomapGuard<'a> {
master: &'a EtherCATMaster,
}
impl<'a> Drop for IomapGuard<'a> {
fn drop(&mut self) {
self.master.unlock_iomap();
}
}
pub struct SlaveGroupAccessor<'a> {
master: &'a EtherCATMaster,
}
impl<'a> SlaveGroupAccessor<'a> {
pub fn new(master: &'a EtherCATMaster) -> Self {
Self { master }
}
pub fn count(&self) -> usize {
self.master.active_group_count() as usize
}
pub fn slaves(&self, group: u8) -> Vec<Slave> {
let group_count = unsafe { ffi::GetGroupSlaveCount(self.master.index, group) };
if group_count == 0 {
return Vec::new();
}
let total = self.master.slave_count();
let mut result = Vec::new();
for i in 1..=total {
let slave = Slave::new(self.master.index, i);
if slave.group() == group {
result.push(slave);
}
}
result
}
pub fn divider(&self, group: u8) -> u8 {
unsafe { ffi::GetGroupCycleDivider(self.master.index, group) }
}
pub fn set_divider(&self, group: u8, div: u8) {
if div > 0 {
unsafe { ffi::SetGroupCycleDivider(self.master.index, group, div) };
}
}
pub fn group_slave_count(&self, group: u8) -> u16 {
unsafe { ffi::GetGroupSlaveCount(self.master.index, group) }
}
pub fn group_expected_wkc(&self, group: u8) -> u16 {
unsafe { ffi::GetGroupExpectedWKC(self.master.index, group) }
}
}
pub struct SlaveGroupIter<'a> {
accessor: &'a SlaveGroupAccessor<'a>,
current_group: u8,
}
impl<'a> Iterator for SlaveGroupIter<'a> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
while self.current_group < 8 {
let group = self.current_group;
self.current_group += 1;
if unsafe { ffi::GetGroupSlaveCount(self.accessor.master.index, group) } > 0 {
return Some(group);
}
}
None
}
}
impl<'a> IntoIterator for &'a SlaveGroupAccessor<'a> {
type Item = u8;
type IntoIter = SlaveGroupIter<'a>;
fn into_iter(self) -> Self::IntoIter {
SlaveGroupIter {
accessor: self,
current_group: 0,
}
}
}
impl EtherCATMaster {
pub fn group_accessor(&self) -> SlaveGroupAccessor<'_> {
SlaveGroupAccessor::new(self)
}
}
pub struct BuildResult {
pub master: EtherCATMaster,
pub slave_count: usize,
pub success: bool,
}
impl Deref for BuildResult {
type Target = EtherCATMaster;
fn deref(&self) -> &Self::Target {
&self.master
}
}
impl std::fmt::Display for BuildResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "BuildResult(success={}, slaves={})", self.success, self.slave_count)
}
}
impl std::fmt::Debug for BuildResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BuildResult")
.field("success", &self.success)
.field("slave_count", &self.slave_count)
.field("master_index", &self.master.index)
.finish()
}
}
#[derive(Debug, Clone)]
pub struct ValidationResult {
pub is_valid: bool,
pub errors: Vec<String>,
}
pub struct MasterBuilder {
pending_network: Option<String>,
pending_redundant: Option<String>,
pending_eni_path: Option<String>,
pending_esi_paths: Vec<String>,
pending_auto_startup: bool,
configuration_set: bool,
}
impl MasterBuilder {
pub fn new() -> Self {
Self {
pending_network: None,
pending_redundant: None,
pending_eni_path: None,
pending_esi_paths: Vec::new(),
pending_auto_startup: false,
configuration_set: false,
}
}
pub fn set_eni(mut self, path: &str) -> Self {
self.pending_eni_path = Some(path.to_string());
self.configuration_set = true;
self
}
pub fn set_esi_file(mut self, path: &str) -> Self {
if !path.is_empty() {
self.pending_esi_paths.push(path.to_string());
}
self
}
pub fn set_esi_files(mut self, path: &str) -> Self {
if !path.is_empty() {
self.pending_esi_paths.push(path.to_string());
}
self
}
pub fn set_network(mut self, primary: &str, redundant: Option<&str>) -> Self {
self.pending_network = Some(primary.to_string());
self.pending_redundant = redundant.map(|s| s.to_string());
self.configuration_set = true;
self
}
pub fn enable_auto_startup(mut self) -> Self {
self.pending_auto_startup = true;
self
}
pub fn validate(&self) -> ValidationResult {
let mut errors = Vec::new();
if !self.configuration_set {
errors.push("未设置网口或 ENI 配置, 主站无法初始化".into());
}
if self.pending_eni_path.is_none() && self.pending_network.is_none() {
errors.push("未设置 ENI 配置文件路径或网口名称".into());
}
if let Some(ref path) = self.pending_eni_path {
if path.trim().is_empty() {
errors.push("ENI 配置文件路径不能为空".into());
}
}
if let Some(ref primary) = self.pending_network {
if primary.trim().is_empty() {
errors.push("主网口名称不能为空".into());
}
}
ValidationResult {
is_valid: errors.is_empty(),
errors,
}
}
pub fn build(self) -> Result<BuildResult> {
if !self.configuration_set {
return Err(DarraError::Other(
"未设置网口或 ENI 配置, 主站无法初始化".into()
));
}
let index = unsafe { ffi::Initialize() };
if index == 0xFFFF {
return Err(DarraError::AlreadyInitialized);
}
let mut master = EtherCATMaster {
index,
started: false,
owned: true,
};
for _esi_path in &self.pending_esi_paths {
}
if let Some(ref eni_path) = self.pending_eni_path {
let config_json = format!(
"{{\"eni_path\": \"{}\", \"auto_startup\": {}}}",
eni_path.replace('\\', "\\\\").replace('"', "\\\""),
self.pending_auto_startup
);
let c_json = CString::new(config_json)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("配置 JSON 包含空字节").into()))?;
let ret = unsafe {
ffi::DarraCoreInvokeText(index, ffi::CORE_OP_23, c_json.as_ptr(), ffi::CORE_FLAG_01, 0, 0)
};
if ret < 0 {
master.owned = false;
unsafe { ffi::Dispose(index) };
return Err(DarraError::ConfigLoadFailed(ret));
}
}
if let Some(ref primary) = self.pending_network {
let c_adapter = CString::new(primary.as_str())
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("主网口名称包含空字节").into()))?;
let redundant_str = self.pending_redundant.as_deref().unwrap_or("");
let c_redundant = CString::new(redundant_str)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("冗余网口名称包含空字节").into()))?;
let ret = unsafe { ffi::SetNetwork(index, c_adapter.as_ptr(), c_redundant.as_ptr()) };
if ret <= 0 {
master.owned = false;
unsafe { ffi::Dispose(index) };
return Err(DarraError::NetworkFailed(ret));
}
}
let slave_count = master.slave_count() as usize;
if self.pending_auto_startup && slave_count > 0 {
let ret = unsafe { ffi::DarraCoreInvoke(index, ffi::CORE_OP_24, 0, 0, 0) };
if ret < 0 {
master.owned = false;
unsafe { ffi::Dispose(index) };
return Err(DarraError::Other(format!("AutoStartup native protocol action failed: {}", ret)));
}
}
unsafe { ffi::ApplyRealtimeOptimizations() };
Ok(BuildResult {
master,
slave_count,
success: slave_count > 0,
})
}
}
impl Default for MasterBuilder {
fn default() -> Self {
Self::new()
}
}
impl EtherCATMaster {
pub fn get_multi_slave_sdo_list(&self, slave_indices: &[u16]) -> Vec<Option<crate::slave::coe::OdList>> {
use std::os::raw::c_int;
let count = slave_indices.len() as c_int;
if count == 0 {
return Vec::new();
}
let mut results: Vec<*const std::ffi::c_void> = vec![std::ptr::null(); count as usize];
let ret = unsafe {
ffi::GetMultiSlaveSDOList(
self.index,
slave_indices.as_ptr(),
count,
results.as_mut_ptr(),
)
};
let mut output = Vec::with_capacity(count as usize);
if ret <= 0 {
for _ in 0..count {
output.push(None);
}
return output;
}
for i in 0..count as usize {
if results[i].is_null() {
output.push(None);
} else {
match crate::slave::coe::OdList::from_raw_ptr(
self.index, slave_indices[i], results[i],
) {
Ok(od) => output.push(Some(od)),
Err(_) => output.push(None),
}
}
}
unsafe { ffi::FreeMultiSlaveSDOList(results.as_mut_ptr(), count) };
output
}
pub fn deconstruct(&self) -> (bool, u16, u16) {
(self.owned, self.slave_count(), self.index)
}
pub fn pdo_lost_frames(&self, group: u8) -> u32 {
let mut total: u32 = 0;
let mut consecutive: u32 = 0;
let mut max_consecutive: u32 = 0;
unsafe {
ffi::GetPDOFrameLossStats(self.index, group,
&mut total, &mut consecutive, &mut max_consecutive);
}
total
}
pub fn pdo_consecutive_lost(&self, group: u8) -> u32 {
let mut total: u32 = 0;
let mut consecutive: u32 = 0;
let mut max_consecutive: u32 = 0;
unsafe {
ffi::GetPDOFrameLossStats(self.index, group,
&mut total, &mut consecutive, &mut max_consecutive);
}
consecutive
}
pub fn pdo_max_consecutive_lost(&self, group: u8) -> u32 {
let mut total: u32 = 0;
let mut consecutive: u32 = 0;
let mut max_consecutive: u32 = 0;
unsafe {
ffi::GetPDOFrameLossStats(self.index, group,
&mut total, &mut consecutive, &mut max_consecutive);
}
max_consecutive
}
pub fn esi_load_directory(&self, dir: &str) -> Result<i32> {
let c_dir = CString::new(dir)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("ESI dir 含空字节").into()))?;
let n = unsafe { ffi::EcEsi_LoadDirectory(c_dir.as_ptr()) };
Ok(n)
}
pub fn esi_load_file(&self, path: &str) -> Result<i32> {
let c_path = CString::new(path)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("ESI 路径含空字节").into()))?;
let n = unsafe { ffi::EcEsi_LoadFile(c_path.as_ptr()) };
Ok(n)
}
pub fn esi_clear(&self) {
unsafe { ffi::EcEsi_Clear() }
}
pub fn esi_loaded_count(&self) -> i32 {
unsafe { ffi::EcEsi_GetLoadedCount() }
}
pub fn esi_auto_match_all(&self) -> i32 {
unsafe { ffi::EcEsi_AutoMatchAll(self.index) }
}
pub fn esi_bind_to_slave(&self, slave_index: u16, path: &str) -> Result<i32> {
let c_path = CString::new(path)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("ESI 路径含空字节").into()))?;
let n = unsafe { ffi::EcEsi_BindToSlave(self.index, slave_index, c_path.as_ptr()) };
Ok(n)
}
pub fn esi_register_startup_parameters(&self, slave_index: u16) -> i32 {
unsafe { ffi::EcEsi_RegisterStartupParameters(self.index, slave_index) }
}
pub fn esi_apply_all_slaves(&self) -> i32 {
unsafe { ffi::EcEsi_ApplyAllSlaves(self.index) }
}
pub fn set_slave_esi_file(&self, slave_index: u16, name: &str) -> Result<i32> {
let c_name = CString::new(name)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("ESI 文件名含空字节").into()))?;
let n = unsafe { ffi::SetSlaveEsiFile(self.index, slave_index, c_name.as_ptr()) };
Ok(n)
}
}