use oxictl::protocol::canopen::ds402::{cw, sw, Ds402StateMachine};
use oxictl::protocol::ethercat::dc::DcSynchronizer;
use oxictl::protocol::ethercat::master::{AlState, EtherCatMaster, MasterState, SlaveConfig};
use oxictl::protocol::ethercat::pdo::{PdoEntry, PdoMapping, ProcessImage};
use oxictl::protocol::ethercat::sdo::SdoClient;
fn main() {
println!("=== EtherCAT Servo Simulation ===\n");
let mut master = EtherCatMaster::<4>::new();
master.add_slave(SlaveConfig::new(0, 0x0000_22D2, 0x0000_0001));
println!("[Master] State: {:?}", master.state);
let transitions = [AlState::PreOp, AlState::SafeOp, AlState::Op];
for &target in &transitions {
if master.request_state(target) {
println!("[Master] → {:?}", master.state);
} else {
println!("[Master] Failed to reach {:?}", target);
return;
}
}
let mut tx_pdo = PdoMapping::<2>::new(0x1A00);
tx_pdo.add_entry(PdoEntry::new(0x6064, 0x00, 32)); tx_pdo.add_entry(PdoEntry::new(0x6041, 0x00, 16));
let mut rx_pdo = PdoMapping::<2>::new(0x1600);
rx_pdo.add_entry(PdoEntry::new(0x607A, 0x00, 32)); rx_pdo.add_entry(PdoEntry::new(0x6040, 0x00, 16));
println!("\n[PDO] TxPDO size: {} bytes", tx_pdo.total_bytes());
println!("[PDO] RxPDO size: {} bytes", rx_pdo.total_bytes());
let mut sdo = SdoClient::new();
sdo.define_object(0x6098, 0, &[0x00], false); sdo.define_object(0x6099, 1, &[0x00, 0x00], false); sdo.write_u16(0x6098, 0, 0x0023).ok(); sdo.write_u16(0x6099, 1, 1000).ok(); println!(
"\n[SDO] Homing method set: 0x{:02X}",
sdo.read_u16(0x6098, 0).unwrap_or(0)
);
let mut dc = DcSynchronizer::new(2_000_000, 500); dc.enable();
println!("\n[DC] State: {:?}", dc.state);
let mut t = 0u64;
for _ in 0..20 {
t += 2_000_000 + 800; dc.update(t);
}
println!("[DC] After locking: {:?}", dc.state);
let mut ds402 = Ds402StateMachine::new();
println!("\n[DS402] Initial state: {:?}", ds402.state);
let drive_sequence = [
cw::ENABLE_VOLTAGE | cw::QUICK_STOP, cw::SWITCH_ON | cw::ENABLE_VOLTAGE | cw::QUICK_STOP, cw::SWITCH_ON | cw::ENABLE_VOLTAGE | cw::QUICK_STOP | cw::ENABLE_OPERATION, ];
for &ctrl_word in &drive_sequence {
ds402.apply_control_word(ctrl_word);
println!("[DS402] CW=0x{:04X} → State: {:?}", ctrl_word, ds402.state);
}
println!("\n[Loop] Starting cyclic position control...");
println!("step,target_pos,actual_pos,status_word");
let mut process_image = ProcessImage::<64>::new();
let mut actual_pos_inc: i32 = 0;
let target_positions = [0i32, 100_000, 200_000, 150_000, 0];
for (step, &target) in target_positions.iter().enumerate() {
process_image.write_u32(0, target as u32);
let ctrl = cw::SWITCH_ON | cw::ENABLE_VOLTAGE | cw::QUICK_STOP | cw::ENABLE_OPERATION;
process_image.write_u16(4, ctrl);
actual_pos_inc += (target - actual_pos_inc) / 5;
process_image.write_u32(6, actual_pos_inc as u32); let sw_val = sw::SWITCHED_ON | sw::OPERATION_ENABLED | sw::VOLTAGE_ENABLED | sw::QUICK_STOP;
process_image.write_u16(10, sw_val);
let actual = process_image.read_u32(6).unwrap_or(0);
let status = process_image.read_u16(10).unwrap_or(0);
println!("{},{},{},0x{:04X}", step, target, actual as i32, status);
}
println!("\n[Master] Final bus state: {:?}", master.state);
assert_eq!(master.state, MasterState::Running);
eprintln!("EtherCAT servo simulation complete.");
}