use crate::{
master::Master,
rawmaster::{RawMaster, SlaveAddress},
data::{PduData, Field},
sii::{Sii, SiiError},
mailbox::Mailbox,
can::Can,
registers::{self, AlError},
eeprom,
error::{EthercatError, EthercatResult},
};
use tokio::sync::{Mutex, MutexGuard};
use std::sync::Arc;
use core::time::Duration;
pub type CommunicationState = registers::AlState;
use registers::AlState::*;
pub struct Slave<'a> {
master: Arc<RawMaster>,
address: SlaveAddress,
state: CommunicationState,
safemaster: Option<&'a Master>,
sii: Option<Mutex<Sii>>,
mailbox: Option<Arc<Mutex<Mailbox>>>,
coe: Option<Arc<Mutex<Can>>>,
}
impl<'a> Slave<'a> {
pub fn raw(master: Arc<RawMaster>, address: SlaveAddress) -> Self {
Self {
master,
safemaster: None,
address,
state: Init,
sii: None,
mailbox: None,
coe: None,
}
}
pub async fn new(master: &'a Master, address: SlaveAddress) -> EthercatResult<Slave<'a>> {
let fixed = SlaveAddress::Fixed(master.raw.read(address, registers::address::fixed).await.one()?);
let mut book = master.slaves.lock().unwrap();
if book.contains(&address)
|| book.contains(&fixed)
{Err(EthercatError::Master("slave already in use by an other instance"))}
else {
book.insert(address);
drop(book);
Ok(Self {
master: master.raw.clone(),
safemaster: Some(master),
address,
state: Init,
sii: None,
mailbox: None,
coe: None,
})
}
}
pub unsafe fn raw_master(&self) -> &Arc<RawMaster> {&self.master}
pub fn informations(&self) {todo!()}
pub async fn state(&self) -> EthercatResult<CommunicationState> {
self.master.read(self.address, registers::al::status).await.one()?
.state().try_into()
.map_err(|_| EthercatError::Protocol("undefined slave state"))
}
pub async fn switch(&mut self, target: CommunicationState) -> EthercatResult<(), AlError> {
self.master.write(self.address, registers::al::control, {
let mut config = registers::AlControlRequest::default();
config.set_state(target.into());
config.set_ack(true);
config.set_request_id(true);
config
}).await.one()?;
loop {
let status = self.master.read(self.address, registers::al::response).await.one()?;
if status.error() {
let error = self.master.read(self.address, registers::al::error).await.one()?;
if error != registers::AlError::NoError
{return Err(EthercatError::Slave(self.address, error))}
}
if status.state() == target.into() {break}
}
self.state = target;
Ok(())
}
pub fn expect(&mut self, state: CommunicationState) {
self.state = state;
}
pub fn expected(&self) -> CommunicationState {
self.state
}
pub fn address(&self) -> SlaveAddress {self.address}
pub async fn set_address(&mut self, fixed: u16) -> EthercatResult {
assert!(fixed != 0);
self.master.write(self.address, registers::address::fixed, fixed).await.one()?;
let new = SlaveAddress::Fixed(fixed);
if let Some(safe) = self.safemaster {
let mut book = safe.slaves.lock().unwrap();
book.remove(&self.address);
book.insert(new);
}
self.address = new;
Ok(())
}
pub async fn reset_address(self) -> EthercatResult {
self.master.write(self.address, registers::address::fixed, 0).await.one()?;
Ok(())
}
pub async fn init_sii(&mut self) -> EthercatResult<(), SiiError> {
let sii = Sii::new(self.master.clone(), self.address).await?;
self.sii = Some(Mutex::new(sii));
Ok(())
}
pub async fn init_mailbox(&mut self) -> EthercatResult<(), SiiError> {
assert_eq!(self.state, Init);
let address = match self.address {
SlaveAddress::Fixed(i) => i,
_ => panic!("mailbox is unsafe without fixed addresses"),
};
if self.sii.is_none() {
self.init_sii().await?;
}
let mut sii = self.sii().await;
sii.acquire().await?;
let write_offset = sii.read(eeprom::mailbox::standard::write::offset).await?;
let write_size = sii.read(eeprom::mailbox::standard::write::size).await?;
let read_offset = sii.read(eeprom::mailbox::standard::read::offset).await?;
let read_size = sii.read(eeprom::mailbox::standard::read::size).await?;
sii.release().await?;
drop(sii);
let mailbox = Mailbox::new(
self.master.clone(),
address,
(registers::sync_manager::interface.mailbox_write(), write_offset .. write_offset + write_size),
(registers::sync_manager::interface.mailbox_read(), read_offset .. read_offset + read_size),
).await?;
self.coe = None;
self.mailbox = Some(Arc::new(Mutex::new(mailbox)));
Ok(())
}
pub async fn init_coe(&mut self) -> EthercatResult<(), SiiError> {
if self.mailbox.is_none() {
self.init_mailbox().await?;
}
let mailbox = self.mailbox.clone().unwrap();
self.coe = Some(Arc::new(Mutex::new(Can::new(mailbox))));
Ok(())
}
pub async fn init_sync(&mut self, period: Duration, activation: Duration) -> EthercatResult {
assert_eq!(self.state, PreOperational);
let period = u32::try_from(period.as_nanos()).expect("synchronization period must be below 4seconds");
let activation = u32::try_from(activation.as_nanos()).expect("activation delay must be below 4seconds");
self.master.write(self.address, registers::isochronous::sync::enable, Default::default()).await.one()?;
self.master.write(self.address, Field::<u8>::simple(0x980), 0).await.one()?;
let start = self.master.read(self.address, registers::dc::system_time).await.one()? as u32;
let start = ((start + activation) / period) * period + period;
self.master.write(self.address, registers::isochronous::sync::start_time, start).await.one()?;
self.master.write(self.address, registers::isochronous::sync::sync0_cycle_time, period).await.one()?;
self.master.write(self.address, registers::isochronous::sync::enable, {
let mut enables = registers::IsochronousEnables::default();
enables.set_operation(true);
enables.set_sync0(true);
enables
}).await.one()?;
Ok(())
}
pub async fn sii(&self) -> MutexGuard<'_, Sii> {
self.sii
.as_ref().expect("sii not initialized")
.lock().await
}
pub async fn coe(&self) -> MutexGuard<'_, Can> {
self.coe
.as_ref().expect("coe not initialized")
.lock().await
}
pub async fn physical_read<T: PduData>(&self, field: Field<T>) -> EthercatResult<T> {
self.master.read(self.address, field).await.one()
}
}
impl Drop for Slave<'_> {
fn drop(&mut self) {
if let Some(safe) = self.safemaster {
let mut book = safe.slaves.lock().unwrap();
book.remove(&self.address);
}
}
}
impl From<EthercatError<()>> for EthercatError<AlError> {
fn from(src: EthercatError<()>) -> Self {src.upgrade()}
}