1use std::{
2 collections::HashMap,
3 fs::File,
4 io::{self, Read},
5 ops::Range,
6 sync::{Arc, Condvar, Mutex, RwLock},
7 thread,
8 time::Duration,
9};
10
11use ethercat::{
12 AlState, DataType, DomainIdx, Master, MasterAccess, Offset, PdoCfg, PdoEntryIdx, PdoEntryInfo,
13 PdoEntryPos, PdoIdx, PdoPos, SdoData, SdoIdx, SdoPos, SlaveAddr, SlaveId, SlavePos, SmCfg,
14 SmIdx, SmInfo, SubIdx,
15};
16
17use crossbeam_channel::{bounded, Receiver, Sender};
18
19use crate::{watchdog, MailboxPdoEntries, PdoOffsets, SlaveNames, SlaveOffsets, SlaveSetup};
20
21use crate::ethercat_patch::master_configure_sync;
23
24#[cfg(feature = "verify_mailbox_pdos")]
25use crate::mailboxes::{init_mailbox_pdo_verification, verify_mailbox_pdos};
26#[cfg(feature = "enable_watchdog")]
27use crate::watchdog::{init_watchdog_settings, verify_watchdog};
28
29use crate::mailboxes::mailbox_sdo_read;
30
31#[derive(Debug)]
34pub struct EtherCatController {
35 offsets: SlaveOffsets,
36 slave_names: SlaveNames,
37
38 data_lock: Arc<RwLock<Option<Vec<u8>>>>,
39 ready_condvar: Arc<(Mutex<bool>, Condvar)>,
40 cycle_condvar: Arc<(Mutex<bool>, Condvar)>,
41 slave_states_condvar: Arc<(Mutex<Vec<u8>>, Condvar)>,
42
43 cmd_buff: Sender<(Range<usize>, Vec<u8>)>,
44
45 setup_condvar: Arc<(Mutex<SlaveSetup>, Condvar)>,
47
48 pub command_drop_time_us: u32,
49}
50
51impl EtherCatController {
52 pub fn open(
68 master_id: u32,
69 cycle_period: Duration,
70 command_drop_time_us: u32,
71 watchdog_timeout_ms: u32,
72 mailbox_wait_time_ms: u32,
73 ) -> Result<Self, io::Error> {
74 let (mut master, domain_idx, offsets, slave_names, mailbox_pdo_entries) =
75 init_master(master_id)?;
76
77 for slave_id in 0..slave_names.len() {
81 let mut data = vec![0u8; 1];
82 match mailbox_sdo_read(&master, slave_id as u16, 0x201, 0x1, &mut data) {
83 Ok(_) => {
84 log::info!("Slave {}, DXL_ID: {:?}", slave_id, data[0]);
85 }
86 Err(_) => {
87 log::warn!("Slave {}, DXL_ID unknown!", slave_id);
88 }
89 }
90
91 let mut data = vec![0u8; 40];
92 match mailbox_sdo_read(&master, slave_id as u16, 0x200, 0x1, &mut data) {
93 Ok(_) => {
94 log::info!(
95 "Slave {} firmware version: {:?}",
96 slave_id,
97 String::from_utf8(data).unwrap()
98 );
99 }
100 Err(_) => {
101 log::warn!("Slave {}, firmware version unknown!", slave_id);
102 }
103 }
104 }
105
106 master.activate()?;
107
108 log_pdo_offsets(&offsets);
110
111 let data_lock = Arc::new(RwLock::new(None));
114 let write_data_lock = Arc::clone(&data_lock);
115
116 let ready_condvar = Arc::new((Mutex::new(false), Condvar::new()));
118 let write_ready_condvar = Arc::clone(&ready_condvar);
119
120 let cycle_condvar = Arc::new((Mutex::new(false), Condvar::new()));
122 let write_cycle_condvar = Arc::clone(&cycle_condvar);
123
124 let slave_states_condvar = Arc::new((Mutex::new(vec![0]), Condvar::new()));
126 let sstate_condvar = Arc::clone(&slave_states_condvar);
127
128 let mut is_poulpe_setup: SlaveSetup = HashMap::new();
130 for i in 0..slave_names.len() {
131 is_poulpe_setup.insert(SlavePos::from(i as u16), false);
132 }
133 let slave_setup_condvar = Arc::new((Mutex::new(is_poulpe_setup), Condvar::new()));
134 let setup_condvar = Arc::clone(&slave_setup_condvar);
135
136 let slave_number = slave_names.len() as u32;
138 let slave_name_from_id = create_slave_name_mapper(slave_names.clone());
140
141 let buffer_size = (slave_number * 20) as usize;
144 let (tx, rx): (
145 crossbeam_channel::Sender<(Range<usize>, Vec<u8>)>,
146 Receiver<(Range<usize>, Vec<u8>)>,
147 ) = bounded(buffer_size);
148
149 #[cfg(feature = "verify_mailbox_pdos")]
150 let (
152 mut slave_mailbox_pdo_offsets,
153 mut slave_mailbox_pdo_timestamps,
154 mut slave_is_mailbox_pdo_responding,
155 mut slave_mailbox_pdo_data_buffer,
156 ) = init_mailbox_pdo_verification(
157 slave_number,
158 &mailbox_pdo_entries,
159 &offsets,
160 &get_reg_addr_ranges,
161 );
162
163 #[cfg(feature = "enable_watchdog")]
164 let (
166 slave_watchdog_control_offsets,
167 slave_watchdog_status_offsets,
168 mut slave_watchdog_timestamps,
169 mut slave_is_watchdog_responding,
170 mut slave_previous_watchdog_counter,
171 ) = init_watchdog_settings(slave_number, &offsets, &get_reg_addr_ranges);
172
173 let mut watchdog_counter = 0;
174
175 thread::spawn(move || {
176 let mut master_operational = false;
178 let mut display_not_operational_timestamp = std::time::Instant::now();
180 let mut loop_period_timestamp = std::time::Instant::now();
182 let mut debug_loop_timestamp = std::time::Instant::now();
183 let mut debug_loop_counter = 0;
184 loop {
186 let dt_sleep =
190 cycle_period.as_secs_f32() - loop_period_timestamp.elapsed().as_secs_f32();
191 if dt_sleep > 0.0 {
192 thread::sleep(Duration::from_secs_f32(dt_sleep));
193 }
194 loop_period_timestamp = std::time::Instant::now();
196
197 debug_loop_counter += 1;
199 if debug_loop_timestamp.elapsed().as_secs_f32() > 10.0 {
200 log::info!(
201 "EtherCAT loop: {:.02} Hz",
202 debug_loop_counter as f32 / debug_loop_timestamp.elapsed().as_secs_f32()
203 );
204 debug_loop_timestamp = std::time::Instant::now();
205 debug_loop_counter = 0;
206 }
207
208 master.receive().unwrap();
210 master.domain(domain_idx).process().unwrap();
211 master.domain(domain_idx).queue().unwrap();
212
213 let mut data = master.domain_data(domain_idx).unwrap();
215
216 #[cfg(feature = "verify_mailbox_pdos")]
227 let all_slaves_responding = verify_mailbox_pdos(
228 slave_number,
229 &mut data,
230 &mut slave_mailbox_pdo_offsets,
231 &mut slave_mailbox_pdo_timestamps,
232 &mut slave_is_mailbox_pdo_responding,
233 &mut slave_mailbox_pdo_data_buffer,
234 mailbox_wait_time_ms,
235 );
236
237 if let Ok(mut write_guard) = write_data_lock.write() {
239 *write_guard = Some(data.to_vec());
240 }
241
242 notify_next_cycle(&write_cycle_condvar);
244
245 if master_operational {
248 while let Ok((reg_addr_range, value)) = rx.try_recv() {
252 data[reg_addr_range].copy_from_slice(&value);
253 }
254 }
255
256 #[cfg(feature = "enable_watchdog")]
257 let all_slaves_have_watchdog = verify_watchdog(
259 slave_number,
260 &mut data,
261 watchdog_timeout_ms,
262 watchdog_counter,
263 &slave_watchdog_control_offsets,
264 &slave_watchdog_status_offsets,
265 &mut slave_watchdog_timestamps,
266 &mut slave_is_watchdog_responding,
267 &mut slave_previous_watchdog_counter,
268 &slave_name_from_id,
269 );
270 watchdog_counter = (watchdog_counter + 1) % 8;
272
273 master.send().unwrap();
275
276 let m_state = master.state().unwrap();
278 #[cfg(not(feature = "verify_mailbox_pdos"))]
279 let all_slaves_responding = m_state.slaves_responding == slave_number;
281 #[cfg(not(feature = "enable_watchdog"))]
282 let all_slaves_have_watchdog = m_state.slaves_responding == slave_number;
283
284 if !master_operational {
300 if all_slaves_responding
309 && all_slaves_have_watchdog
310 && m_state.link_up
311 && m_state.al_states == AlState::Op as u8 && m_state.slaves_responding == slave_number
313 {
314 set_ready_flag(&write_ready_condvar, true);
316 master_operational = true;
317 notify_slave_state(
319 &sstate_condvar,
320 vec![AlState::Op as u8; slave_number as usize],
321 );
322 log::info!("Master and all slaves operational!");
323 } else {
324 if display_not_operational_timestamp.elapsed().as_secs() > 1 {
326 display_not_operational_timestamp = std::time::Instant::now();
327 log::warn!("Master cannot go to operational!");
328 log_master_state(
331 &master,
332 slave_number,
333 #[cfg(feature = "verify_mailbox_pdos")]
334 mailbox_wait_time_ms,
335 #[cfg(feature = "enable_watchdog")]
336 watchdog_timeout_ms,
337 &slave_name_from_id,
338 #[cfg(feature = "verify_mailbox_pdos")]
339 &slave_is_mailbox_pdo_responding,
340 #[cfg(feature = "enable_watchdog")]
341 &slave_is_watchdog_responding,
342 );
343
344 #[cfg(feature = "stop_opeation_on_error")]
346 std::process::exit(10);
347 }
348 }
349 } else {
350 if m_state.slaves_responding != slave_number {
356 match m_state.slaves_responding {
357 0 => log::error!("No slaves are connected!"),
358 _ if m_state.slaves_responding < slave_number => log::error!(
359 "Not all slaves are connected! Expected: {}, Responding: {}",
360 slave_number,
361 m_state.slaves_responding
362 ),
363 _ if m_state.slaves_responding > slave_number => log::error!(
364 "New slaves are connected! Inintially: {}, Now: {}",
365 slave_number,
366 m_state.slaves_responding
367 ),
368 _ => {}
369 }
370
371 let slave_current_state = (0..slave_number)
373 .map(|i| {
374 get_slave_current_state(
375 &master,
376 SlavePos::from(i as u16),
377 &slave_name_from_id,
378 #[cfg(feature = "verify_mailbox_pdos")]
379 slave_is_mailbox_pdo_responding[i as usize],
380 #[cfg(feature = "enable_watchdog")]
381 slave_is_watchdog_responding[i as usize],
382 )
383 })
384 .collect::<Vec<_>>();
385
386 notify_slave_state(&sstate_condvar, slave_current_state);
388
389 set_ready_flag(&write_ready_condvar, false);
390 master_operational = false;
391 }
392
393 if m_state.al_states != AlState::Op as u8
395 || !all_slaves_responding
396 || !all_slaves_have_watchdog
397 {
398 if m_state.al_states != AlState::Op as u8 {
400 log::error!(
401 "Master is not operational! State: {:?}",
402 m_state.al_states
403 );
404 }
405 if !all_slaves_responding {
406 log::error!("Not all slaves are responding!");
408 }
409 if !all_slaves_have_watchdog {
410 log::error!("Not all slaves have watchdog!");
412 }
413
414 let slave_current_state = (0..slave_number)
419 .map(|i| {
420 get_slave_current_state(
421 &master,
422 SlavePos::from(i as u16),
423 &slave_name_from_id,
424 #[cfg(feature = "verify_mailbox_pdos")]
425 slave_is_mailbox_pdo_responding[i as usize],
426 #[cfg(feature = "enable_watchdog")]
427 slave_is_watchdog_responding[i as usize],
428 )
429 })
430 .collect::<Vec<_>>();
431
432 notify_slave_state(&sstate_condvar, slave_current_state);
434
435 set_ready_flag(&write_ready_condvar, false);
437 master_operational = false;
439 }
440 }
441 }
442 });
443
444 Ok(EtherCatController {
445 offsets,
446 slave_names,
447 data_lock,
448 ready_condvar,
449 cycle_condvar,
450 slave_states_condvar,
451 setup_condvar,
452 cmd_buff: tx,
453 command_drop_time_us,
454 })
455 }
456
457 pub fn get_slave_ids(&self) -> Vec<u16> {
459 let mut ids: Vec<u16> = self
460 .offsets
461 .keys()
462 .map(|slave_pos| u16::from(*slave_pos))
463 .collect();
464 ids.sort();
465 ids
466 }
467
468 pub fn get_pdo_register(
480 &self,
481 slave_id: u16,
482 register: &String,
483 index: usize,
484 ) -> Option<Vec<u8>> {
485 let reg_addr_range = self.get_reg_addr_range(slave_id, register, index);
486
487 (*self.data_lock.read().unwrap())
488 .as_ref()
489 .map(|data| data[reg_addr_range].to_vec())
490 }
491
492 pub fn set_pdo_register(&self, slave_id: u16, register: &String, index: usize, value: Vec<u8>) {
501 let reg_addr_range = self.get_reg_addr_range(slave_id, register, index);
502
503 self.cmd_buff.send((reg_addr_range, value)).unwrap();
504 }
505
506 pub fn get_pdo_registers(&self, slave_id: u16, register: &String) -> Option<Vec<Vec<u8>>> {
517 let reg_addr_ranges = self.get_reg_addr_ranges(slave_id, register);
518
519 let vals = reg_addr_ranges
520 .iter()
521 .map(|reg_addr_range| {
522 (*self.data_lock.read().unwrap())
523 .as_ref()
524 .map(|data| data[reg_addr_range.clone()].to_vec())
525 })
526 .collect::<Option<Vec<Vec<u8>>>>()?;
527 Some(vals)
528 }
529
530 pub fn set_pdo_registers(&self, slave_id: u16, register: &String, values: Vec<Vec<u8>>) {
538 let reg_addr_ranges = self.get_reg_addr_ranges(slave_id, register);
539
540 if values.len() != reg_addr_ranges.len() {
541 log::warn!(
543 "Values length does not match register count, using first {} elements!",
544 reg_addr_ranges.len()
545 );
546 }
547
548 for (reg_addr_range, v) in reg_addr_ranges.iter().zip(values) {
549 self.cmd_buff.send((reg_addr_range.clone(), v)).unwrap();
550 }
551 }
552
553 pub fn wait_for_next_cycle(&self) {
555 let (lock, cvar) = &*self.cycle_condvar;
556 let mut next_cycle = lock.lock().unwrap();
557
558 *next_cycle = false;
559 while !*next_cycle {
560 next_cycle = cvar.wait(next_cycle).unwrap();
561 }
562 }
563
564 pub fn master_operational(self) -> bool {
566 {
567 let (lock, _cvar) = &*self.ready_condvar;
568 let ready = lock.lock().unwrap();
569 return *ready;
570 }
571 }
572
573 pub fn is_slave_ready(&self, slave_id: u16) -> bool {
575 let states = self.get_slave_states();
576 match states.get(slave_id as usize).map(|s| *s) {
577 Some(state) => state == (AlState::Op as u8),
578 None => return false,
579 }
580 }
581
582 pub fn get_slave_states(&self) -> Vec<u8> {
584 {
585 let (lock, _cvar) = &*self.slave_states_condvar;
586 let states = lock.lock().unwrap();
587 states.clone()
588 }
589 }
590
591 pub fn wait_for_ready(self) -> Self {
593 {
594 let (lock, cvar) = &*self.ready_condvar;
595 let mut ready = lock.lock().unwrap();
596
597 *ready = false;
598 while !*ready {
599 ready = cvar.wait(ready).unwrap();
600 }
601 }
602 self
603 }
604
605 pub fn get_slave_setup(&self, slave_id: u16) -> bool {
607 {
608 let (lock, _cvar) = &*self.setup_condvar;
609 let setup = lock.lock().unwrap();
610 *setup.get(&SlavePos::from(slave_id)).unwrap_or(&false)
611 }
612 }
613 pub fn set_slave_setup(&self, slave_id: u16, setup: bool) {
615 {
616 let (lock, cvar) = &*self.setup_condvar;
617 let mut setup_lock = lock.lock().unwrap();
618 *setup_lock.get_mut(&SlavePos::from(slave_id)).unwrap() = setup;
619 }
620 }
621
622 fn get_reg_addr_range(&self, slave_id: u16, register: &String, index: usize) -> Range<usize> {
624 get_reg_addr_range(&self.offsets, slave_id, register, index)
625 }
626
627 fn get_reg_addr_ranges(&self, slave_id: u16, register: &String) -> Vec<Range<usize>> {
629 get_reg_addr_ranges(&self.offsets, slave_id, register)
630 }
631
632 pub fn get_slave_name(&self, slave_id: u16) -> Option<String> {
634 self.slave_names
635 .iter()
636 .find(|(_, id)| u16::from(**id) == slave_id)
637 .map(|(name, _)| name.clone())
638 }
639
640 pub fn get_slave_id(&self, slave_name: &String) -> Option<u16> {
642 self.slave_names.get(slave_name).map(|id| u16::from(*id))
643 }
644
645 pub fn get_slave_ids_and_names(&self) -> Vec<(u16, String)> {
647 self.slave_names
648 .iter()
649 .map(|(name, id)| (u16::from(*id), name.clone()))
650 .collect()
651 }
652}
653
654pub fn get_reg_addr_range(
656 offsets: &SlaveOffsets,
657 slave_id: u16,
658 register: &String,
659 index: usize,
660) -> Range<usize> {
661 let slave_pos = SlavePos::from(slave_id);
662
663 let (_pdo_entry_idx, bit_len, offset) = offsets[&slave_pos][register][index];
664 let addr = offset.byte;
665 let bytes_len = (bit_len / 8) as usize;
666
667 addr..addr + bytes_len
668}
669
670fn get_reg_addr_ranges(
672 offsets: &SlaveOffsets,
673 slave_id: u16,
674 register: &String,
675) -> Vec<Range<usize>> {
676 let slave_pos = SlavePos::from(slave_id);
677
678 let register_data = &offsets[&slave_pos][register];
680
681 let mut ranges = Vec::with_capacity(register_data.len());
682 for i in 0..register_data.len() {
683 ranges.push(get_reg_addr_range(offsets, slave_id, register, i));
684 }
685 ranges
686}
687
688pub fn init_master_for_foe(idx: u32) -> Result<Master, io::Error> {
696 let mut master = match Master::open(idx, MasterAccess::ReadWrite) {
699 Ok(master) => master,
700 Err(_) => {
701 log::error!("Failed to connecitng to master! Is ethercat master started?");
702 return Err(io::Error::new(
703 io::ErrorKind::Other,
704 "Failed to connect to master",
705 ));
706 }
707 };
708 log::debug!("Reserve master");
709 master.reserve()?;
710 log::debug!("Create domain");
711 let domain_idx = master.create_domain()?;
712
713 let slave_num = master.get_info().unwrap().slave_count;
714 log::info!("Found {:?} slaves", slave_num);
715
716 if slave_num == 0 {
718 log::error!("No slaves found, check slave connections!");
719 return Err(io::Error::new(io::ErrorKind::Other, "No slaves found"));
720 }
721
722 for i in 0..slave_num {
723 let slave_info = master.get_slave_info(SlavePos::from(i as u16)).unwrap();
724 log::info!("Slave {:?} at position {:?}", slave_info.name, i);
725 log::debug!("Found device {:?}", slave_info);
726 log::debug!(
727 "Vendor ID: {:X}, Product Code: {:X}, SM count {:?}",
728 slave_info.id.vendor_id,
729 slave_info.id.product_code,
730 slave_info.sync_count
731 );
732 let slave_addr = SlaveAddr::ByPos(i as u16);
733 let slave_id = SlaveId {
734 vendor_id: slave_info.id.vendor_id,
735 product_code: slave_info.id.product_code,
736 };
737
738 for j in 0..slave_info.sync_count {
739 let sm_idx = SmIdx::new(j);
740 let sm_info = master.get_sync(SlavePos::from(i as u16), sm_idx).unwrap();
741
742 if sm_info.pdo_count == 0 {
744 log::debug!("No PDOs found for SM {:?}", sm_idx);
745 }
746
747 if sm_info.control_register & 0b10 != 0 {
750 log::debug!("SM is in mailbox mode!");
751 } else {
752 log::debug!("SM is in buffered mode!");
753 continue;
754 }
755
756 if sm_info.control_register & 0b100 != 0 {
757 log::debug!("Input SM!");
758 } else {
759 log::debug!("Output SM!");
760 }
761
762 master_configure_sync(&mut master, SlavePos::from(i as u16), sm_info);
763 }
764
765 let mut config = master.configure_slave(slave_addr, slave_id)?;
766
767 let cfg_index = config.index();
768
769 let cfg_info = master.get_config_info(cfg_index)?;
770 if cfg_info.slave_position.is_none() {
771 return Err(io::Error::new(
772 io::ErrorKind::Other,
773 "Unable to configure slave",
774 ));
775 }
776 }
777
778 Ok(master)
779}
780
781pub fn init_master(
807 idx: u32,
808) -> Result<
809 (
810 Master,
811 DomainIdx,
812 SlaveOffsets,
813 SlaveNames,
814 MailboxPdoEntries,
815 ),
816 io::Error,
817> {
818 let mut master = match Master::open(idx, MasterAccess::ReadWrite) {
821 Ok(master) => master,
822 Err(_) => {
823 log::error!("Failed to connecitng to master! Is ethercat master started?");
824 return Err(io::Error::new(
825 io::ErrorKind::Other,
826 "Failed to connect to master",
827 ));
828 }
829 };
830 log::debug!("Reserve master");
831 master.reserve()?;
832 log::debug!("Create domain");
833 let domain_idx = master.create_domain()?;
834 let mut offsets: SlaveOffsets = HashMap::new();
835 let mut slave_names: SlaveNames = HashMap::new();
836
837 let mut mailbox_pdos: MailboxPdoEntries = HashMap::new();
838
839 let slave_num = master.get_info().unwrap().slave_count;
840 log::info!("Found {:?} slaves", slave_num);
841
842 if slave_num == 0 {
844 log::error!("No slaves found, check slave connections!");
845 return Err(io::Error::new(io::ErrorKind::Other, "No slaves found"));
846 }
847
848 for i in 0..slave_num {
849 let slave_info = master.get_slave_info(SlavePos::from(i as u16)).unwrap();
850 log::info!("Slave {:?} at position {:?}", slave_info.name, i);
851 slave_names.insert(slave_info.name.clone(), SlavePos::from(i as u16));
852 log::debug!("Found device {:?}", slave_info);
853 log::debug!(
854 "Vendor ID: {:X}, Product Code: {:X}, SM count {:?}",
855 slave_info.id.vendor_id,
856 slave_info.id.product_code,
857 slave_info.sync_count
858 );
859 let slave_addr = SlaveAddr::ByPos(i as u16);
860 let slave_id = SlaveId {
861 vendor_id: slave_info.id.vendor_id,
862 product_code: slave_info.id.product_code,
863 };
864
865 let mut pdos: Vec<Vec<PdoCfg>> = vec![];
866 let mut sms = vec![];
867 let mut mailbox = vec![];
868 let mut direction = vec![];
869 let mut mailbox_entires = vec![];
870 for j in 0..slave_info.sync_count {
871 let sm_idx = SmIdx::new(j);
872 let sm_info = master.get_sync(SlavePos::from(i as u16), sm_idx).unwrap();
873 log::debug!("Found sm {:?}, pdo_count {:?}", sm_info, sm_info.pdo_count);
874
875 if sm_info.pdo_count == 0 {
877 log::debug!("No pdo found in sync manager, skipping!");
878 continue;
879 }
880
881 if sm_info.control_register & 0b10 != 0 {
884 log::debug!("SM is in mailbox mode!");
885 mailbox.push(true);
886 } else {
887 log::debug!("SM is in buffered mode!");
888 mailbox.push(false);
889 }
890
891 if sm_info.control_register & 0b100 != 0 {
892 log::debug!("Input pdos!");
893 direction.push(1);
894 } else {
895 log::debug!("Output pdos!");
896 direction.push(-1);
897 }
898
899 let mut pdo_cfgs = vec![];
900 for pdo_ind in 0..sm_info.pdo_count {
901 let pdo_cfg: PdoCfg = {
902 let pdo_info = master
903 .get_pdo(SlavePos::from(i as u16), sm_idx, PdoPos::new(pdo_ind))
904 .unwrap();
905 log::debug!(
906 "Found pdo {:?}, entry_count {:?}",
907 pdo_info,
908 pdo_info.entry_count
909 );
910
911 let pdo_entries = (0..pdo_info.entry_count)
912 .map(|e| {
913 let entry_info = master
914 .get_pdo_entry(
915 SlavePos::from(i as u16),
916 sm_idx,
917 PdoPos::new(pdo_ind),
918 PdoEntryPos::new(e),
919 )
920 .unwrap();
921 log::debug!(
922 "Found entry {:?}, bit_len {:?}",
923 entry_info,
924 entry_info.bit_len
925 );
926 PdoEntryInfo {
927 entry_idx: entry_info.entry_idx,
928 bit_len: entry_info.bit_len as u8,
929 name: entry_info.name.clone(),
930 pos: PdoEntryPos::from(e as u8),
931 }
932 })
933 .collect();
934 PdoCfg {
935 idx: PdoIdx::new(pdo_info.idx.into()),
936 entries: pdo_entries,
937 }
938 };
939 pdo_cfgs.push(pdo_cfg.clone());
940 }
941 pdos.push(pdo_cfgs.clone());
942 sms.push(sm_info);
943 }
944
945 let mut config = master.configure_slave(slave_addr, slave_id)?;
946 let mut entry_offsets: PdoOffsets = HashMap::new();
947
948 for i in 0..sms.len() {
949 let pds = pdos[i].clone();
950 let sm = sms[i].clone();
951
952 if direction[i] > 0 {
955 config.config_sm_pdos(SmCfg::output(sm.idx), &pds)?;
956 for pdo in &pds {
958 log::debug!("Positions of TX PDO 0x{:X}:", u16::from(pdo.idx));
959 }
960 } else {
961 config.config_sm_pdos(SmCfg::input(sm.idx), &pds)?;
962 for pdo in &pds {
964 log::debug!("Positions of RX PDO 0x{:X}:", u16::from(pdo.idx));
965 }
966 }
967 for pdo in pds {
968 for entry in &pdo.entries {
969 let offset = config.register_pdo_entry(entry.entry_idx, domain_idx)?;
970 let name = entry.name.clone();
971 if entry_offsets.contains_key(&name) {
972 entry_offsets.get_mut(&name).unwrap().push((
973 entry.entry_idx,
974 entry.bit_len,
975 offset,
976 ));
977 } else {
978 entry_offsets.insert(name, vec![(entry.entry_idx, entry.bit_len, offset)]);
979 }
980 if mailbox[i] && direction[i] < 0 {
981 mailbox_entires.push(entry.name.clone());
983 }
984 }
985 }
986 }
987
988 let cfg_index = config.index();
989
990 let cfg_info = master.get_config_info(cfg_index)?;
991 log::debug!("Config info: {:#?}", cfg_info);
992 if cfg_info.slave_position.is_none() {
993 return Err(io::Error::new(
994 io::ErrorKind::Other,
995 "Unable to configure slave",
996 ));
997 }
998 offsets.insert(SlavePos::new(i as u16), entry_offsets);
999 mailbox_pdos.insert(SlavePos::new(i as u16), mailbox_entires);
1000 }
1001
1002 Ok((master, domain_idx, offsets, slave_names, mailbox_pdos))
1003}
1004
1005fn log_pdo_offsets(offsets: &SlaveOffsets) {
1007 for (s, o) in offsets {
1008 log::debug!("PDO offsets of Slave {}:", u16::from(*s));
1009 for (name, pdos) in o {
1010 for (pdo, bit_len, offset) in pdos {
1011 log::debug!(
1012 " - \"{}\" : {:X}:{:X} - {:?}, bit length: {}",
1013 name,
1014 u16::from(pdo.idx),
1015 u8::from(pdo.sub_idx),
1016 offset,
1017 bit_len
1018 );
1019 }
1020 }
1021 }
1022}
1023
1024fn create_slave_name_mapper(slave_names: SlaveNames) -> impl Fn(u16) -> String {
1026 move |id: u16| -> String {
1027 slave_names
1028 .iter()
1029 .find(|(_, sid)| u16::from(**sid) == id)
1030 .unwrap()
1031 .0
1032 .clone()
1033 }
1034}
1035
1036fn set_ready_flag(condvar: &Arc<(Mutex<bool>, Condvar)>, flag: bool) {
1038 let (lock, cvar) = &**condvar;
1039 let mut ready = lock.lock().unwrap();
1040 *ready = flag;
1041 cvar.notify_one();
1042}
1043
1044fn notify_slave_state(condvar: &Arc<(Mutex<Vec<u8>>, Condvar)>, state: Vec<u8>) {
1046 let (lock, cvar) = &**condvar;
1047 let mut sstate = lock.lock().unwrap();
1048 *sstate = state;
1049 cvar.notify_one();
1050}
1051
1052fn notify_next_cycle(condvar: &Arc<(Mutex<bool>, Condvar)>) {
1053 let (lock, cvar) = &**condvar;
1054 let mut next_cycle = lock.lock().unwrap();
1055 *next_cycle = true;
1056 cvar.notify_one();
1057}
1058
1059fn get_slave_current_state(
1061 master: &Master,
1062 slave_pos: SlavePos,
1063 slave_name_from_id: &impl Fn(u16) -> String,
1064 #[cfg(feature = "verify_mailbox_pdos")] slave_is_mailbox_pdo_responding: bool,
1065 #[cfg(feature = "enable_watchdog")] slave_is_watchdog_responding: bool,
1066) -> u8 {
1067 #[cfg(feature = "verify_mailbox_pdos")]
1068 if !slave_is_mailbox_pdo_responding {
1069 log::error!(
1070 "Slave {:?} (pos: {:?}) is not responding (mailbox check failed)!",
1071 slave_name_from_id(slave_pos.into()),
1072 slave_pos
1073 );
1074 return 0;
1075 }
1076 #[cfg(feature = "enable_watchdog")]
1077 if !slave_is_watchdog_responding {
1078 log::error!(
1079 "Slave {:?} (pos: {:?}) is not responding (watchdog check failed)!",
1080 slave_name_from_id(slave_pos.into()),
1081 slave_pos
1082 );
1083 return 0;
1084 }
1085
1086 match master.get_slave_info(slave_pos) {
1087 Ok(info) => {
1088 if info.al_state != AlState::Op {
1089 log::error!(
1090 "Slave {:?} is not operational! State: {:?}",
1091 info.name,
1092 info.al_state
1093 );
1094 0
1095 } else {
1096 AlState::Op as u8
1097 }
1098 }
1099 Err(_) => {
1100 log::error!(
1101 "Failed to get slave info for slave {:?}, name: {:?}",
1102 slave_pos,
1103 slave_name_from_id(slave_pos.into())
1104 );
1105 255
1106 }
1107 }
1108}
1109
1110fn log_master_state(
1112 master: &Master,
1113 slave_number: u32,
1114 #[cfg(feature = "verify_mailbox_pdos")] maibox_timeout_ms: u32,
1115 #[cfg(feature = "enable_watchdog")] watchdog_timeout_ms: u32,
1116 slave_name_from_id: &impl Fn(u16) -> String,
1117 #[cfg(feature = "verify_mailbox_pdos")] slave_is_mailbox_pdo_responding: &Vec<bool>,
1118 #[cfg(feature = "enable_watchdog")] slave_is_watchdog_responding: &Vec<bool>,
1119) {
1120 let m_state = master.state().unwrap();
1121 log::debug!(
1122 "Master State: {:?}, Link up: {}, Slaves connected: {} out of {}",
1123 m_state.al_states,
1124 m_state.link_up,
1125 m_state.slaves_responding,
1126 slave_number
1127 );
1128 if m_state.al_states != AlState::Op as u8 {
1129 log::error!("Master is not operational! State: {:?}", m_state.al_states);
1130 }
1131 if !m_state.link_up {
1132 log::error!("Link is not up!");
1133 }
1134 if m_state.slaves_responding < slave_number {
1135 log::error!(
1136 "Not all slaves are connected! Expected: {}, Responding: {}",
1137 slave_number,
1138 m_state.slaves_responding
1139 );
1140 }
1141 if m_state.slaves_responding > slave_number {
1142 log::error!(
1143 "New slaves are connected! Inintially: {}, Now: {}",
1144 slave_number,
1145 m_state.slaves_responding
1146 );
1147 }
1148
1149 log::info!("Connected slaves:");
1151 for i in 0..slave_number {
1152 match master.get_slave_info(SlavePos::from(i as u16)) {
1153 Ok(info) => {
1154 if info.al_state == AlState::Op {
1155 log::info!(
1156 "Slave {:?} (id: {}) is connected and operational! State: {:?}",
1157 info.name,
1158 i,
1159 info.al_state
1160 );
1161 } else {
1162 log::warn!(
1163 "Slave {:?} (id: {}) is connected but not operational! State: {:?}",
1164 info.name,
1165 i,
1166 info.al_state
1167 );
1168 }
1169 }
1170 Err(_) => {
1171 log::error!("Slave {:?} not connected!", i);
1172 }
1173 }
1174 }
1175
1176 #[cfg(feature = "verify_mailbox_pdos")]
1178 if !slave_is_mailbox_pdo_responding.iter().all(|&r| r) {
1179 log::error!("Not all slaves are responding!");
1180 for i in 0..slave_number {
1181 if !slave_is_mailbox_pdo_responding[i as usize] {
1182 log::error!(
1183 "Poulpe {:?} (pos: {:?}) not responding for more than {}ms",
1184 slave_name_from_id(i as u16),
1185 i,
1186 maibox_timeout_ms
1187 );
1188 }
1189 }
1190 }
1191
1192 #[cfg(feature = "enable_watchdog")]
1193 if !slave_is_watchdog_responding.iter().all(|&r| r) {
1194 log::error!("Not all slaves have watchdog!");
1195 for i in 0..slave_number {
1196 if !slave_is_watchdog_responding[i as usize] {
1197 log::error!(
1198 "Poulpe {:?} (pos: {:?}) watchdog not responding for more than {}ms",
1199 slave_name_from_id(i as u16),
1200 i,
1201 watchdog_timeout_ms
1202 );
1203 }
1204 }
1205 }
1206}