use crate::utils::ffi;
pub struct SlavePdo {
master_index: u16,
slave_index: u16,
}
impl SlavePdo {
pub fn new(master_index: u16, slave_index: u16) -> Self {
Self {
master_index,
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_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) -> bool {
unsafe { ffi::PDOWriteOutputU8(self.master_index, self.slave_index, offset, value) != 0 }
}
pub fn write_output_i16(&self, offset: u32, value: i16) -> bool {
unsafe { ffi::PDOWriteOutputI16(self.master_index, self.slave_index, offset, value) != 0 }
}
pub fn write_output_u16(&self, offset: u32, value: u16) -> bool {
unsafe { ffi::PDOWriteOutputU16(self.master_index, self.slave_index, offset, value) != 0 }
}
pub fn write_output_i32(&self, offset: u32, value: i32) -> bool {
unsafe { ffi::PDOWriteOutputI32(self.master_index, self.slave_index, offset, value) != 0 }
}
pub fn write_output_u32(&self, offset: u32, value: u32) -> bool {
unsafe { ffi::PDOWriteOutputU32(self.master_index, self.slave_index, offset, value) != 0 }
}
pub fn write_output_f32(&self, offset: u32, value: f32) -> bool {
unsafe { ffi::PDOWriteOutputF32(self.master_index, self.slave_index, offset, value) != 0 }
}
pub fn stats(&self) -> Option<*const std::os::raw::c_void> {
unsafe {
let ptr = ffi::GetPDOStats(self.master_index, self.slave_index);
if ptr.is_null() { None } else { Some(ptr) }
}
}
pub fn reset_stats(&self) {
unsafe { ffi::ResetPDOStats(self.master_index, self.slave_index); }
}
pub fn get_input_data_pointer(&self) -> *mut u8 {
super::pdo::get_input_data_pointer(self.master_index, self.slave_index)
}
pub fn get_output_data_pointer(&self) -> *mut u8 {
super::pdo::get_output_data_pointer(self.master_index, self.slave_index)
}
pub fn inputs(&self) -> Vec<u8> {
let (ptr, size) = self.inputs_mapping_info();
if ptr.is_null() || size == 0 {
return Vec::new();
}
let mut buf = vec![0u8; size];
unsafe { std::ptr::copy_nonoverlapping(ptr, buf.as_mut_ptr(), size); }
buf
}
pub fn outputs(&self) -> Vec<u8> {
let (ptr, size) = self.outputs_mapping_info();
if ptr.is_null() || size == 0 {
return Vec::new();
}
let mut buf = vec![0u8; size];
unsafe { std::ptr::copy_nonoverlapping(ptr, buf.as_mut_ptr(), size); }
buf
}
pub fn write_outputs(&self, data: &[u8]) {
let (ptr, size) = self.outputs_mapping_info();
if ptr.is_null() || size == 0 { return; }
let len = data.len().min(size);
unsafe { std::ptr::copy_nonoverlapping(data.as_ptr(), ptr, len); }
}
pub fn read_input_data_direct(&self, buffer: &mut [u8], offset: usize, length: usize) -> usize {
super::pdo::read_input_data_direct(self.master_index, self.slave_index, buffer, offset, length)
}
pub fn write_output_data_direct(&self, data: &[u8], offset: usize, length: usize) -> usize {
super::pdo::write_output_data_direct(self.master_index, self.slave_index, data, offset, length)
}
pub unsafe fn input_slice(&self) -> &[u8] {
super::pdo::input_slice(self.master_index, self.slave_index)
}
pub unsafe fn output_slice(&self) -> &mut [u8] {
super::pdo::output_slice(self.master_index, self.slave_index)
}
pub fn inputs_mapping_info(&self) -> (*mut u8, usize) {
let mut out_size: i32 = 0;
let mut out_ptr: *mut u8 = std::ptr::null_mut();
let mut in_size: i32 = 0;
let mut in_ptr: *mut u8 = std::ptr::null_mut();
unsafe {
ffi::GetIO(self.master_index, self.slave_index,
&mut out_size, &mut out_ptr,
&mut in_size, &mut in_ptr);
}
(in_ptr, in_size.max(0) as usize)
}
pub fn outputs_mapping_info(&self) -> (*mut u8, usize) {
let mut out_size: i32 = 0;
let mut out_ptr: *mut u8 = std::ptr::null_mut();
let mut in_size: i32 = 0;
let mut in_ptr: *mut u8 = std::ptr::null_mut();
unsafe {
ffi::GetIO(self.master_index, self.slave_index,
&mut out_size, &mut out_ptr,
&mut in_size, &mut in_ptr);
}
(out_ptr, out_size.max(0) as usize)
}
pub unsafe fn inputs_slice_mapping(&self, offset: usize, size: usize) -> &[u8] {
let (ptr, total) = self.inputs_mapping_info();
if ptr.is_null() || offset + size > total {
return &[];
}
std::slice::from_raw_parts(ptr.add(offset), size)
}
pub unsafe fn outputs_slice_mapping(&self, offset: usize, size: usize) -> &mut [u8] {
let (ptr, total) = self.outputs_mapping_info();
if ptr.is_null() || offset + size > total {
return &mut [];
}
std::slice::from_raw_parts_mut(ptr.add(offset), size)
}
pub unsafe fn bind_pdo_struct<T>(&self, is_input: bool) -> Option<*mut T> {
let (ptr, size) = if is_input {
self.inputs_mapping_info()
} else {
self.outputs_mapping_info()
};
if ptr.is_null() || size < std::mem::size_of::<T>() {
return None;
}
Some(ptr as *mut T)
}
pub fn start_monitoring(&self) {
unsafe { ffi::EnablePDOMonitoring(1); }
}
pub fn stop_monitoring(&self) {
unsafe { ffi::EnablePDOMonitoring(0); }
}
pub fn is_monitoring_enabled(&self) -> bool {
unsafe { ffi::IsPDOMonitoringEnabled() != 0 }
}
pub fn get_pdo_mapping(&self, sm_index: u16) -> Option<(Vec<u8>, u32)> {
let mut buffer = vec![0u8; 512];
let mut mapping_count: u32 = 0;
let ret = unsafe {
ffi::GetPDOMapping(
self.master_index, self.slave_index, sm_index,
buffer.as_mut_ptr() as *mut std::os::raw::c_void,
buffer.len() as u32, &mut mapping_count,
)
};
if ret != 0 && mapping_count > 0 {
buffer.truncate((mapping_count * 8) as usize); Some((buffer, mapping_count))
} else {
None
}
}
pub fn lock_iomap(&self) {
unsafe { ffi::LockIOmap(self.master_index); }
}
pub fn unlock_iomap(&self) {
unsafe { ffi::UnlockIOmap(self.master_index); }
}
pub fn set_mutex_protection(&self, enabled: bool) {
unsafe { ffi::SetMutexProtection(self.master_index, if enabled { 1 } else { 0 }); }
}
pub fn get_mutex_protection(&self) -> bool {
unsafe { ffi::GetMutexProtection(self.master_index) != 0 }
}
pub fn read_direct(&self, pdo_index: u16, size: u32) -> Option<Vec<u8>> {
let mut buffer = vec![0u8; size as usize];
let mut bytes_read: u32 = 0;
let ret = unsafe {
ffi::PDOReadDirect(
self.master_index, self.slave_index, pdo_index,
buffer.as_mut_ptr() as *mut std::os::raw::c_void,
size, &mut bytes_read,
)
};
if ret != 0 && bytes_read > 0 {
buffer.truncate(bytes_read as usize);
Some(buffer)
} else {
None
}
}
pub fn write_direct(&self, pdo_index: u16, data: &[u8]) -> bool {
let ret = unsafe {
ffi::PDOWriteDirect(
self.master_index, self.slave_index, pdo_index,
data.as_ptr() as *const std::os::raw::c_void,
data.len() as u32,
)
};
ret != 0
}
pub fn read_string(&self, offset: u32, max_len: u32) -> String {
let mut buf = Vec::with_capacity(max_len as usize);
for i in 0..max_len {
let b = unsafe { ffi::PDOReadInputU8(self.master_index, self.slave_index, offset + i) };
if b == 0 { break; }
buf.push(b);
}
crate::utils::help::decode_ethercat_string(&buf)
}
pub fn write_string(&self, offset: u32, value: &str, max_len: u32) -> bool {
let bytes = value.as_bytes();
let write_len = bytes.len().min(max_len as usize);
for i in 0..write_len {
let ret = unsafe {
ffi::PDOWriteOutputU8(self.master_index, self.slave_index, offset + i as u32, bytes[i])
};
if ret == 0 { return false; }
}
if write_len < max_len as usize {
unsafe {
ffi::PDOWriteOutputU8(self.master_index, self.slave_index, offset + write_len as u32, 0);
};
}
true
}
pub fn read_input_i64(&self, offset: u32) -> i64 {
let low = unsafe { ffi::PDOReadInputU32(self.master_index, self.slave_index, offset) };
let high = unsafe { ffi::PDOReadInputU32(self.master_index, self.slave_index, offset + 4) };
((high as i64) << 32) | (low as i64)
}
pub fn write_output_i64(&self, offset: u32, value: i64) -> bool {
let low = value as u32;
let high = (value >> 32) as u32;
unsafe {
ffi::PDOWriteOutputU32(self.master_index, self.slave_index, offset, low);
ffi::PDOWriteOutputU32(self.master_index, self.slave_index, offset + 4, high);
}
true
}
pub fn group_cycle_divider(&self, group: u8) -> u8 {
unsafe { ffi::GetGroupCycleDivider(self.master_index, group) }
}
pub fn set_group_cycle_divider(&self, group: u8, divider: u8) -> bool {
unsafe { ffi::SetGroupCycleDivider(self.master_index, group, divider) != 0 }
}
pub fn frame_loss_stats(&self, group: u8) -> (u32, u32, u32) {
let mut total_lost: u32 = 0;
let mut consecutive_lost: u32 = 0;
let mut max_consecutive_lost: u32 = 0;
unsafe {
ffi::GetPDOFrameLossStats(
self.master_index, group,
&mut total_lost, &mut consecutive_lost, &mut max_consecutive_lost,
);
}
(total_lost, consecutive_lost, max_consecutive_lost)
}
pub fn reset_frame_loss_stats(&self, group: u8) {
unsafe { ffi::ResetPDOFrameLossStats(self.master_index, group); }
}
}