pub struct MainDevice<'sto> { /* private fields */ }Expand description
The main EtherCAT controller.
The MainDevice is passed by reference to SubDeviceGroups to drive their TX/RX methods. It
also provides direct access to EtherCAT PDUs like BRD, LRW, etc.
Implementations§
Source§impl<'sto> MainDevice<'sto>
impl<'sto> MainDevice<'sto>
Sourcepub const fn new(
pdu_loop: PduLoop<'sto>,
timeouts: Timeouts,
config: MainDeviceConfig,
) -> Self
pub const fn new( pdu_loop: PduLoop<'sto>, timeouts: Timeouts, config: MainDeviceConfig, ) -> Self
Create a new EtherCrab MainDevice.
Sourcepub async fn init<const MAX_SUBDEVICES: usize, G>(
&self,
now: impl Fn() -> u64 + Copy,
groups: G,
group_filter: impl for<'g> FnMut(&'g G, &SubDevice) -> Result<&'g dyn SubDeviceGroupHandle, Error>,
) -> Result<G, Error>
pub async fn init<const MAX_SUBDEVICES: usize, G>( &self, now: impl Fn() -> u64 + Copy, groups: G, group_filter: impl for<'g> FnMut(&'g G, &SubDevice) -> Result<&'g dyn SubDeviceGroupHandle, Error>, ) -> Result<G, Error>
Detect SubDevices, set their configured station addresses, assign to groups, configure SubDevices from EEPROM.
This method will request and wait for all SubDevices to be in PRE-OP before returning.
To transition groups into different states, see SubDeviceGroup::into_safe_op or
SubDeviceGroup::into_op.
The group_filter closure should return a &dyn SubDeviceGroupHandle to add the SubDevice
to. All SubDevices must be assigned to a group even if they are unused.
If a SubDevice cannot or should not be added to a group for some reason (e.g. an
unrecognised SubDevice was detected on the network), an
Err(Error::UnknownSubDevice) should be returned.
MAX_SUBDEVICES must be a power of 2 greater than 1.
Note that the sum of the PDI data length for all SubDeviceGroups must not exceed the
value of MAX_PDU_DATA.
§Examples
§Multiple groups
This example groups SubDevices into two different groups.
use ethercrab::{
error::Error, std::{ethercat_now, tx_rx_task}, MainDevice, MainDeviceConfig, PduStorage,
SubDeviceGroup, Timeouts, subdevice_group
};
const MAX_SUBDEVICES: usize = 2;
const MAX_PDU_DATA: usize = PduStorage::element_size(1100);
const MAX_FRAMES: usize = 16;
static PDU_STORAGE: PduStorage<MAX_FRAMES, MAX_PDU_DATA> = PduStorage::new();
/// A custom struct containing two groups to assign SubDevices into.
#[derive(Default)]
struct Groups {
/// 2 SubDevices, totalling 1 byte of PDI.
group_1: SubDeviceGroup<2, 1>,
/// 1 SubDevice, totalling 4 bytes of PDI
group_2: SubDeviceGroup<1, 4>,
}
let (_tx, _rx, pdu_loop) = PDU_STORAGE.try_split().expect("can only split once");
let maindevice = MainDevice::new(pdu_loop, Timeouts::default(), MainDeviceConfig::default());
let groups = maindevice
.init::<MAX_SUBDEVICES, _>(ethercat_now, Groups::default(), |groups: &Groups, subdevice| {
match subdevice.name() {
"COUPLER" | "IO69420" => Ok(&groups.group_1),
"COOLSERVO" => Ok(&groups.group_2),
_ => Err(Error::UnknownSubDevice),
}
},)
.await
.expect("Init");Sourcepub async fn init_single_group<const MAX_SUBDEVICES: usize, const MAX_PDI: usize>(
&self,
now: impl Fn() -> u64 + Copy,
) -> Result<SubDeviceGroup<MAX_SUBDEVICES, MAX_PDI, DefaultLock, PreOp>, Error>
pub async fn init_single_group<const MAX_SUBDEVICES: usize, const MAX_PDI: usize>( &self, now: impl Fn() -> u64 + Copy, ) -> Result<SubDeviceGroup<MAX_SUBDEVICES, MAX_PDI, DefaultLock, PreOp>, Error>
A convenience method to allow the quicker creation of a single group containing all discovered SubDevices.
This method will request and wait for all SubDevices to be in PRE-OP before returning.
To transition groups into different states, see SubDeviceGroup::into_safe_op or
SubDeviceGroup::into_op.
For multiple groups, see MainDevice::init.
§Examples
§Create a single SubDevice group with no PREOP -> SAFEOP configuration
use ethercrab::{
error::Error, MainDevice, MainDeviceConfig, PduStorage, Timeouts, std::ethercat_now
};
const MAX_SUBDEVICES: usize = 2;
const MAX_PDU_DATA: usize = PduStorage::element_size(1100);
const MAX_FRAMES: usize = 16;
const MAX_PDI: usize = 8;
static PDU_STORAGE: PduStorage<MAX_FRAMES, MAX_PDU_DATA> = PduStorage::new();
let (_tx, _rx, pdu_loop) = PDU_STORAGE.try_split().expect("can only split once");
let maindevice = MainDevice::new(pdu_loop, Timeouts::default(), MainDeviceConfig::default());
let group = maindevice
.init_single_group::<MAX_SUBDEVICES, MAX_PDI>(ethercat_now)
.await
.expect("Init");§Create a single SubDevice group with PREOP -> SAFEOP configuration of SDOs
use ethercrab::{
error::Error, MainDevice, MainDeviceConfig, PduStorage, Timeouts, std::ethercat_now
};
const MAX_SUBDEVICES: usize = 2;
const MAX_PDU_DATA: usize = PduStorage::element_size(1100);
const MAX_FRAMES: usize = 16;
const MAX_PDI: usize = 8;
static PDU_STORAGE: PduStorage<MAX_FRAMES, MAX_PDU_DATA> = PduStorage::new();
let (_tx, _rx, pdu_loop) = PDU_STORAGE.try_split().expect("can only split once");
let maindevice = MainDevice::new(pdu_loop, Timeouts::default(), MainDeviceConfig::default());
let mut group = maindevice
.init_single_group::<MAX_SUBDEVICES, MAX_PDI>(ethercat_now)
.await
.expect("Init");
for subdevice in group.iter(&maindevice) {
if subdevice.name() == "EL3004" {
log::info!("Found EL3004. Configuring...");
subdevice.sdo_write(0x1c12, 0, 0u8).await?;
subdevice.sdo_write(0x1c13, 0, 0u8).await?;
subdevice.sdo_write(0x1c13, 1, 0x1a00u16).await?;
subdevice.sdo_write(0x1c13, 2, 0x1a02u16).await?;
subdevice.sdo_write(0x1c13, 3, 0x1a04u16).await?;
subdevice.sdo_write(0x1c13, 4, 0x1a06u16).await?;
subdevice.sdo_write(0x1c13, 0, 4u8).await?;
}
}
let mut group = group.into_safe_op(&maindevice).await.expect("PRE-OP -> SAFE-OP");Sourcepub fn num_subdevices(&self) -> usize
pub fn num_subdevices(&self) -> usize
Get the number of discovered SubDevices in the EtherCAT network.
As init runs SubDevice autodetection, it must be called before this
method to get an accurate count.
Sourcepub async fn wait_for_state(
&self,
desired_state: SubDeviceState,
) -> Result<(), Error>
pub async fn wait_for_state( &self, desired_state: SubDeviceState, ) -> Result<(), Error>
Wait for all SubDevices on the network to reach a given state.
Sourcepub unsafe fn release(self) -> PduLoop<'sto>
pub unsafe fn release(self) -> PduLoop<'sto>
Release the PduLoop storage without resetting it.
To reset the released PduLoop, call PduLoop::reset. This method does not release the
network TX/RX handles created by e.g.
PduStorage::try_split to allow a new MainDevice to be
created while reusing an existing network interface. To release the TX and RX handles as
well, call release_all.
The application should ensure that no EtherCAT data is in flight when this method is called,
i.e. all frames must have either returned to the MainDevice or timed out. If a frame is
received after this method has been called, the PduRx instance handling
that frame will most likely produce an error as the underlying storage for that frame has
been freed.
§Safety
Any groups configured using the previous MainDevice instance must not be used again
with any MainDevices created with the PduLoop returned by this method. The group state
(PDI addresses, offsets, sizes, etc) are only valid with the MainDevice the group was
initialised with.
Sourcepub unsafe fn release_all(self) -> PduLoop<'sto>
pub unsafe fn release_all(self) -> PduLoop<'sto>
Release the PduLoop storage and signal the TX/RX handles to release their resources.
This method is useful to close down a TX/RX loop and the network interface associated with it.
To reuse the TX/RX loop and only free the PduLoop for reuse in another MainDevice
instance, call release.
The application should ensure that no EtherCAT data is in flight when this method is called,
i.e. all frames must have either returned to the MainDevice or timed out. If a frame is
received after this method has been called, the PduRx instance handling
that frame will most likely produce an error as the underlying storage for that frame has
been freed.
§Safety
Any groups configured using the previous MainDevice instance must not be used again
with any MainDevices created with the PduLoop returned by this method. The group state
(PDI addresses, offsets, sizes, etc) are only valid with the MainDevice the group was
initialised with.