1#![cfg_attr(not(debug_assertions), deny(warnings))]
4#![deny(rust_2018_idioms)]
5#![deny(rust_2021_compatibility)]
6#![deny(missing_debug_implementations)]
7#![deny(rustdoc::broken_intra_doc_links)]
8#![deny(clippy::all)]
9#![deny(clippy::explicit_deref_methods)]
10#![deny(clippy::explicit_into_iter_loop)]
11#![deny(clippy::explicit_iter_loop)]
12#![deny(clippy::must_use_candidate)]
13#![cfg_attr(not(test), deny(clippy::panic_in_result_fn))]
14#![cfg_attr(not(debug_assertions), deny(clippy::used_underscore_binding))]
15
16use ethercat_soem_sys as sys;
17use std::{
18 ffi::{CStr, CString},
19 mem::{self, zeroed},
20 os::raw::c_void,
21 time::Duration,
22};
23
24mod error;
25mod group;
26mod od_list;
27mod oe_list;
28mod slave;
29mod sm;
30
31pub use crate::{error::*, group::*, od_list::*, oe_list::*, slave::*};
32
33const EC_MAX_GROUP: usize = 2;
34const EC_MAX_SLAVE: usize = 200;
35
36const EC_MAX_EEP_BITMAP: usize = 128;
38
39const EC_MAX_EEP_BUF: usize = EC_MAX_EEP_BITMAP << 5;
41
42#[allow(missing_debug_implementations)]
44pub struct Ctx {
45 #[allow(dead_code)]
46 port: Box<sys::ecx_portt>,
47 slave_list: Box<[Slave; EC_MAX_SLAVE]>,
48 slave_count: Box<i32>,
49 group_list: Box<[Group; EC_MAX_GROUP]>,
50 #[allow(dead_code)]
51 esi_buf: Box<[u8; EC_MAX_EEP_BUF]>,
52 #[allow(dead_code)]
53 esi_map: Box<[u32; EC_MAX_EEP_BITMAP]>,
54 #[allow(dead_code)]
55 e_list: Box<[u32; 456]>,
56 #[allow(dead_code)]
57 idx_stack: Box<[u64; 27]>,
58 #[allow(dead_code)]
59 ecat_error: Box<u8>,
60 dc_time: Box<i64>,
61 #[allow(dead_code)]
62 sm_comm_type: Box<[u8; 10]>,
63 #[allow(dead_code)]
64 pdo_assign: Box<[u8; 514]>,
65 #[allow(dead_code)]
66 pdo_desc: Box<[u8; 1026]>,
67 #[allow(dead_code)]
68 eep_sm: Box<[u16; 6]>,
69 #[allow(dead_code)]
70 eep_fmmu: Box<[u16; 4]>,
71
72 ecx_ctx: sys::ecx_context,
74
75 pub io_map: [u8; 4096],
77}
78
79impl Default for Ctx {
80 fn default() -> Self {
81 let mut port = Box::new(sys::ecx_portt {
82 _bindgen_opaque_blob: [0; 6502],
83 });
84 let mut slave_list: Box<[Slave; EC_MAX_SLAVE]> = Box::new(unsafe { zeroed() });
85 let mut slave_count = Box::new(0);
86 let mut group_list: Box<[Group; EC_MAX_GROUP]> = Box::new(unsafe { zeroed() });
87 let mut esi_buf: Box<[u8; EC_MAX_EEP_BUF]> = Box::new([0; EC_MAX_EEP_BUF]);
88 let mut esi_map: Box<[u32; EC_MAX_EEP_BITMAP]> = Box::new([0; EC_MAX_EEP_BITMAP]);
89 let mut e_list: Box<[u32; 456]> = Box::new([0; 456]);
90 let mut idx_stack: Box<[u64; 27]> = Box::new([0; 27]);
91 let mut ecat_error: Box<u8> = Box::new(0);
92 let mut dc_time = Box::new(0);
93 let mut sm_comm_type: Box<[u8; 10]> = Box::new([0; 10]);
94 let mut pdo_assign: Box<[u8; 514]> = Box::new([0; 514]);
95 let mut pdo_desc: Box<[u8; 1026]> = Box::new([0; 1026]);
96 let mut eep_sm: Box<[u16; 6]> = Box::new([0; 6]);
97 let mut eep_fmmu: Box<[u16; 4]> = Box::new([0; 4]);
98 let io_map = [0; 4096];
99
100 let ecx_ctx = sys::ecx_context {
102 port: &mut *port,
103 slavelist: slave_list.as_mut_ptr() as *mut sys::ec_slave,
104 slavecount: &mut *slave_count,
105 maxslave: EC_MAX_SLAVE as i32,
106 grouplist: group_list.as_mut_ptr() as *mut sys::ec_group,
107 maxgroup: EC_MAX_GROUP as i32,
108 esibuf: esi_buf.as_mut_ptr(),
109 esimap: esi_map.as_mut_ptr(),
110 esislave: 0,
111 elist: &mut *e_list,
112 idxstack: &mut *idx_stack,
113 ecaterror: &mut *ecat_error,
114 DCtime: &mut *dc_time,
115 SMcommtype: &mut *sm_comm_type,
116 PDOassign: &mut *pdo_assign,
117 PDOdesc: &mut *pdo_desc,
118 eepSM: &mut *eep_sm,
119 eepFMMU: &mut *eep_fmmu,
120 FOEhook: None,
121 EOEhook: None,
122 manualstatechange: 0,
123 userdata: std::ptr::null_mut(),
124 };
125
126 Self {
127 port,
128 slave_list,
129 slave_count,
130 group_list,
131 esi_buf,
132 esi_map,
133 e_list,
134 idx_stack,
135 ecat_error,
136 dc_time,
137 sm_comm_type,
138 pdo_assign,
139 pdo_desc,
140 eep_sm,
141 eep_fmmu,
142 ecx_ctx,
143 io_map,
144 }
145 }
146}
147
148impl Ctx {
149 pub fn init(&mut self, iface: CString) -> i32 {
153 unsafe { sys::ecx_init(&mut self.ecx_ctx, iface.as_ptr()) }
154 }
155 pub fn config_init(&mut self, use_table: bool) -> i32 {
156 unsafe { sys::ecx_config_init(&mut self.ecx_ctx, if use_table { 1 } else { 0 }) }
157 }
158 pub fn config_map_group(&mut self, group: u8) -> i32 {
159 unsafe {
160 sys::ecx_config_map_group(
161 &mut self.ecx_ctx,
162 self.io_map.as_mut_ptr() as *mut std::ffi::c_void,
163 group,
164 )
165 }
166 }
167 pub fn config_dc(&mut self) -> u8 {
168 unsafe { sys::ecx_configdc(&mut self.ecx_ctx) }
169 }
170 pub const fn slave_count(&self) -> usize {
171 *self.slave_count as usize
172 }
173 pub fn slaves(&self) -> &[Slave; EC_MAX_SLAVE] {
174 &self.slave_list
175 }
176 pub fn slaves_mut(&mut self) -> &mut [Slave; EC_MAX_SLAVE] {
177 &mut self.slave_list
178 }
179 pub const fn groups(&self) -> &[Group; EC_MAX_GROUP] {
180 &self.group_list
181 }
182 pub fn write_state(&mut self, slave: u16) -> i32 {
187 unsafe { sys::ecx_writestate(&mut self.ecx_ctx, slave) }
188 }
189 pub fn state_check(&mut self, slave: u16, state: u16, timeout: Duration) -> u16 {
198 unsafe { sys::ecx_statecheck(&mut self.ecx_ctx, slave, state, timeout.as_micros() as i32) }
199 }
200 pub fn read_state(&mut self) -> i32 {
204 unsafe { sys::ecx_readstate(&mut self.ecx_ctx) }
205 }
206 pub fn send_processdata(&mut self) -> i32 {
207 unsafe { sys::ecx_send_processdata(&mut self.ecx_ctx) }
208 }
209 pub fn receive_processdata(&mut self, timeout: Duration) -> i32 {
210 unsafe { sys::ecx_receive_processdata(&mut self.ecx_ctx, timeout.as_micros() as i32) }
211 }
212 pub fn read_od_list(&mut self, slave: u16, od_list: &mut OdList) -> i32 {
213 unsafe { sys::ecx_readODlist(&mut self.ecx_ctx, slave, &mut od_list.0) }
214 }
215 pub fn read_od_description(&mut self, item: u16, od_list: &mut OdList) -> i32 {
216 unsafe { sys::ecx_readODdescription(&mut self.ecx_ctx, item, &mut od_list.0) }
217 }
218 pub fn read_oe(&mut self, item: u16, od_list: &mut OdList, oe_list: &mut OeList) -> i32 {
219 unsafe { sys::ecx_readOE(&mut self.ecx_ctx, item, &mut od_list.0, &mut oe_list.0) }
220 }
221 pub fn sdo_read<'t>(
222 &mut self,
223 slave: u16,
224 idx: u16,
225 sub_idx: u8,
226 access_complete: bool,
227 target: &'t mut [u8],
228 timeout: Duration,
229 ) -> (i32, &'t mut [u8]) {
230 let mut size = mem::size_of_val(target) as i32;
231 let timeout = timeout.as_micros() as i32; let wkc = unsafe {
233 sys::ecx_SDOread(
234 &mut self.ecx_ctx,
235 slave,
236 idx,
237 sub_idx,
238 if access_complete { 1 } else { 0 },
239 &mut size,
240 target.as_mut_ptr() as *mut c_void,
241 timeout,
242 )
243 };
244 if wkc <= 0 {
245 (wkc, target)
246 } else {
247 (wkc, &mut target[..size as usize])
248 }
249 }
250 pub fn sdo_write(
251 &mut self,
252 slave: u16,
253 idx: u16,
254 sub_idx: u8,
255 access_complete: bool,
256 data: &[u8],
257 timeout: Duration,
258 ) -> i32 {
259 let size = mem::size_of_val(data) as i32;
260 let timeout = timeout.as_micros() as i32; unsafe {
262 sys::ecx_SDOwrite(
263 &mut self.ecx_ctx,
264 slave,
265 idx,
266 sub_idx,
267 if access_complete { 1 } else { 0 },
268 size,
269 data.as_ptr() as *mut c_void,
270 timeout,
271 )
272 }
273 }
274 pub const fn max_group(&self) -> i32 {
275 self.ecx_ctx.maxgroup
276 }
277 pub fn is_err(&mut self) -> bool {
278 unsafe { sys::ecx_iserror(&mut self.ecx_ctx) != 0 }
279 }
280 pub fn pop_error(&mut self) -> Option<Error> {
281 let mut ec: sys::ec_errort = unsafe { zeroed() };
282 if unsafe { sys::ecx_poperror(&mut self.ecx_ctx, &mut ec) } != 0 {
283 Some(Error::from(ec))
284 } else {
285 None
286 }
287 }
288 pub const fn dc_time(&self) -> i64 {
289 *self.dc_time
290 }
291}
292
293fn c_array_to_string(data: *const i8) -> String {
294 unsafe { CStr::from_ptr(data).to_string_lossy().into_owned() }
295}
296
297#[cfg(test)]
298mod tests {
299
300 use super::*;
301
302 #[test]
303 fn context_wrapper() {
304 let mut wrapper = Ctx::default();
305 wrapper.port._bindgen_opaque_blob[3] = 5;
306 assert_eq!(
307 unsafe { (*wrapper.ecx_ctx.port)._bindgen_opaque_blob[3] },
308 5
309 );
310 assert_eq!(wrapper.slave_list.len(), 200);
311 assert_eq!(wrapper.slave_list[7].0.ALstatuscode, 0);
312 wrapper.slave_list[7].0.ALstatuscode = 33;
313 assert_eq!(
314 unsafe {
315 std::slice::from_raw_parts_mut(wrapper.ecx_ctx.slavelist, 200)[7].ALstatuscode
316 },
317 33
318 );
319 }
320}