use ethercat_soem_sys as sys;
use std::{
ffi::{CStr, CString},
mem::{self, zeroed},
os::raw::c_void,
time::Duration,
};
mod error;
mod group;
mod od_list;
mod oe_list;
mod slave;
mod sm;
pub use crate::{error::*, group::*, od_list::*, oe_list::*, slave::*};
const EC_MAX_GROUP: usize = 2;
const EC_MAX_SLAVE: usize = 200;
const EC_MAX_EEP_BITMAP: usize = 128;
const EC_MAX_EEP_BUF: usize = EC_MAX_EEP_BITMAP << 5;
pub struct Ctx {
#[allow(dead_code)]
port: Box<sys::ecx_portt>,
slave_list: Box<[Slave; EC_MAX_SLAVE]>,
slave_count: Box<i32>,
group_list: Box<[Group; EC_MAX_GROUP]>,
#[allow(dead_code)]
esi_buf: Box<[u8; EC_MAX_EEP_BUF]>,
#[allow(dead_code)]
esi_map: Box<[u32; EC_MAX_EEP_BITMAP]>,
#[allow(dead_code)]
e_list: Box<[u32; 456]>,
#[allow(dead_code)]
idx_stack: Box<[u64; 27]>,
#[allow(dead_code)]
ecat_error: Box<u8>,
dc_time: Box<i64>,
#[allow(dead_code)]
sm_comm_type: Box<[u8; 10]>,
#[allow(dead_code)]
pdo_assign: Box<[u8; 514]>,
#[allow(dead_code)]
pdo_desc: Box<[u8; 1026]>,
#[allow(dead_code)]
eep_sm: Box<[u16; 6]>,
#[allow(dead_code)]
eep_fmmu: Box<[u16; 4]>,
ecx_ctx: sys::ecx_context,
pub io_map: [u8; 4096],
}
impl Default for Ctx {
fn default() -> Self {
let mut port = Box::new(sys::ecx_portt {
_bindgen_opaque_blob: [0; 6502],
});
let mut slave_list: Box<[Slave; EC_MAX_SLAVE]> = Box::new(unsafe { zeroed() });
let mut slave_count = Box::new(0);
let mut group_list: Box<[Group; EC_MAX_GROUP]> = Box::new(unsafe { zeroed() });
let mut esi_buf: Box<[u8; EC_MAX_EEP_BUF]> = Box::new([0; EC_MAX_EEP_BUF]);
let mut esi_map: Box<[u32; EC_MAX_EEP_BITMAP]> = Box::new([0; EC_MAX_EEP_BITMAP]);
let mut e_list: Box<[u32; 456]> = Box::new([0; 456]);
let mut idx_stack: Box<[u64; 27]> = Box::new([0; 27]);
let mut ecat_error: Box<u8> = Box::new(0);
let mut dc_time = Box::new(0);
let mut sm_comm_type: Box<[u8; 10]> = Box::new([0; 10]);
let mut pdo_assign: Box<[u8; 514]> = Box::new([0; 514]);
let mut pdo_desc: Box<[u8; 1026]> = Box::new([0; 1026]);
let mut eep_sm: Box<[u16; 6]> = Box::new([0; 6]);
let mut eep_fmmu: Box<[u16; 4]> = Box::new([0; 4]);
let io_map = [0; 4096];
let ecx_ctx = sys::ecx_context {
port: &mut *port,
slavelist: slave_list.as_mut_ptr() as *mut sys::ec_slave,
slavecount: &mut *slave_count,
maxslave: EC_MAX_SLAVE as i32,
grouplist: group_list.as_mut_ptr() as *mut sys::ec_group,
maxgroup: EC_MAX_GROUP as i32,
esibuf: esi_buf.as_mut_ptr(),
esimap: esi_map.as_mut_ptr(),
esislave: 0,
elist: &mut *e_list,
idxstack: &mut *idx_stack,
ecaterror: &mut *ecat_error,
DCtime: &mut *dc_time,
SMcommtype: &mut *sm_comm_type,
PDOassign: &mut *pdo_assign,
PDOdesc: &mut *pdo_desc,
eepSM: &mut *eep_sm,
eepFMMU: &mut *eep_fmmu,
FOEhook: None,
EOEhook: None,
manualstatechange: 0,
};
Self {
port,
slave_list,
slave_count,
group_list,
esi_buf,
esi_map,
e_list,
idx_stack,
ecat_error,
dc_time,
sm_comm_type,
pdo_assign,
pdo_desc,
eep_sm,
eep_fmmu,
ecx_ctx,
io_map,
}
}
}
impl Ctx {
pub fn init(&mut self, iface: CString) -> i32 {
unsafe { sys::ecx_init(&mut self.ecx_ctx, iface.as_ptr()) }
}
pub fn config_init(&mut self, use_table: bool) -> i32 {
unsafe { sys::ecx_config_init(&mut self.ecx_ctx, if use_table { 1 } else { 0 }) }
}
pub fn config_map_group(&mut self, group: u8) -> i32 {
unsafe {
sys::ecx_config_map_group(
&mut self.ecx_ctx,
self.io_map.as_mut_ptr() as *mut std::ffi::c_void,
group,
)
}
}
pub fn config_dc(&mut self) -> u8 {
unsafe { sys::ecx_configdc(&mut self.ecx_ctx) }
}
pub const fn slave_count(&self) -> usize {
*self.slave_count as usize
}
pub fn slaves(&self) -> &[Slave; EC_MAX_SLAVE] {
&self.slave_list
}
pub fn slaves_mut(&mut self) -> &mut [Slave; EC_MAX_SLAVE] {
&mut self.slave_list
}
pub const fn groups(&self) -> &[Group; EC_MAX_GROUP] {
&self.group_list
}
pub fn write_state(&mut self, slave: u16) -> i32 {
unsafe { sys::ecx_writestate(&mut self.ecx_ctx, slave) }
}
pub fn state_check(&mut self, slave: u16, state: u16, timeout: Duration) -> u16 {
unsafe { sys::ecx_statecheck(&mut self.ecx_ctx, slave, state, timeout.as_micros() as i32) }
}
pub fn read_state(&mut self) -> i32 {
unsafe { sys::ecx_readstate(&mut self.ecx_ctx) }
}
pub fn send_processdata(&mut self) -> i32 {
unsafe { sys::ecx_send_processdata(&mut self.ecx_ctx) }
}
pub fn receive_processdata(&mut self, timeout: Duration) -> i32 {
unsafe { sys::ecx_receive_processdata(&mut self.ecx_ctx, timeout.as_micros() as i32) }
}
pub fn read_od_list(&mut self, slave: u16, od_list: &mut OdList) -> i32 {
unsafe { sys::ecx_readODlist(&mut self.ecx_ctx, slave, &mut od_list.0) }
}
pub fn read_od_description(&mut self, item: u16, od_list: &mut OdList) -> i32 {
unsafe { sys::ecx_readODdescription(&mut self.ecx_ctx, item, &mut od_list.0) }
}
pub fn read_oe(&mut self, item: u16, od_list: &mut OdList, oe_list: &mut OeList) -> i32 {
unsafe { sys::ecx_readOE(&mut self.ecx_ctx, item, &mut od_list.0, &mut oe_list.0) }
}
pub fn sdo_read<'t>(
&mut self,
slave: u16,
idx: u16,
sub_idx: u8,
access_complete: bool,
target: &'t mut [u8],
timeout: Duration,
) -> (i32, &'t mut [u8]) {
let mut size = mem::size_of_val(target) as i32;
let timeout = timeout.as_micros() as i32;
let wkc = unsafe {
sys::ecx_SDOread(
&mut self.ecx_ctx,
slave,
idx,
sub_idx,
if access_complete { 1 } else { 0 },
&mut size,
target.as_mut_ptr() as *mut c_void,
timeout,
)
};
if wkc <= 0 {
(wkc, target)
} else {
(wkc, &mut target[..size as usize])
}
}
pub fn sdo_write(
&mut self,
slave: u16,
idx: u16,
sub_idx: u8,
access_complete: bool,
data: &[u8],
timeout: Duration,
) -> i32 {
let size = mem::size_of_val(data) as i32;
let timeout = timeout.as_micros() as i32;
unsafe {
sys::ecx_SDOwrite(
&mut self.ecx_ctx,
slave,
idx,
sub_idx,
if access_complete { 1 } else { 0 },
size,
data.as_ptr() as *mut c_void,
timeout,
)
}
}
pub const fn max_group(&self) -> i32 {
self.ecx_ctx.maxgroup
}
pub fn is_err(&mut self) -> bool {
unsafe { sys::ecx_iserror(&mut self.ecx_ctx) != 0 }
}
pub fn pop_error(&mut self) -> Option<Error> {
let mut ec: sys::ec_errort = unsafe { zeroed() };
if unsafe { sys::ecx_poperror(&mut self.ecx_ctx, &mut ec) } != 0 {
Some(Error::from(ec))
} else {
None
}
}
pub const fn dc_time(&self) -> i64 {
*self.dc_time
}
}
fn c_array_to_string(data: *const i8) -> String {
unsafe { CStr::from_ptr(data).to_string_lossy().into_owned() }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn context_wrapper() {
let mut wrapper = Ctx::default();
wrapper.port._bindgen_opaque_blob[3] = 5;
assert_eq!(
unsafe { (*wrapper.ecx_ctx.port)._bindgen_opaque_blob[3] },
5
);
assert_eq!(wrapper.slave_list.len(), 200);
assert_eq!(wrapper.slave_list[7].0.ALstatuscode, 0);
wrapper.slave_list[7].0.ALstatuscode = 33;
assert_eq!(
unsafe {
std::slice::from_raw_parts_mut(wrapper.ecx_ctx.slavelist, 200)[7].ALstatuscode
},
33
);
}
}