Skip to main content

MainDevice

Struct MainDevice 

Source
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>

Source

pub const fn new( pdu_loop: PduLoop<'sto>, timeouts: Timeouts, config: MainDeviceConfig, ) -> Self

Create a new EtherCrab MainDevice.

Source

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");
Source

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");
Source

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.

Source

pub async fn wait_for_state( &self, desired_state: SubDeviceState, ) -> Result<(), Error>

Wait for all SubDevices on the network to reach a given state.

Source

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.

Source

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.

Trait Implementations§

Source§

impl<'sto> Debug for MainDevice<'sto>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Sync for MainDevice<'_>

Auto Trait Implementations§

§

impl<'sto> !Freeze for MainDevice<'sto>

§

impl<'sto> !RefUnwindSafe for MainDevice<'sto>

§

impl<'sto> Send for MainDevice<'sto>

§

impl<'sto> Unpin for MainDevice<'sto>

§

impl<'sto> UnsafeUnpin for MainDevice<'sto>

§

impl<'sto> !UnwindSafe for MainDevice<'sto>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more