use crate::data::error::{CiA402State, DarraError, EcState, SyncWindowStatus, Result};
use crate::data::types::EcALState;
use crate::utils::ffi::{self, EcSlaveBlittable, EmcyRecord, SlaveIdentity, EsmTimeouts, FoEOptions};
use std::ffi::CString;
use std::os::raw::c_int;
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct SlaveDetailedInfo {
pub configadr: u16,
pub eep_man: u32,
pub eep_id: u32,
pub eep_rev: u32,
pub state: u8,
pub ptype: u8,
pub topology: u8,
pub activeports: u8,
pub parent: u16,
pub hasdc: u8,
pub group: u8,
pub ibytes: u16,
pub obytes: u16,
pub mbx_proto: u16,
pub pdelay: i32,
pub dc_cycle: i32,
pub dc_shift: i32,
pub al_status_code: u16,
}
#[derive(Debug, Clone, Copy)]
pub struct Slave {
master_index: u16,
slave_index: u16,
}
impl Slave {
pub(crate) fn new(master_index: u16, slave_index: u16) -> Self {
Self { master_index, slave_index }
}
fn slave_ptr(&self) -> Option<*const EcSlaveBlittable> {
let ptr = unsafe { ffi::GetSlave(self.master_index, self.slave_index) };
if ptr.is_null() { None } else { Some(ptr as *const EcSlaveBlittable) }
}
fn read_slave(&self) -> Option<EcSlaveBlittable> {
self.slave_ptr().map(|ptr| unsafe { std::ptr::read_unaligned(ptr) })
}
pub fn detailed_info(&self) -> Option<SlaveDetailedInfo> {
let ptr = unsafe { ffi::GetSlaveDetailedInfo(self.master_index, self.slave_index) };
if ptr.is_null() {
None
} else {
Some(unsafe { std::ptr::read(ptr as *const SlaveDetailedInfo) })
}
}
pub fn index(&self) -> u16 {
self.slave_index
}
pub fn master_index(&self) -> u16 {
self.master_index
}
pub fn name(&self) -> String {
self.read_slave().map(|s| s.group_name_str()).unwrap_or_default()
}
pub fn drive_name(&self) -> String {
self.read_slave().map(|s| s.device_name_str()).unwrap_or_default()
}
pub fn vendor_id(&self) -> u32 {
self.read_slave().map(|s| s.metadata.identity.vendor_id).unwrap_or(0)
}
pub fn product_id(&self) -> u32 {
self.read_slave().map(|s| s.metadata.identity.product_id).unwrap_or(0)
}
pub fn rev_id(&self) -> u32 {
self.read_slave().map(|s| s.metadata.identity.revision).unwrap_or(0)
}
pub fn serial_number(&self) -> u32 {
self.read_slave().map(|s| s.metadata.identity.serial).unwrap_or(0)
}
pub fn identity(&self) -> Result<SlaveIdentity> {
let mut id = SlaveIdentity { vendor_id: 0, product_code: 0, revision_no: 0, serial_no: 0 };
if unsafe { ffi::GetSlaveIdentity(self.master_index, self.slave_index, &mut id) } != 0 {
Ok(id)
} else {
Err(DarraError::Other(obfstr::obfstr!("获取从站身份失败").into()))
}
}
pub fn alias_address(&self) -> u16 {
self.read_slave().map(|s| s.alias_addr).unwrap_or(0)
}
pub fn config_addr(&self) -> u16 {
self.read_slave().map(|s| s.config_addr).unwrap_or(0)
}
pub fn sii_index(&self) -> u16 {
self.read_slave().map(|s| s.eeprom_config.sii_index).unwrap_or(0)
}
pub fn state(&self) -> Option<EcState> {
let raw = unsafe { ffi::GetSlaveState(self.master_index, self.slave_index) };
EcState::from_raw(raw)
}
pub fn state_raw(&self) -> u8 {
unsafe { ffi::GetSlaveState(self.master_index, self.slave_index) }
}
pub fn error_code(&self) -> EcALState {
let raw = unsafe { ffi::GetSlaveALStatusCode(self.master_index, self.slave_index) };
EcALState::from_value(raw as i32)
}
pub fn error_code_raw(&self) -> u16 {
unsafe { ffi::GetSlaveALStatusCode(self.master_index, self.slave_index) }
}
pub fn is_lost(&self) -> bool {
self.read_slave().map(|s| s.runtime.is_lost != 0).unwrap_or(false)
}
pub fn block_lrw(&self) -> bool {
self.read_slave().map(|s| s.runtime.block_lrw != 0).unwrap_or(false)
}
pub fn dtype(&self) -> u16 {
self.read_slave().map(|s| s.metadata.identity.dtype).unwrap_or(0)
}
pub fn physical_type(&self) -> u8 {
self.read_slave().map(|s| s.topo.phy_type).unwrap_or(0)
}
pub fn ebus_current(&self) -> i16 {
self.read_slave().map(|s| s.runtime.ebus_current).unwrap_or(0)
}
pub fn input_bits(&self) -> u16 {
self.read_slave().map(|s| s.input.bits).unwrap_or(0)
}
pub fn input_bytes(&self) -> u32 {
self.read_slave().map(|s| s.input.bytes).unwrap_or(0)
}
pub fn output_bits(&self) -> u16 {
self.read_slave().map(|s| s.output.bits).unwrap_or(0)
}
pub fn output_bytes(&self) -> u32 {
self.read_slave().map(|s| s.output.bytes).unwrap_or(0)
}
pub fn input_offset(&self) -> u32 {
self.read_slave().map(|s| s.input.offset).unwrap_or(0)
}
pub fn output_offset(&self) -> u32 {
self.read_slave().map(|s| s.output.offset).unwrap_or(0)
}
pub fn input_startbit(&self) -> u8 {
self.read_slave().map(|s| s.input.startbit).unwrap_or(0)
}
pub fn output_startbit(&self) -> u8 {
self.read_slave().map(|s| s.output.startbit).unwrap_or(0)
}
pub fn io(&self) -> Result<(i32, *mut u8, i32, *mut u8)> {
let mut out_size: c_int = 0;
let mut out_ptr: *mut u8 = std::ptr::null_mut();
let mut in_size: c_int = 0;
let mut in_ptr: *mut u8 = std::ptr::null_mut();
let ret = unsafe {
ffi::GetIO(self.master_index, self.slave_index,
&mut out_size, &mut out_ptr, &mut in_size, &mut in_ptr)
};
if ret != 0 { Ok((out_size, out_ptr, in_size, in_ptr)) }
else { Err(DarraError::NullPointer) }
}
pub fn mbx_length(&self) -> u16 {
self.read_slave().map(|s| s.mbx.length).unwrap_or(0)
}
pub fn mbx_protocol(&self) -> u16 {
self.read_slave().map(|s| s.mbx.supported_proto).unwrap_or(0)
}
pub fn mbx_read_length(&self) -> u16 {
self.read_slave().map(|s| s.mbx.read_length).unwrap_or(0)
}
pub fn mbx_write_offset(&self) -> u16 {
self.read_slave().map(|s| s.mbx.write_offset).unwrap_or(0)
}
pub fn mbx_read_offset(&self) -> u16 {
self.read_slave().map(|s| s.mbx.read_offset).unwrap_or(0)
}
pub fn mbx_count(&self) -> u8 {
self.read_slave().map(|s| s.mbx.cnt).unwrap_or(0)
}
pub fn has_dc(&self) -> bool {
self.read_slave().map(|s| s.topo.has_dc != 0).unwrap_or(false)
}
pub fn dc_active(&self) -> u16 {
self.read_slave().map(|s| s.dc.active).unwrap_or(0)
}
pub fn dc_cycle0(&self) -> i32 {
self.read_slave().map(|s| s.dc.cycle0).unwrap_or(0)
}
pub fn dc_cycle1(&self) -> i32 {
self.read_slave().map(|s| s.dc.cycle1).unwrap_or(0)
}
pub fn dc_shift(&self) -> i32 {
self.read_slave().map(|s| s.dc.shift).unwrap_or(0)
}
pub fn dc_next(&self) -> u16 {
self.read_slave().map(|s| s.dc.next).unwrap_or(0)
}
pub fn dc_previous(&self) -> u16 {
self.read_slave().map(|s| s.dc.prev).unwrap_or(0)
}
pub fn dc_parent_port(&self) -> u8 {
self.read_slave().map(|s| s.topo.parent_port).unwrap_or(0)
}
pub fn dc_receive_time_a(&self) -> i32 {
self.read_slave().map(|s| s.dc.recvtime[0]).unwrap_or(0)
}
pub fn dc_receive_time_b(&self) -> i32 {
self.read_slave().map(|s| s.dc.recvtime[1]).unwrap_or(0)
}
pub fn dc_receive_time_c(&self) -> i32 {
self.read_slave().map(|s| s.dc.recvtime[2]).unwrap_or(0)
}
pub fn dc_receive_time_d(&self) -> i32 {
self.read_slave().map(|s| s.dc.recvtime[3]).unwrap_or(0)
}
pub fn propagation_delay(&self) -> i32 {
unsafe { ffi::GetSlavePropagationDelay(self.master_index, self.slave_index) }
}
pub fn active_ports(&self) -> u8 {
unsafe { ffi::GetSlaveActivePorts(self.master_index, self.slave_index) }
}
pub fn topology(&self) -> u8 {
self.read_slave().map(|s| s.topo.link_count).unwrap_or(0)
}
pub fn entry_port(&self) -> u8 {
self.read_slave().map(|s| s.topo.entry_port).unwrap_or(0)
}
pub fn parent(&self) -> u16 {
unsafe { ffi::GetSlaveParent(self.master_index, self.slave_index) }
}
pub fn parent_port(&self) -> u8 {
self.read_slave().map(|s| s.topo.parent_port).unwrap_or(0)
}
pub fn eep_8byte_addressing(&self) -> bool {
self.read_slave().map(|s| s.eeprom_config.read_8byte != 0).unwrap_or(false)
}
pub fn eep_pdi(&self) -> u8 {
self.read_slave().map(|s| s.eeprom_config.pdi).unwrap_or(0)
}
pub fn coe_details(&self) -> u8 {
self.read_slave().map(|s| s.capabilities.coe_details).unwrap_or(0)
}
pub fn foe_details(&self) -> u8 {
self.read_slave().map(|s| s.capabilities.foe_details).unwrap_or(0)
}
pub fn eoe_details(&self) -> u8 {
self.read_slave().map(|s| s.capabilities.eoe_details).unwrap_or(0)
}
pub fn soe_details(&self) -> u8 {
self.read_slave().map(|s| s.capabilities.soe_details).unwrap_or(0)
}
pub fn fmmu0_func(&self) -> u8 {
self.read_slave().map(|s| s.sm_fmmu.fmmu_func[0]).unwrap_or(0)
}
pub fn fmmu1_func(&self) -> u8 {
self.read_slave().map(|s| s.sm_fmmu.fmmu_func[1]).unwrap_or(0)
}
pub fn fmmu2_func(&self) -> u8 {
self.read_slave().map(|s| s.sm_fmmu.fmmu_func[2]).unwrap_or(0)
}
pub fn fmmu3_func(&self) -> u8 {
self.read_slave().map(|s| s.sm_fmmu.fmmu_func[3]).unwrap_or(0)
}
pub fn sync_manager_count(&self) -> u16 {
self.read_slave().map(|s| s.metadata.sm_count).unwrap_or(0)
}
pub fn supports_complete_access(&self) -> bool {
self.read_slave().map(|s| s.pdo_config.supports_complete_access != 0).unwrap_or(false)
}
pub fn set_supports_complete_access(&self, value: bool) {
if let Some(ptr) = self.slave_ptr() {
unsafe { (*(ptr as *mut EcSlaveBlittable)).pdo_config.supports_complete_access = if value { 1 } else { 0 }; }
}
}
pub fn pdo_assignment_enabled(&self) -> bool {
self.read_slave().map(|s| s.pdo_config.assignment_enabled != 0).unwrap_or(true)
}
pub fn set_pdo_assignment_enabled(&self, value: bool) {
if let Some(ptr) = self.slave_ptr() {
unsafe { (*(ptr as *mut EcSlaveBlittable)).pdo_config.assignment_enabled = if value { 1 } else { 0 }; }
}
}
pub fn pdo_configuration_enabled(&self) -> bool {
self.read_slave().map(|s| s.pdo_config.configuration_enabled != 0).unwrap_or(false)
}
pub fn set_pdo_configuration_enabled(&self, value: bool) {
if let Some(ptr) = self.slave_ptr() {
unsafe { (*(ptr as *mut EcSlaveBlittable)).pdo_config.configuration_enabled = if value { 1 } else { 0 }; }
}
}
pub fn group(&self) -> u8 {
unsafe { ffi::GetSlaveGroup(self.master_index, self.slave_index) }
}
pub fn set_group(&self, group: u8) -> Result<()> {
if unsafe { ffi::SetSlaveGroup(self.master_index, self.slave_index, group) } != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("设置从站分组失败").into()))
}
}
pub fn is_optional(&self) -> bool {
(unsafe { ffi::GetSlaveOptional(self.master_index, self.slave_index) }) != 0
}
pub fn set_optional(&self, optional: bool) -> Result<()> {
if unsafe { ffi::SetSlaveOptional(self.master_index, self.slave_index, if optional { 1 } else { 0 }) } != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("设置可选标志失败").into()))
}
}
pub fn supports_frame_repeat(&self) -> bool {
(unsafe { ffi::GetSlaveSupportsFrameRepeat(self.master_index, self.slave_index) }) != 0
}
pub fn set_supports_frame_repeat(&self, supports: bool) -> Result<()> {
if unsafe { ffi::SetSlaveSupportsFrameRepeat(self.master_index, self.slave_index, if supports { 1 } else { 0 }) } != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("设置帧重发支持标志失败").into()))
}
}
pub fn set_watchdog(&self, timeout_ms: u32) -> Result<()> {
if unsafe { ffi::SetSlaveWatchdog(self.master_index, self.slave_index, timeout_ms) } != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("设置看门狗失败").into()))
}
}
pub fn set_pdi_watchdog(&self, timeout_ms: u32) -> Result<()> {
if unsafe { ffi::SetSlavePdiWatchdog(self.master_index, self.slave_index, timeout_ms) } != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("设置 PDI 看门狗失败").into()))
}
}
pub fn watchdog_config(&self) -> Result<ffi::WatchdogConfig> {
let mut config = ffi::WatchdogConfig { wd_divider: 0, wd_time_pdi: 0, wd_time_pd: 0 };
if unsafe { ffi::GetSlaveWatchdogConfig(self.master_index, self.slave_index, &mut config) } != 0 {
Ok(config)
} else {
Err(DarraError::Other(obfstr::obfstr!("获取看门狗配置失败").into()))
}
}
pub fn watchdog_status(&self) -> Result<ffi::WatchdogStatus> {
let mut status = ffi::WatchdogStatus { wd_status: 0, wd_counter: 0, wd_divider: 0, wd_time_pd: 0 };
if unsafe { ffi::GetSlaveWatchdogStatus(self.master_index, self.slave_index, &mut status) } != 0 {
Ok(status)
} else {
Err(DarraError::Other(obfstr::obfstr!("获取看门狗状态失败").into()))
}
}
pub fn set_state(&self, state: EcState, timeout_ms: u32) -> Result<()> {
let ret = unsafe {
ffi::SetSlaveStateWithTimeout(self.master_index, self.slave_index, state as c_int, timeout_ms)
};
if ret != 0 { Ok(()) } else { Err(DarraError::StateChangeFailed(state as u8)) }
}
pub fn set_dc_sync(&self, sync0_ns: u32, sync1_ns: u32, shift_ns: i32) {
unsafe {
ffi::SetSyncBySlaveIndex(self.master_index, self.slave_index, sync0_ns, sync1_ns, shift_ns);
}
}
pub fn sync_window_status(&self) -> Result<SyncWindowStatus> {
let mut diff: c_int = 0;
let mut max_diff: c_int = 0;
let mut min_diff: c_int = 0;
let mut in_sync: i32 = 0;
let mut out_count: u32 = 0;
if unsafe {
ffi::GetSlaveSyncWindowStatus(
self.master_index, self.slave_index,
&mut diff, &mut max_diff, &mut min_diff,
&mut in_sync, &mut out_count,
)
} != 0 {
Ok(SyncWindowStatus {
diff_ns: diff,
max_diff_ns: max_diff,
min_diff_ns: min_diff,
in_sync: in_sync != 0,
out_of_sync_count: out_count,
})
} else {
Err(DarraError::Other(obfstr::obfstr!("获取同步窗口状态失败").into()))
}
}
pub fn reset_sync_window_stats(&self) {
unsafe { ffi::ResetSlaveSyncWindowStats(self.master_index, self.slave_index) };
}
pub fn verify_identity(&self, expected: &SlaveIdentity, check_rev: bool, check_serial: bool) -> bool {
unsafe {
ffi::VerifySlaveIdentity(
self.master_index, self.slave_index, expected,
if check_rev { 1 } else { 0 },
if check_serial { 1 } else { 0 },
) != 0
}
}
pub fn esm_timeouts(&self) -> Result<EsmTimeouts> {
let mut t = EsmTimeouts { ip: 0, ps: 0, so: 0, os: 0, sp: 0, pi: 0, bi: 0, ib: 0 };
if unsafe { ffi::GetSlaveEsmTimeouts(self.master_index, self.slave_index, &mut t) } != 0 {
Ok(t)
} else {
Err(DarraError::Other(obfstr::obfstr!("获取 ESM 超时失败").into()))
}
}
pub fn set_esm_timeouts(&self, timeouts: &EsmTimeouts) -> Result<()> {
if unsafe { ffi::SetSlaveEsmTimeouts(self.master_index, self.slave_index, timeouts) } != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("设置 ESM 超时失败").into()))
}
}
pub fn write_register(&self, reg_addr: u16, data: &[u8]) -> Result<()> {
if unsafe { ffi::WriteSlaveRegister(self.master_index, self.slave_index, reg_addr, data.as_ptr(), data.len() as u32) } != 0 {
Ok(())
} else {
Err(DarraError::RegisterFailed)
}
}
pub fn read_register(&self, reg_addr: u16, len: u32) -> Result<Vec<u8>> {
let mut buf = vec![0u8; len as usize];
if unsafe { ffi::ReadSlaveRegister(self.master_index, self.slave_index, reg_addr, buf.as_mut_ptr(), len) } != 0 {
Ok(buf)
} else {
Err(DarraError::RegisterFailed)
}
}
pub fn read_eeprom(&self, byte_offset: u16, byte_length: u16) -> Result<Vec<u8>> {
if byte_length == 0 {
return Ok(Vec::new());
}
let word_addr = byte_offset >> 1;
let word_count = (byte_length + 1) >> 1;
let first_byte_odd = byte_offset & 1;
let mut result: Vec<u8> = Vec::with_capacity(byte_length as usize);
for i in 0..word_count {
let mut w: u16 = 0;
if unsafe { ffi::SIIReadWord(self.master_index, self.slave_index, word_addr + i, &mut w) } == 0 {
return Err(DarraError::Other(obfstr::obfstr!("EEPROM 读失败").into()));
}
let lo = (w & 0xFF) as u8;
let hi = ((w >> 8) & 0xFF) as u8;
if i == 0 && first_byte_odd != 0 {
if (result.len() as u16) < byte_length { result.push(hi); }
} else {
if (result.len() as u16) < byte_length { result.push(lo); }
if (result.len() as u16) < byte_length { result.push(hi); }
}
}
Ok(result)
}
pub fn write_eeprom(&self, byte_offset: u16, data: &[u8]) -> Result<()> {
if data.is_empty() || (byte_offset & 1) != 0 || (data.len() & 1) != 0 {
return Err(DarraError::InvalidParameter(obfstr::obfstr!("EEPROM 写参数无效").into()));
}
let word_addr = byte_offset >> 1;
let word_count = (data.len() / 2) as u16;
for i in 0..word_count {
let idx = (i as usize) * 2;
let w: u16 = (data[idx] as u16) | ((data[idx + 1] as u16) << 8);
if unsafe { ffi::SIIWriteWord(self.master_index, self.slave_index, word_addr + i, w) } == 0 {
return Err(DarraError::Other(obfstr::obfstr!("EEPROM 写失败").into()));
}
}
Ok(())
}
pub fn configure_sync_manager(&self, sm_index: u8, start_addr: u16, length: u16, control: u8, enable: bool) -> Result<()> {
if unsafe { ffi::ConfigureSyncManager(self.master_index, self.slave_index, sm_index, start_addr, length, control, if enable { 1 } else { 0 }) } != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("配置 SyncManager 失败").into()))
}
}
pub fn configure_fmmu(
&self, fmmu_index: u8, logical_addr: u32, length: u16,
logical_start_bit: u8, logical_end_bit: u8,
physical_addr: u16, physical_start_bit: u8,
fmmu_type: u8, enable: bool,
) -> Result<()> {
if unsafe {
ffi::ConfigureFMMU(
self.master_index, self.slave_index, fmmu_index,
logical_addr, length, logical_start_bit, logical_end_bit,
physical_addr, physical_start_bit, fmmu_type,
if enable { 1 } else { 0 },
)
} != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("配置 FMMU 失败").into()))
}
}
pub fn write_dl_port(&self, value: u8) -> bool {
unsafe { ffi::WriteSlaveDLPORT(self.master_index, self.slave_index, value) != 0 }
}
pub fn read_dl_port(&self) -> Option<u8> {
let mut val: u8 = 0;
let ok = unsafe { ffi::ReadSlaveDLPORT(self.master_index, self.slave_index, &mut val) };
if ok != 0 { Some(val) } else { None }
}
pub fn is_op_only(&self) -> bool {
unsafe { ffi::GetSlaveOpOnlyFlag(self.master_index, self.slave_index) != 0 }
}
pub fn is_device_emulation(&self) -> bool {
unsafe { ffi::GetSlaveDeviceEmulationFlag(self.master_index, self.slave_index) != 0 }
}
pub fn enable_output_sync_manager(&self) -> Result<()> {
if unsafe { ffi::EnableOutputSyncManager(self.master_index, self.slave_index) } != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("启用输出 SyncManager 失败").into()))
}
}
pub fn disable_output_sync_manager(&self) -> Result<()> {
if unsafe { ffi::DisableOutputSyncManager(self.master_index, self.slave_index) } != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("禁用输出 SyncManager 失败").into()))
}
}
pub fn set_error_ack(&self, set_ack: bool) -> Result<()> {
if unsafe { ffi::SetSlaveErrorAck(self.master_index, self.slave_index, if set_ack { 1 } else { 0 }) } != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("设置错误确认位失败").into()))
}
}
pub fn needs_startup_reconfig(&self) -> bool {
unsafe { ffi::GetSlaveNeedsStartupReconfig(self.master_index, self.slave_index) != 0 }
}
pub fn clear_startup_reconfig(&self) {
unsafe { ffi::ClearSlaveNeedsStartupReconfig(self.master_index, self.slave_index) };
}
pub fn sdo_read(&self, index: u16, subindex: u8, complete_access: bool) -> Result<Vec<u8>> {
let mut size: c_int = 0;
let ptr = unsafe {
ffi::SDOread(self.master_index, self.slave_index, index, subindex,
if complete_access { 1 } else { 0 }, &mut size)
};
if ptr.is_null() || size <= 0 {
if !ptr.is_null() { unsafe { ffi::FreeMemory(ptr as *mut _) }; }
return Err(DarraError::SdoReadFailed { index, subindex });
}
let data = unsafe { std::slice::from_raw_parts(ptr, size as usize).to_vec() };
unsafe { ffi::FreeMemory(ptr as *mut _) };
Ok(data)
}
pub fn sdo_read_value<T: Copy>(&self, index: u16, subindex: u8) -> Result<T> {
let data = self.sdo_read(index, subindex, false)?;
if data.len() < std::mem::size_of::<T>() {
return Err(DarraError::SdoReadFailed { index, subindex });
}
Ok(unsafe { std::ptr::read_unaligned(data.as_ptr() as *const T) })
}
pub fn sdo_write(&self, index: u16, subindex: u8, complete_access: bool, data: &[u8]) -> Result<()> {
let ok = unsafe {
ffi::SDOwrite_raw(self.master_index, self.slave_index, index, subindex,
if complete_access { 1 } else { 0 }, data.as_ptr(), data.len() as c_int)
};
if ok != 0 { Ok(()) } else { Err(DarraError::SdoWriteFailed { index, subindex }) }
}
pub fn sdo_write_value<T: Copy>(&self, index: u16, subindex: u8, value: &T) -> Result<()> {
let bytes = unsafe {
std::slice::from_raw_parts(value as *const T as *const u8, std::mem::size_of::<T>())
};
self.sdo_write(index, subindex, false, bytes)
}
pub fn soe_read(&self, idn: u16, drive_no: u8, element_flags: u8, timeout_us: i32) -> Result<Vec<u8>> {
let mut data_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut data_size: c_int = 0;
let ok = unsafe {
ffi::SoERead(self.master_index, self.slave_index, drive_no, element_flags, idn,
&mut data_ptr, &mut data_size, timeout_us)
};
if ok == 0 || data_ptr.is_null() || data_size <= 0 {
if !data_ptr.is_null() { unsafe { ffi::FreeMemory(data_ptr) }; }
return Err(DarraError::SoeFailed(idn));
}
let data = unsafe { std::slice::from_raw_parts(data_ptr as *const u8, data_size as usize).to_vec() };
unsafe { ffi::FreeMemory(data_ptr) };
Ok(data)
}
pub fn soe_write(&self, idn: u16, drive_no: u8, element_flags: u8, data: &[u8], timeout_us: i32) -> Result<()> {
let ok = unsafe {
ffi::SoEWrite(self.master_index, self.slave_index, drive_no, element_flags, idn,
data.as_ptr(), data.len() as c_int, timeout_us)
};
if ok != 0 { Ok(()) } else { Err(DarraError::SoeFailed(idn)) }
}
pub fn soe_read_name(&self, idn: u16, drive_no: u8, timeout_us: i32) -> Result<String> {
let mut name_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut name_len: c_int = 0;
let ok = unsafe {
ffi::SoEReadName(self.master_index, self.slave_index, drive_no, idn,
&mut name_ptr, &mut name_len, timeout_us)
};
if ok == 0 || name_ptr.is_null() || name_len <= 0 {
if !name_ptr.is_null() { unsafe { ffi::FreeMemory(name_ptr) }; }
return Err(DarraError::SoeFailed(idn));
}
let bytes = unsafe { std::slice::from_raw_parts(name_ptr as *const u8, name_len as usize) };
let name = crate::utils::help::decode_ethercat_string(bytes);
unsafe { ffi::FreeMemory(name_ptr) };
Ok(name)
}
pub fn soe_read_unit(&self, idn: u16, drive_no: u8, timeout_us: i32) -> Result<String> {
let mut unit_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut unit_len: c_int = 0;
let ok = unsafe {
ffi::SoEReadUnit(self.master_index, self.slave_index, drive_no, idn,
&mut unit_ptr, &mut unit_len, timeout_us)
};
if ok == 0 || unit_ptr.is_null() || unit_len <= 0 {
if !unit_ptr.is_null() { unsafe { ffi::FreeMemory(unit_ptr) }; }
return Err(DarraError::SoeFailed(idn));
}
let bytes = unsafe { std::slice::from_raw_parts(unit_ptr as *const u8, unit_len as usize) };
let unit = crate::utils::help::decode_ethercat_string(bytes);
unsafe { ffi::FreeMemory(unit_ptr) };
Ok(unit)
}
pub fn soe_read_min_max(&self, idn: u16, drive_no: u8, timeout_us: i32) -> Result<(Vec<u8>, Vec<u8>)> {
let mut min_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut min_size: c_int = 0;
let mut max_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut max_size: c_int = 0;
let ok = unsafe {
ffi::SoEReadMinMax(self.master_index, self.slave_index, drive_no, idn,
&mut min_ptr, &mut min_size, &mut max_ptr, &mut max_size, timeout_us)
};
if ok == 0 {
return Err(DarraError::SoeFailed(idn));
}
let min_data = if !min_ptr.is_null() && min_size > 0 {
let d = unsafe { std::slice::from_raw_parts(min_ptr as *const u8, min_size as usize).to_vec() };
unsafe { ffi::FreeMemory(min_ptr) };
d
} else { Vec::new() };
let max_data = if !max_ptr.is_null() && max_size > 0 {
let d = unsafe { std::slice::from_raw_parts(max_ptr as *const u8, max_size as usize).to_vec() };
unsafe { ffi::FreeMemory(max_ptr) };
d
} else { Vec::new() };
Ok((min_data, max_data))
}
pub fn soe_read_attributes(&self, idn: u16, drive_no: u8, timeout_us: i32) -> Result<u32> {
let mut attrs: u32 = 0;
let ok = unsafe {
ffi::SoEReadAttributes(self.master_index, self.slave_index, drive_no, idn,
&mut attrs, timeout_us)
};
if ok != 0 { Ok(attrs) } else { Err(DarraError::SoeFailed(idn)) }
}
pub fn soe_read_idn_list(&self, drive_no: u8, timeout_us: i32) -> Result<Vec<u16>> {
let mut list_ptr: *mut u16 = std::ptr::null_mut();
let mut list_count: c_int = 0;
let ok = unsafe {
ffi::SoEReadIDNList(self.master_index, self.slave_index, drive_no,
&mut list_ptr, &mut list_count, timeout_us)
};
if ok == 0 || list_ptr.is_null() || list_count <= 0 {
if !list_ptr.is_null() { unsafe { ffi::FreeMemory(list_ptr as *mut _) }; }
return Err(DarraError::SoeFailed(0));
}
let idns = unsafe {
std::slice::from_raw_parts(list_ptr, list_count as usize).to_vec()
};
unsafe { ffi::FreeMemory(list_ptr as *mut _) };
Ok(idns)
}
pub fn foe_read(&self, filename: &str, password: u32, timeout_us: i32) -> Result<Vec<u8>> {
let c_filename = CString::new(filename)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("文件名包含空字节").into()))?;
let mut file_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut file_size: c_int = 0;
let ok = unsafe {
ffi::FOERead(self.master_index, self.slave_index, c_filename.as_ptr(),
password, &mut file_ptr, &mut file_size, timeout_us)
};
if ok == 0 || file_ptr.is_null() {
return Err(DarraError::FoeFailed(filename.to_string()));
}
let data = if file_size > 0 {
unsafe { std::slice::from_raw_parts(file_ptr as *const u8, file_size as usize).to_vec() }
} else { Vec::new() };
unsafe { ffi::FreeMemory(file_ptr) };
Ok(data)
}
pub fn foe_write(&self, filename: &str, password: u32, data: &[u8], timeout_us: i32) -> Result<()> {
let c_filename = CString::new(filename)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("文件名包含空字节").into()))?;
let ok = unsafe {
ffi::FOEWrite(self.master_index, self.slave_index, c_filename.as_ptr(),
password, data.as_ptr() as *const _, data.len() as c_int, timeout_us)
};
if ok != 0 { Ok(()) } else { Err(DarraError::FoeFailed(filename.to_string())) }
}
pub fn foe_read_ex(&self, filename: &str, password: u32, timeout_us: i32, options: &mut FoEOptions) -> Result<Vec<u8>> {
let c_filename = CString::new(filename)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("文件名包含空字节").into()))?;
let mut file_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut file_size: c_int = 0;
let ok = unsafe {
ffi::FOEReadEx(self.master_index, self.slave_index, c_filename.as_ptr(),
password, &mut file_ptr, &mut file_size, timeout_us, options)
};
if ok == 0 || file_ptr.is_null() {
return Err(DarraError::FoeFailed(filename.to_string()));
}
let data = if file_size > 0 {
unsafe { std::slice::from_raw_parts(file_ptr as *const u8, file_size as usize).to_vec() }
} else { Vec::new() };
unsafe { ffi::FreeMemory(file_ptr) };
Ok(data)
}
pub fn foe_write_ex(&self, filename: &str, password: u32, data: &[u8], timeout_us: i32, options: &mut FoEOptions) -> Result<()> {
let c_filename = CString::new(filename)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("文件名包含空字节").into()))?;
let ok = unsafe {
ffi::FOEWriteEx(self.master_index, self.slave_index, c_filename.as_ptr(),
password, data.as_ptr() as *const _, data.len() as c_int, timeout_us, options)
};
if ok != 0 { Ok(()) } else { Err(DarraError::FoeFailed(filename.to_string())) }
}
pub fn foe_set_progress_hook(master_index: u16, callback: ffi::FoEProgressCallback) -> Result<()> {
if unsafe { ffi::FOESetProgressHook(master_index, Some(callback)) } != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("设置 FoE 进度回调失败").into()))
}
}
pub fn foe_clear_progress_hook(master_index: u16) -> Result<()> {
if unsafe { ffi::FOEClearProgressHook(master_index) } != 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("清除 FoE 进度回调失败").into()))
}
}
pub fn eoe_set_ip(&self, port: u8, ip: u32, subnet: u32, gateway: u32, timeout_us: i32) -> Result<()> {
let ok = unsafe {
ffi::EOESetIP(self.master_index, self.slave_index, port, ip, subnet, gateway, timeout_us)
};
if ok != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
}
pub fn eoe_ip(&self, port: u8, timeout_us: i32) -> Result<(u32, u32, u32)> {
let mut ip: u32 = 0;
let mut subnet: u32 = 0;
let mut gateway: u32 = 0;
let ok = unsafe {
ffi::EOEGetIP(self.master_index, self.slave_index, port,
&mut ip, &mut subnet, &mut gateway, timeout_us)
};
if ok != 0 { Ok((ip, subnet, gateway)) } else { Err(DarraError::EoeFailed) }
}
pub fn eoe_send_frame(&self, port: u8, data: &[u8], timeout_us: i32) -> Result<()> {
let ok = unsafe {
ffi::EOESendFrame(self.master_index, self.slave_index, port,
data.as_ptr(), data.len() as c_int, timeout_us)
};
if ok != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
}
pub fn eoe_receive_frame(&self, port: u8, timeout_us: i32) -> Result<Vec<u8>> {
let mut frame_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut frame_size: c_int = 0;
let ok = unsafe {
ffi::EOEReceiveFrame(self.master_index, self.slave_index, port,
&mut frame_ptr, &mut frame_size, timeout_us)
};
if ok == 0 || frame_ptr.is_null() {
return Err(DarraError::EoeFailed);
}
let data = if frame_size > 0 {
unsafe { std::slice::from_raw_parts(frame_ptr as *const u8, frame_size as usize).to_vec() }
} else {
Vec::new()
};
unsafe { ffi::FreeMemory(frame_ptr) };
Ok(data)
}
pub fn eoe_set_mac(&self, port: u8, mac: &[u8; 6], timeout_us: i32) -> Result<()> {
let ok = unsafe {
ffi::EOESetMAC(self.master_index, self.slave_index, port, mac.as_ptr(), timeout_us)
};
if ok != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
}
pub fn eoe_mac(&self, port: u8, timeout_us: i32) -> Result<[u8; 6]> {
let mut mac = [0u8; 6];
let ok = unsafe {
ffi::EOEGetMAC(self.master_index, self.slave_index, port, mac.as_mut_ptr(), timeout_us)
};
if ok != 0 { Ok(mac) } else { Err(DarraError::EoeFailed) }
}
pub fn eoe_set_dns(&self, port: u8, dns_ip: u32, dns_name: &str, timeout_us: i32) -> Result<()> {
let c_name = CString::new(dns_name)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("DNS 名称包含空字节").into()))?;
let ok = unsafe {
ffi::EOESetDNS(self.master_index, self.slave_index, port, dns_ip, c_name.as_ptr(), timeout_us)
};
if ok != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
}
pub fn eoe_dns(&self, port: u8, timeout_us: i32) -> Result<(u32, String)> {
let mut dns_ip: u32 = 0;
let mut dns_name_buf = [0i8; 256];
let ok = unsafe {
ffi::EOEGetDNS(self.master_index, self.slave_index, port,
&mut dns_ip, dns_name_buf.as_mut_ptr(), timeout_us)
};
if ok == 0 { return Err(DarraError::EoeFailed); }
let name = unsafe { std::ffi::CStr::from_ptr(dns_name_buf.as_ptr()) }
.to_string_lossy().into_owned();
Ok((dns_ip, name))
}
pub fn eoe_full_param(&self, port: u8, timeout_us: i32) -> Result<(u32, u32, u32, [u8; 6], u32, String)> {
let mut ip: u32 = 0;
let mut subnet: u32 = 0;
let mut gateway: u32 = 0;
let mut mac = [0u8; 6];
let mut dns_ip: u32 = 0;
let mut dns_name_buf = [0i8; 256];
let ok = unsafe {
ffi::EOEGetFullParam(
self.master_index, self.slave_index, port,
&mut ip, &mut subnet, &mut gateway,
mac.as_mut_ptr(), &mut dns_ip, dns_name_buf.as_mut_ptr(), timeout_us,
)
};
if ok == 0 { return Err(DarraError::EoeFailed); }
let name = unsafe { std::ffi::CStr::from_ptr(dns_name_buf.as_ptr()) }
.to_string_lossy().into_owned();
Ok((ip, subnet, gateway, mac, dns_ip, name))
}
pub fn eoe_set_full_param(
&self, port: u8, ip: u32, subnet: u32, gateway: u32,
mac: &[u8; 6], dns_ip: u32, dns_name: &str, timeout_us: i32,
) -> Result<()> {
let c_name = CString::new(dns_name)
.map_err(|_| DarraError::InvalidParameter(obfstr::obfstr!("DNS 名称包含空字节").into()))?;
let ok = unsafe {
ffi::EOESetFullParam(
self.master_index, self.slave_index, port,
ip, subnet, gateway, mac.as_ptr(), dns_ip, c_name.as_ptr(), timeout_us,
)
};
if ok != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
}
pub fn eoe_set_address_filter(&self, port: u8, mac_filters: &[u8], filter_count: u8, timeout_us: i32) -> Result<()> {
let ok = unsafe {
ffi::EOESetAddressFilter(
self.master_index, self.slave_index, port,
filter_count, mac_filters.as_ptr(), timeout_us,
)
};
if ok != 0 { Ok(()) } else { Err(DarraError::EoeFailed) }
}
pub fn eoe_address_filter(&self, port: u8, max_filters: i32, timeout_us: i32) -> Result<(u8, Vec<u8>)> {
let mut filter_count: u8 = 0;
let buf_size = (max_filters as usize) * 6;
let mut mac_buf = vec![0u8; buf_size];
let ok = unsafe {
ffi::EOEGetAddressFilter(
self.master_index, self.slave_index, port,
&mut filter_count, mac_buf.as_mut_ptr(), max_filters, timeout_us,
)
};
if ok == 0 { return Err(DarraError::EoeFailed); }
mac_buf.truncate((filter_count as usize) * 6);
Ok((filter_count, mac_buf))
}
pub fn aoe_read_write(&self, index_group: u32, index_offset: u32, read_length: u32, write_data: &[u8], timeout_us: i32) -> Result<Vec<u8>> {
let mut read_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut bytes_read: u32 = 0;
let ok = unsafe {
ffi::AOEReadWrite(self.master_index, self.slave_index, index_group, index_offset,
read_length, write_data.len() as u32, write_data.as_ptr(),
&mut read_ptr, &mut bytes_read, timeout_us)
};
if ok == 0 { return Err(DarraError::AoeFailed); }
let data = if !read_ptr.is_null() && bytes_read > 0 {
let d = unsafe { std::slice::from_raw_parts(read_ptr as *const u8, bytes_read as usize).to_vec() };
unsafe { ffi::FreeMemory(read_ptr) };
d
} else { Vec::new() };
Ok(data)
}
pub fn aoe_read_device_info(&self, timeout_us: i32) -> Result<(u8, u8, u16, String)> {
let mut major: u8 = 0;
let mut minor: u8 = 0;
let mut build: u16 = 0;
let mut name_buf = [0u8; 64];
let ok = unsafe {
ffi::AOEReadDeviceInfo(self.master_index, self.slave_index,
&mut major, &mut minor, &mut build,
name_buf.as_mut_ptr(), 64, timeout_us)
};
if ok == 0 { return Err(DarraError::AoeFailed); }
let name_len = name_buf.iter().position(|&b| b == 0).unwrap_or(64);
let name = crate::utils::help::decode_ethercat_string(&name_buf[..name_len]);
Ok((major, minor, build, name))
}
pub fn aoe_read_state(&self, timeout_us: i32) -> Result<(u16, u16)> {
let mut ads_state: u16 = 0;
let mut device_state: u16 = 0;
let ok = unsafe {
ffi::AOEReadState(self.master_index, self.slave_index,
&mut ads_state, &mut device_state, timeout_us)
};
if ok != 0 { Ok((ads_state, device_state)) } else { Err(DarraError::AoeFailed) }
}
pub fn aoe_write_control(&self, ads_state: u16, device_state: u16, data: &[u8], timeout_us: i32) -> Result<()> {
let ok = unsafe {
ffi::AOEWriteControl(self.master_index, self.slave_index,
ads_state, device_state, data.as_ptr(), data.len() as c_int, timeout_us)
};
if ok != 0 { Ok(()) } else { Err(DarraError::AoeFailed) }
}
pub fn aoe_add_notification(
&self, index_group: u32, index_offset: u32, length: u32,
trans_mode: u32, max_delay: u32, cycle_time: u32, timeout_us: i32,
) -> Result<u32> {
let mut handle: u32 = 0;
let ok = unsafe {
ffi::AOEAddNotification(self.master_index, self.slave_index,
index_group, index_offset, length,
trans_mode, max_delay, cycle_time,
&mut handle, timeout_us)
};
if ok != 0 { Ok(handle) } else { Err(DarraError::AoeFailed) }
}
pub fn aoe_del_notification(&self, handle: u32, timeout_us: i32) -> Result<()> {
let ok = unsafe {
ffi::AOEDelNotification(self.master_index, self.slave_index, handle, timeout_us)
};
if ok != 0 { Ok(()) } else { Err(DarraError::AoeFailed) }
}
pub fn aoe_set_config(&self, target_net_id: &[u8; 6], target_port: u16, source_net_id: &[u8; 6], source_port: u16) -> Result<()> {
let ok = unsafe {
ffi::AOESetConfig(self.master_index, self.slave_index,
target_net_id.as_ptr(), target_port,
source_net_id.as_ptr(), source_port)
};
if ok != 0 { Ok(()) } else { Err(DarraError::AoeFailed) }
}
pub fn aoe_config(&self) -> Result<([u8; 6], u16, [u8; 6], u16)> {
let mut target_net_id = [0u8; 6];
let mut target_port: u16 = 0;
let mut source_net_id = [0u8; 6];
let mut source_port: u16 = 0;
let ok = unsafe {
ffi::AOEGetConfig(self.master_index, self.slave_index,
target_net_id.as_mut_ptr(), &mut target_port,
source_net_id.as_mut_ptr(), &mut source_port)
};
if ok != 0 { Ok((target_net_id, target_port, source_net_id, source_port)) }
else { Err(DarraError::AoeFailed) }
}
pub fn aoe_send_command(&self, target_port: u16, command_id: u16, command_data: &[u8], timeout_us: i32) -> Result<Vec<u8>> {
let mut response_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut response_size: u32 = 0;
let ok = unsafe {
ffi::AOESendCommand(
self.master_index, self.slave_index, target_port, command_id,
command_data.as_ptr(), command_data.len() as u32,
&mut response_ptr, &mut response_size, timeout_us,
)
};
if ok == 0 { return Err(DarraError::AoeFailed); }
let data = if !response_ptr.is_null() && response_size > 0 {
let d = unsafe { std::slice::from_raw_parts(response_ptr as *const u8, response_size as usize).to_vec() };
unsafe { ffi::FreeMemory(response_ptr) };
d
} else { Vec::new() };
Ok(data)
}
pub fn voe_is_supported(&self) -> bool {
(unsafe { ffi::VOEIsSupported(self.master_index, self.slave_index) }) != 0
}
pub fn voe_send(&self, vendor_id: u32, vendor_type: u16, data: &[u8], timeout_us: i32) -> Result<()> {
let ok = unsafe {
ffi::VOESend(self.master_index, self.slave_index, vendor_id, vendor_type,
data.as_ptr(), data.len() as c_int, timeout_us)
};
if ok != 0 { Ok(()) } else { Err(DarraError::VoeFailed) }
}
pub fn voe_receive(&self, timeout_us: i32) -> Result<(u32, u16, Vec<u8>)> {
let mut vendor_id: u32 = 0;
let mut vendor_type: u16 = 0;
let mut data_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut data_size: c_int = 0;
let ok = unsafe {
ffi::VOEReceive(self.master_index, self.slave_index,
&mut vendor_id, &mut vendor_type, &mut data_ptr, &mut data_size, timeout_us)
};
if ok == 0 { return Err(DarraError::VoeFailed); }
let data = if !data_ptr.is_null() && data_size > 0 {
let d = unsafe { std::slice::from_raw_parts(data_ptr as *const u8, data_size as usize).to_vec() };
unsafe { ffi::FreeMemory(data_ptr) };
d
} else { Vec::new() };
Ok((vendor_id, vendor_type, data))
}
pub fn voe_send_raw(&self, frame_data: &[u8], timeout_us: i32) -> Result<()> {
let ok = unsafe {
ffi::VOESendRaw(self.master_index, self.slave_index,
frame_data.as_ptr(), frame_data.len() as c_int, timeout_us)
};
if ok != 0 { Ok(()) } else { Err(DarraError::VoeFailed) }
}
pub fn voe_receive_raw(&self, timeout_us: i32) -> Result<Vec<u8>> {
let mut data_ptr: *mut std::ffi::c_void = std::ptr::null_mut();
let mut data_size: c_int = 0;
let ok = unsafe {
ffi::VOEReceiveRaw(self.master_index, self.slave_index,
&mut data_ptr, &mut data_size, timeout_us)
};
if ok == 0 { return Err(DarraError::VoeFailed); }
let data = if !data_ptr.is_null() && data_size > 0 {
let d = unsafe { std::slice::from_raw_parts(data_ptr as *const u8, data_size as usize).to_vec() };
unsafe { ffi::FreeMemory(data_ptr) };
d
} else { Vec::new() };
Ok(data)
}
pub fn add_startup_parameter(
&self, index: u16, sub_index: u8, data: &[u8],
transition: u8, timing: u8, complete_access: bool,
) -> Result<()> {
let mut param = ffi::StartupParam {
index, sub_index, data: [0u8; 256],
data_len: data.len().min(256) as u16,
transition, timing, priority: 0,
complete_access: if complete_access { 1 } else { 0 },
is_register_write: 0,
};
let copy_len = data.len().min(256);
param.data[..copy_len].copy_from_slice(&data[..copy_len]);
let ret = unsafe { ffi::AddStartupParameter(self.master_index, self.slave_index, ¶m) };
if ret >= 0 { Ok(()) }
else { Err(DarraError::Other(format!("添加启动参数失败 (返回码: {})", ret))) }
}
pub fn clear_startup_parameters(&self) -> Result<()> {
let ret = unsafe { ffi::ClearStartupParameters(self.master_index, self.slave_index) };
if ret >= 0 { Ok(()) } else { Err(DarraError::Other(obfstr::obfstr!("清除启动参数失败").into())) }
}
pub fn startup_parameter_count(&self) -> i32 {
unsafe { ffi::GetStartupParameterCount(self.master_index, self.slave_index) }
}
pub fn apply_startup_parameters(&self, transition: u8, timing: u8) -> Result<i32> {
let ret = unsafe {
ffi::DarraCoreInvoke(
self.master_index,
ffi::CORE_OP_26,
self.slave_index as u32,
1u32 << transition,
1u32 << timing)
};
if ret >= 0 { Ok(ret) }
else { Err(DarraError::Other(format!("执行启动参数失败 (返回码: {})", ret))) }
}
pub fn write_output(&self, data: &[u8]) {
unsafe { ffi::WriteSlaveOutput(self.master_index, self.slave_index, data.as_ptr(), data.len() as u32) };
}
pub fn write_output_byte(&self, offset: u32, value: u8) {
unsafe { ffi::WriteSlaveOutputByte(self.master_index, self.slave_index, offset, value) };
}
pub fn cia402_state(&self) -> CiA402State {
let sw = unsafe { ffi::CiA402_ReadStatusWord(self.master_index, self.slave_index) };
let raw = unsafe { ffi::CiA402_ParseState(sw) };
CiA402State::from_raw(raw)
}
pub fn cia402_enable(&self) -> Result<()> {
let ret = unsafe { ffi::CiA402_Enable(self.master_index, self.slave_index, 50) };
if ret >= 0 { Ok(()) }
else { Err(DarraError::CiA402Failed(format!("使能失败 (返回码: {})", ret))) }
}
pub fn cia402_set_mode(&self, mode: i8) -> Result<()> {
let ret = unsafe { ffi::CiA402_SetMode(self.master_index, self.slave_index, mode) };
if ret >= 0 { Ok(()) }
else { Err(DarraError::CiA402Failed(format!("设置模式失败 (返回码: {})", ret))) }
}
pub fn cia402_mode(&self) -> i8 {
unsafe { ffi::CiA402_GetMode(self.master_index, self.slave_index) }
}
pub fn cia402_status_word(&self) -> u16 {
unsafe { ffi::CiA402_ReadStatusWord(self.master_index, self.slave_index) }
}
pub fn cia402_write_control_word(&self, cw: u16) -> Result<()> {
let ret = unsafe { ffi::CiA402_WriteControlWord(self.master_index, self.slave_index, cw) };
if ret >= 0 { Ok(()) }
else { Err(DarraError::CiA402Failed("写入控制字失败".into())) }
}
pub fn cia402_fault_reset(&self) -> Result<()> {
let ret = unsafe { ffi::CiA402_FaultReset(self.master_index, self.slave_index) };
if ret >= 0 { Ok(()) } else { Err(DarraError::CiA402Failed("故障复位失败".into())) }
}
pub fn emcy_history(&self) -> Vec<EmcyRecord> {
let mut buf = vec![
EmcyRecord { error_code: 0, error_register: 0, data: [0u8; 5], slave_index: 0, timestamp_ms: 0 };
64
];
let count = unsafe {
ffi::EmcyGetHistory(self.master_index, self.slave_index, buf.as_mut_ptr(), 64)
};
if count > 0 { buf.truncate(count as usize); buf }
else { Vec::new() }
}
pub fn emcy_count(&self) -> usize {
let count = unsafe { ffi::EmcyGetCount(self.master_index, self.slave_index) };
if count > 0 { count as usize } else { 0 }
}
pub fn clear_emcy(&self) {
unsafe { ffi::EmcyClearHistory(self.master_index, self.slave_index) };
}
pub fn read_input_u8(&self, offset: u32) -> u8 {
unsafe { ffi::PDOReadInputU8(self.master_index, self.slave_index, offset) }
}
pub fn read_input_i8(&self, offset: u32) -> i8 {
unsafe { ffi::PDOReadInputU8(self.master_index, self.slave_index, offset) as i8 }
}
pub fn read_input_i16(&self, offset: u32) -> i16 {
unsafe { ffi::PDOReadInputI16(self.master_index, self.slave_index, offset) }
}
pub fn read_input_u16(&self, offset: u32) -> u16 {
unsafe { ffi::PDOReadInputU16(self.master_index, self.slave_index, offset) }
}
pub fn read_input_i32(&self, offset: u32) -> i32 {
unsafe { ffi::PDOReadInputI32(self.master_index, self.slave_index, offset) }
}
pub fn read_input_u32(&self, offset: u32) -> u32 {
unsafe { ffi::PDOReadInputU32(self.master_index, self.slave_index, offset) }
}
pub fn read_input_f32(&self, offset: u32) -> f32 {
unsafe { ffi::PDOReadInputF32(self.master_index, self.slave_index, offset) }
}
pub fn write_output_u8(&self, offset: u32, value: u8) {
let _ = unsafe { ffi::PDOWriteOutputU8(self.master_index, self.slave_index, offset, value) };
}
pub fn write_output_i8(&self, offset: u32, value: i8) {
let _ = unsafe { ffi::PDOWriteOutputU8(self.master_index, self.slave_index, offset, value as u8) };
}
pub fn write_output_i16(&self, offset: u32, value: i16) {
let _ = unsafe { ffi::PDOWriteOutputI16(self.master_index, self.slave_index, offset, value) };
}
pub fn write_output_u16(&self, offset: u32, value: u16) {
let _ = unsafe { ffi::PDOWriteOutputU16(self.master_index, self.slave_index, offset, value) };
}
pub fn write_output_i32(&self, offset: u32, value: i32) {
let _ = unsafe { ffi::PDOWriteOutputI32(self.master_index, self.slave_index, offset, value) };
}
pub fn write_output_u32(&self, offset: u32, value: u32) {
let _ = unsafe { ffi::PDOWriteOutputU32(self.master_index, self.slave_index, offset, value) };
}
pub fn write_output_f32(&self, offset: u32, value: f32) {
let _ = unsafe { ffi::PDOWriteOutputF32(self.master_index, self.slave_index, offset, value) };
}
pub fn pdo_read_input_u8(&self, offset: u32) -> u8 {
unsafe { ffi::PDOReadInputU8(self.master_index, self.slave_index, offset) }
}
pub fn pdo_read_input_i16(&self, offset: u32) -> i16 {
unsafe { ffi::PDOReadInputI16(self.master_index, self.slave_index, offset) }
}
pub fn pdo_read_input_u16(&self, offset: u32) -> u16 {
unsafe { ffi::PDOReadInputU16(self.master_index, self.slave_index, offset) }
}
pub fn pdo_read_input_i32(&self, offset: u32) -> i32 {
unsafe { ffi::PDOReadInputI32(self.master_index, self.slave_index, offset) }
}
pub fn pdo_read_input_u32(&self, offset: u32) -> u32 {
unsafe { ffi::PDOReadInputU32(self.master_index, self.slave_index, offset) }
}
pub fn pdo_read_input_f32(&self, offset: u32) -> f32 {
unsafe { ffi::PDOReadInputF32(self.master_index, self.slave_index, offset) }
}
pub fn pdo_write_output_u8(&self, offset: u32, val: u8) -> Result<()> {
let ret = unsafe { ffi::PDOWriteOutputU8(self.master_index, self.slave_index, offset, val) };
if ret >= 0 { Ok(()) } else { Err(DarraError::Other(format!("PDO 写入 u8 失败 (返回码: {})", ret))) }
}
pub fn pdo_write_output_i16(&self, offset: u32, val: i16) -> Result<()> {
let ret = unsafe { ffi::PDOWriteOutputI16(self.master_index, self.slave_index, offset, val) };
if ret >= 0 { Ok(()) } else { Err(DarraError::Other(format!("PDO 写入 i16 失败 (返回码: {})", ret))) }
}
pub fn pdo_write_output_u16(&self, offset: u32, val: u16) -> Result<()> {
let ret = unsafe { ffi::PDOWriteOutputU16(self.master_index, self.slave_index, offset, val) };
if ret >= 0 { Ok(()) } else { Err(DarraError::Other(format!("PDO 写入 u16 失败 (返回码: {})", ret))) }
}
pub fn pdo_write_output_i32(&self, offset: u32, val: i32) -> Result<()> {
let ret = unsafe { ffi::PDOWriteOutputI32(self.master_index, self.slave_index, offset, val) };
if ret >= 0 { Ok(()) } else { Err(DarraError::Other(format!("PDO 写入 i32 失败 (返回码: {})", ret))) }
}
pub fn pdo_write_output_u32(&self, offset: u32, val: u32) -> Result<()> {
let ret = unsafe { ffi::PDOWriteOutputU32(self.master_index, self.slave_index, offset, val) };
if ret >= 0 { Ok(()) } else { Err(DarraError::Other(format!("PDO 写入 u32 失败 (返回码: {})", ret))) }
}
pub fn pdo_write_output_f32(&self, offset: u32, val: f32) -> Result<()> {
let ret = unsafe { ffi::PDOWriteOutputF32(self.master_index, self.slave_index, offset, val) };
if ret >= 0 { Ok(()) } else { Err(DarraError::Other(format!("PDO 写入 f32 失败 (返回码: {})", ret))) }
}
pub fn link_quality(&self) -> i16 {
unsafe { ffi::GetSlaveLinkQuality(self.master_index, self.slave_index) }
}
pub fn reset_port_error_counters(&self) -> bool {
(unsafe { ffi::ResetSlavePortErrorCounters(self.master_index, self.slave_index) }) != 0
}
pub fn read_port_error_counters(&self) -> 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.master_index, self.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 port_error_stats(&self) -> *const std::ffi::c_void {
unsafe { ffi::GetSlavePortErrorStats(self.master_index, self.slave_index) }
}
pub fn debug_dc(&self) {
unsafe { ffi::DebugSlaveHasDC(self.master_index, self.slave_index) };
}
pub fn ibits(&self) -> u16 { self.input_bits() }
pub fn ibytes(&self) -> u32 { self.input_bytes() }
pub fn obits(&self) -> u16 { self.output_bits() }
pub fn obytes(&self) -> u32 { self.output_bytes() }
pub fn ioffset(&self) -> u32 { self.input_offset() }
pub fn ooffset(&self) -> u32 { self.output_offset() }
pub fn istartbit(&self) -> u8 { self.input_startbit() }
pub fn ostartbit(&self) -> u8 { self.output_startbit() }
pub fn revision(&self) -> u32 { self.rev_id() }
pub fn pdelay(&self) -> i32 {
self.read_slave().map(|s| s.dc.propagation_delay).unwrap_or(0)
}
pub fn parent_station(&self) -> u16 {
self.read_slave().map(|s| s.topo.parent).unwrap_or(0)
}
pub fn mbx_proto(&self) -> u16 { self.mbx_protocol() }
pub fn has_esi(&self) -> bool {
unsafe { ffi::GetSlaveHasEsi(self.master_index, self.slave_index) != 0 }
}
pub fn set_esi_file(&self, name: &str) -> bool {
let c_name = match CString::new(name) {
Ok(s) => s,
Err(_) => return false,
};
unsafe { ffi::SetSlaveEsiFile(self.master_index, self.slave_index, c_name.as_ptr()) != 0 }
}
pub fn esi_version(&self) -> String {
let mut buf = [0i8; 64];
let ok = unsafe {
ffi::GetSlaveEsiVersion(self.master_index, self.slave_index, buf.as_mut_ptr(), 64)
};
if ok != 0 {
unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) }
.to_string_lossy().into_owned()
} else {
String::new()
}
}
pub fn has_mdp(&self) -> bool {
unsafe { ffi::GetSlaveHasMDP(self.master_index, self.slave_index) != 0 }
}
pub fn vendor_name(&self) -> String {
let mut buf = [0i8; 128];
let ok = unsafe {
ffi::GetSlaveVendorName(self.master_index, self.slave_index, buf.as_mut_ptr(), 128)
};
if ok != 0 {
unsafe { std::ffi::CStr::from_ptr(buf.as_ptr()) }
.to_string_lossy().into_owned()
} else {
String::new()
}
}
pub fn redundancy_activated(&self) -> bool {
unsafe { ffi::GetSlaveRedundancyActivated(self.master_index, self.slave_index) != 0 }
}
pub fn primary_link_broken(&self) -> bool {
unsafe { ffi::GetSlavePrimaryLinkBroken(self.master_index, self.slave_index) != 0 }
}
pub fn secondary_link_broken(&self) -> bool {
unsafe { ffi::GetSlaveSecondaryLinkBroken(self.master_index, self.slave_index) != 0 }
}
pub fn child_count(&self) -> u16 {
let mut buf = vec![0u16; 64];
let count = unsafe {
ffi::TopologyGetChildren(self.master_index, self.slave_index, buf.as_mut_ptr(), 64)
};
if count > 0 { count as u16 } else { 0 }
}
pub fn children(&self) -> Vec<u16> {
let mut buf = vec![0u16; 64];
let count = unsafe {
ffi::TopologyGetChildren(self.master_index, self.slave_index, buf.as_mut_ptr(), 64)
};
if count > 0 { buf.truncate(count as usize); buf }
else { Vec::new() }
}
pub fn sync_managers(&self) -> Vec<u8> {
self.read_slave().map(|s| s.sm_buffer.to_vec()).unwrap_or_default()
}
pub fn sync_manager_types(&self) -> Vec<u8> {
self.read_slave().map(|s| s.sm_fmmu.sm_type.to_vec()).unwrap_or_default()
}
pub fn fmmus(&self) -> Vec<u8> {
self.read_slave().map(|s| s.fmmu_buffer.to_vec()).unwrap_or_default()
}
pub fn fmmus_raw(&self) -> Vec<u8> {
self.read_register(0x0600, 256).unwrap_or_default()
}
pub fn sync_managers_raw(&self) -> Vec<u8> {
self.read_register(0x0800, 128).unwrap_or_default()
}
pub fn refresh_pointer(&self) {
}
pub fn cia401(&self) -> crate::slave::cia401::CiA401 {
crate::slave::cia401::CiA401::new(*self)
}
pub fn esi(&self) -> crate::slave::esi::EsiLoader {
crate::slave::esi::EsiLoader::new(*self)
}
pub fn events(&self) -> crate::master::events::SlaveEvents {
crate::master::events::SlaveEvents::new(self.master_index, self.slave_index)
}
pub fn voe(&self) -> crate::slave::voe::VoEInstance {
crate::slave::voe::VoEInstance::new(self.master_index, self.slave_index)
}
pub fn aoe(&self) -> crate::slave::aoe::AoEInstance {
crate::slave::aoe::AoEInstance::new(self.master_index, self.slave_index)
}
pub fn soe(&self) -> crate::slave::soe::SoEInstance {
crate::slave::soe::SoEInstance::new(self.master_index, self.slave_index, 0)
}
pub fn soe_drive(&self, drive_number: u8) -> crate::slave::soe::SoEInstance {
crate::slave::soe::SoEInstance::new(self.master_index, self.slave_index, drive_number)
}
pub fn eoe(&self) -> crate::slave::eoe::EoEInstance {
crate::slave::eoe::EoEInstance::new(self.master_index, self.slave_index, 0)
}
pub fn foe(&self) -> crate::slave::foe::FoEInstance {
crate::slave::foe::FoEInstance::new(self.master_index, self.slave_index)
}
pub fn startup(&self) -> crate::slave::startup::StartupParameterList {
crate::slave::startup::StartupParameterList::new(self.master_index, self.slave_index)
}
pub fn dc(&self) -> crate::slave::dc::SlaveDC {
crate::slave::dc::SlaveDC::new(self.master_index, self.slave_index)
}
pub fn diagnostics(&self) -> crate::slave::slave_stats::SlaveDiagnostics {
crate::slave::slave_stats::SlaveDiagnostics::new(self.master_index, self.slave_index)
}
pub fn topology_info(&self) -> crate::slave::topology::SlaveTopology {
crate::slave::topology::SlaveTopology::build(self.master_index)
}
pub fn configure_from_esi(&self) -> Result<()> {
let ret = unsafe {
ffi::DarraCoreInvoke(
self.master_index,
ffi::CORE_OP_25,
self.slave_index as u32,
0,
0)
};
if ret >= 0 {
Ok(())
} else {
Err(DarraError::Other(obfstr::obfstr!("ESI 自动配置失败").into()))
}
}
pub fn config_by_esi(&self) -> Result<()> {
self.configure_from_esi()?;
Ok(())
}
#[doc(hidden)]
pub fn voe_instance(&self) -> crate::slave::voe::VoEInstance { self.voe() }
#[doc(hidden)]
pub fn aoe_instance(&self) -> crate::slave::aoe::AoEInstance { self.aoe() }
#[doc(hidden)]
pub fn startup_params(&self) -> crate::slave::startup::StartupParameterList { self.startup() }
}
impl std::fmt::Display for Slave {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = self.name();
let vid = self.vendor_id();
let pid = self.product_id();
write!(f, "Slave[{}] {} ({:#010x}:{:#010x})", self.slave_index, name, vid, pid)
}
}