#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AlState {
Init = 0x01,
PreOp = 0x02,
SafeOp = 0x04,
Op = 0x08,
}
#[derive(Debug, Clone, Copy)]
pub struct SlaveConfig {
pub address: u16,
pub vendor_id: u32,
pub product_code: u32,
pub state: AlState,
pub input_bytes: usize,
pub output_bytes: usize,
}
impl SlaveConfig {
pub fn new(address: u16, vendor_id: u32, product_code: u32) -> Self {
Self {
address,
vendor_id,
product_code,
state: AlState::Init,
input_bytes: 0,
output_bytes: 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MasterState {
Idle,
Scanning,
Configuring,
Running,
Error,
}
pub struct EtherCatMaster<const MAX_SLAVES: usize> {
pub slaves: [Option<SlaveConfig>; MAX_SLAVES],
pub n_slaves: usize,
pub state: MasterState,
pub cycle_count: u64,
pub working_counter: u16,
}
impl<const MAX_SLAVES: usize> EtherCatMaster<MAX_SLAVES> {
pub fn new() -> Self {
Self {
slaves: core::array::from_fn(|_| None),
n_slaves: 0,
state: MasterState::Idle,
cycle_count: 0,
working_counter: 0,
}
}
pub fn add_slave(&mut self, config: SlaveConfig) -> Option<usize> {
if self.n_slaves >= MAX_SLAVES {
return None;
}
let idx = self.n_slaves;
self.slaves[idx] = Some(config);
self.n_slaves += 1;
Some(idx)
}
pub fn request_state(&mut self, target: AlState) -> bool {
for slave in self.slaves[..self.n_slaves].iter_mut().flatten() {
slave.state = target;
}
self.state = if target == AlState::Op {
MasterState::Running
} else {
MasterState::Configuring
};
true
}
pub fn exchange(&mut self) -> u16 {
if self.state != MasterState::Running {
return 0;
}
self.cycle_count += 1;
self.working_counter = self.n_slaves as u16;
self.working_counter
}
pub fn is_running(&self) -> bool {
self.state == MasterState::Running
}
pub fn slave(&self, address: u16) -> Option<&SlaveConfig> {
self.slaves[..self.n_slaves]
.iter()
.filter_map(|s| s.as_ref())
.find(|s| s.address == address)
}
}
impl<const MAX_SLAVES: usize> Default for EtherCatMaster<MAX_SLAVES> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn master_add_slaves() {
let mut master = EtherCatMaster::<8>::new();
master.add_slave(SlaveConfig::new(0, 0x000022D2, 0x00000001));
master.add_slave(SlaveConfig::new(1, 0x000022D2, 0x00000002));
assert_eq!(master.n_slaves, 2);
}
#[test]
fn master_bring_up() {
let mut master = EtherCatMaster::<4>::new();
master.add_slave(SlaveConfig::new(0, 0x1234, 0x0001));
master.add_slave(SlaveConfig::new(1, 0x1234, 0x0002));
assert!(master.request_state(AlState::Op));
assert_eq!(master.state, MasterState::Running);
assert_eq!(master.slaves[0].unwrap().state, AlState::Op);
}
#[test]
fn master_cyclic_exchange() {
let mut master = EtherCatMaster::<4>::new();
master.add_slave(SlaveConfig::new(0, 0x1234, 0x0001));
master.request_state(AlState::Op);
let wc = master.exchange();
assert_eq!(wc, 1);
assert_eq!(master.cycle_count, 1);
}
#[test]
fn master_not_running_exchange_returns_zero() {
let mut master = EtherCatMaster::<2>::new();
let wc = master.exchange();
assert_eq!(wc, 0);
}
#[test]
fn master_find_slave_by_address() {
let mut master = EtherCatMaster::<4>::new();
master.add_slave(SlaveConfig::new(3, 0xABCD, 0x1234));
let s = master.slave(3).unwrap();
assert_eq!(s.vendor_id, 0xABCD);
}
}