Compare commits

...
3 Commits
Author SHA1 Message Date
kraemr f9eea67743 test 2025-10-29 19:00:15 +01:00
kraemr 70adc3a36c fixed ident 2025-10-29 15:38:48 +01:00
kraemr 6639d11593 Wago is now identified 2025-10-29 15:38:48 +01:00
9 changed files with 327 additions and 112 deletions

No files matched your search

@@ -27,6 +27,7 @@ use ethercat_hal::devices::el7031::{EL7031_IDENTITY_A, EL7031_IDENTITY_B};
use ethercat_hal::devices::el7031_0030::EL7031_0030_IDENTITY_A;
use ethercat_hal::devices::el7041_0052::EL7041_0052_IDENTITY_A;
use ethercat_hal::devices::subdevice_identity_to_tuple;
use ethercat_hal::devices::wago_750_354::WAGO_750_354_IDENTITY;
use ethercat_hal::helpers::ethercrab_types::{
EthercrabSubDeviceOperational, EthercrabSubDevicePreoperational,
};
@@ -258,6 +259,7 @@ pub fn get_identification_addresses(
EL7031_IDENTITY_A | EL7031_IDENTITY_B => MachineIdentificationAddresses::default(),
EL7031_0030_IDENTITY_A => MachineIdentificationAddresses::default(),
EL7041_0052_IDENTITY_A => MachineIdentificationAddresses::default(),
WAGO_750_354_IDENTITY => MachineIdentificationAddresses::default(),
_ => {
// block_on(u16dump(&subdevice, maindevice, 0x00, 0xff))?;
@@ -153,13 +153,13 @@ fn test_interface(interface: &str) -> Result<(), anyhow::Error> {
// Default 0ms
wait_loop_delay: Duration::from_millis(0),
// Default 100ms
mailbox_echo: Duration::from_millis(100),
mailbox_echo: Duration::from_millis(1000),
// Default 1000ms
mailbox_response: Duration::from_millis(1000),
mailbox_response: Duration::from_millis(2000),
},
MainDeviceConfig {
// Default 10000
dc_static_sync_iterations: 10000,
dc_static_sync_iterations: 0,
// Default None
retry_behaviour: ethercrab::RetryBehaviour::Count(3),
},
+2 -2
View File
@@ -52,7 +52,7 @@ pub fn set_realtime_priority() -> Result<(), anyhow::Error> {
use anyhow::anyhow;
use libc::{SCHED_FIFO, pthread_setschedparam, sched_param};
use std::mem;
/*
unsafe {
// Get the pthread_t of the current thread using pthread_self()
let pthread_self = libc::pthread_self();
@@ -80,7 +80,7 @@ pub fn set_realtime_priority() -> Result<(), anyhow::Error> {
std::thread::current().name().unwrap_or("unknown")
);
}
}
}*/
Ok(())
}
+8 -2
View File
@@ -20,11 +20,14 @@ pub mod el6021;
pub mod el7031;
pub mod el7031_0030;
pub mod el7041_0052;
pub mod wago_750_354;
// pub mod el4008;
use super::devices::el1008::EL1008;
use crate::{
devices::{el2521::EL2521, el4002::EL4002},
helpers::ethercrab_types::EthercrabSubDeviceGroupPreoperational,
helpers::ethercrab_types::{
EthercrabSubDeviceGroupPreoperational, EthercrabSubDeviceGroupSafeOperational,
},
};
use anyhow::anyhow;
use bitvec::{order::Lsb0, slice::BitSlice};
@@ -53,6 +56,7 @@ use el7041_0052::EL7041_0052_IDENTITY_A;
use ethercrab::{MainDevice, SubDeviceIdentity};
use smol::lock::RwLock;
use std::{any::Any, fmt::Debug, sync::Arc};
use wago_750_354::{WAGO_750_354, WAGO_750_354_IDENTITY, WAGO_750_354_REVISION};
/// A trait for all devices
///
@@ -218,6 +222,8 @@ pub fn device_from_subdevice_identity_tuple(
EL2521_IDENTITY_0000_A | EL2521_IDENTITY_0000_B | EL2521_IDENTITY_0024_A => {
Ok(Arc::new(RwLock::new(EL2521::new())))
}
WAGO_750_354_IDENTITY => Ok(Arc::new(RwLock::new(WAGO_750_354::new()))),
_ => Err(anyhow::anyhow!(
"[{}::device_from_subdevice] No Driver: vendor_id: 0x{:x}, product_id: 0x{:x}, revision: 0x{:x}",
module_path!(),
@@ -239,7 +245,7 @@ pub fn device_from_subdevice_identity(
/// Array equivalent of [`device_from_subdevice`]
pub fn devices_from_subdevices<'maindevice, const MAX_SUBDEVICES: usize, const PDI_LEN: usize>(
group: &mut EthercrabSubDeviceGroupPreoperational<MAX_SUBDEVICES, PDI_LEN>,
group: &mut EthercrabSubDeviceGroupSafeOperational<MAX_SUBDEVICES, PDI_LEN>,
maindevice: &MainDevice,
) -> Result<Vec<Arc<RwLock<dyn EthercatDevice>>>, anyhow::Error> {
group
+133
View File
@@ -0,0 +1,133 @@
use super::{EthercatDeviceProcessing, NewEthercatDevice, SubDeviceIdentityTuple};
use crate::helpers::ethercrab_types::EthercrabSubDevicePreoperational;
use crate::io::digital_output::{DigitalOutputDevice, DigitalOutputOutput};
use crate::pdo::{RxPdo, basic::BoolPdoObject};
use ethercat_hal_derive::{EthercatDevice, RxPdo};
/// WAGO_730_1506 8-channel digital output device
///
/// 24V DC, 0.5A per channel
#[derive(EthercatDevice)]
pub struct WAGO_730_1506 {
pub rxpdo: WAGO_730_1506_RxPdo,
is_used: bool,
}
impl EthercatDeviceProcessing for WAGO_730_1506 {}
impl std::fmt::Debug for WAGO_730_1506 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "WAGO_730_1506")
}
}
impl NewEthercatDevice for WAGO_730_1506 {
fn new() -> Self {
Self {
rxpdo: WAGO_730_1506_RxPdo::default(),
is_used: false,
}
}
}
impl DigitalOutputDevice<WAGO_730_1506_Port> for WAGO_730_1506 {
fn set_output(&mut self, port: WAGO_730_1506_Port, value: DigitalOutputOutput) {
let expect_text = "All channels should be Some(_)";
match port {
WAGO_730_1506_Port::DO1 => {
self.rxpdo.channel1.as_mut().expect(expect_text).value = value.into()
}
WAGO_730_1506_Port::DO2 => {
self.rxpdo.channel2.as_mut().expect(expect_text).value = value.into()
}
WAGO_730_1506_Port::DO3 => {
self.rxpdo.channel3.as_mut().expect(expect_text).value = value.into()
}
WAGO_730_1506_Port::DO4 => {
self.rxpdo.channel4.as_mut().expect(expect_text).value = value.into()
}
WAGO_730_1506_Port::DO5 => {
self.rxpdo.channel5.as_mut().expect(expect_text).value = value.into()
}
WAGO_730_1506_Port::DO6 => {
self.rxpdo.channel6.as_mut().expect(expect_text).value = value.into()
}
WAGO_730_1506_Port::DO7 => {
self.rxpdo.channel7.as_mut().expect(expect_text).value = value.into()
}
WAGO_730_1506_Port::DO8 => {
self.rxpdo.channel8.as_mut().expect(expect_text).value = value.into()
}
}
}
fn get_output(&self, port: WAGO_730_1506_Port) -> DigitalOutputOutput {
let expect_text = "All channels should be Some(_)";
DigitalOutputOutput(match port {
WAGO_730_1506_Port::DO1 => self.rxpdo.channel1.as_ref().expect(expect_text).value,
WAGO_730_1506_Port::DO2 => self.rxpdo.channel2.as_ref().expect(expect_text).value,
WAGO_730_1506_Port::DO3 => self.rxpdo.channel3.as_ref().expect(expect_text).value,
WAGO_730_1506_Port::DO4 => self.rxpdo.channel4.as_ref().expect(expect_text).value,
WAGO_730_1506_Port::DO5 => self.rxpdo.channel5.as_ref().expect(expect_text).value,
WAGO_730_1506_Port::DO6 => self.rxpdo.channel6.as_ref().expect(expect_text).value,
WAGO_730_1506_Port::DO7 => self.rxpdo.channel7.as_ref().expect(expect_text).value,
WAGO_730_1506_Port::DO8 => self.rxpdo.channel8.as_ref().expect(expect_text).value,
})
}
}
#[derive(Debug, Clone)]
pub enum WAGO_730_1506_Port {
DO1,
DO2,
DO3,
DO4,
DO5,
DO6,
DO7,
DO8,
}
#[derive(Debug, Clone, RxPdo)]
pub struct WAGO_730_1506_RxPdo {
#[pdo_object_index(0x1600)]
pub channel1: Option<BoolPdoObject>,
#[pdo_object_index(0x1601)]
pub channel2: Option<BoolPdoObject>,
#[pdo_object_index(0x1602)]
pub channel3: Option<BoolPdoObject>,
#[pdo_object_index(0x1603)]
pub channel4: Option<BoolPdoObject>,
#[pdo_object_index(0x1604)]
pub channel5: Option<BoolPdoObject>,
#[pdo_object_index(0x1605)]
pub channel6: Option<BoolPdoObject>,
#[pdo_object_index(0x1606)]
pub channel7: Option<BoolPdoObject>,
#[pdo_object_index(0x1607)]
pub channel8: Option<BoolPdoObject>,
}
impl Default for WAGO_730_1506_RxPdo {
fn default() -> Self {
Self {
channel1: Some(BoolPdoObject::default()),
channel2: Some(BoolPdoObject::default()),
channel3: Some(BoolPdoObject::default()),
channel4: Some(BoolPdoObject::default()),
channel5: Some(BoolPdoObject::default()),
channel6: Some(BoolPdoObject::default()),
channel7: Some(BoolPdoObject::default()),
channel8: Some(BoolPdoObject::default()),
}
}
}
pub const WAGO_730_1506_VENDOR_ID: u32 = 0x21;
pub const WAGO_730_1506_PRODUCT_ID: u32 = 0x7500354;
pub const WAGO_730_1506_REVISION: u32 = 0x2;
pub const WAGO_730_1506_IDENTITY: SubDeviceIdentityTuple = (
WAGO_730_1506_VENDOR_ID,
WAGO_730_1506_PRODUCT_ID,
WAGO_730_1506_REVISION,
);
+30
View File
@@ -0,0 +1,30 @@
use super::{EthercatDeviceProcessing, NewEthercatDevice, SubDeviceIdentityTuple};
use ethercat_hal_derive::EthercatDevice;
/// WAGO_750_354 bus coupler
#[derive(Clone, EthercatDevice)]
pub struct WAGO_750_354 {
is_used: bool,
}
impl EthercatDeviceProcessing for WAGO_750_354 {}
impl NewEthercatDevice for WAGO_750_354 {
fn new() -> Self {
Self { is_used: false }
}
}
impl std::fmt::Debug for WAGO_750_354 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "WAGO_750_354")
}
}
pub const WAGO_750_354_VENDOR_ID: u32 = 0x21;
pub const WAGO_750_354_PRODUCT_ID: u32 = 0x7500354;
pub const WAGO_750_354_REVISION: u32 = 0x2;
pub const WAGO_750_354_IDENTITY: SubDeviceIdentityTuple = (
WAGO_750_354_VENDOR_ID,
WAGO_750_354_PRODUCT_ID,
WAGO_750_354_REVISION,
);
+7 -1
View File
@@ -1,4 +1,7 @@
use ethercrab::{SubDevice, SubDeviceGroup, SubDevicePdi, SubDeviceRef, subdevice_group::Op};
use ethercrab::{
SubDevice, SubDeviceGroup, SubDevicePdi, SubDeviceRef,
subdevice_group::{Op, SafeOp},
};
pub type EthercrabSubDevicePreoperational<'maindevice> =
SubDeviceRef<'maindevice, &'maindevice SubDevice>;
pub type EthercrabSubDeviceOperational<'maindevice, const MAX_PDI: usize> =
@@ -7,5 +10,8 @@ pub type EthercrabSubDeviceOperational<'maindevice, const MAX_PDI: usize> =
pub type EthercrabSubDeviceGroupOperational<const MAX_SUBDEVICES: usize, const MAX_PDI: usize> =
SubDeviceGroup<MAX_SUBDEVICES, MAX_PDI, Op>;
pub type EthercrabSubDeviceGroupSafeOperational<const MAX_SUBDEVICES: usize, const MAX_PDI: usize> =
SubDeviceGroup<MAX_SUBDEVICES, MAX_PDI, SafeOp>;
pub type EthercrabSubDeviceGroupPreoperational<const MAX_SUBDEVICES: usize, const MAX_PDI: usize> =
SubDeviceGroup<MAX_SUBDEVICES, MAX_PDI>;
+2 -4
View File
@@ -3,8 +3,6 @@ use ethercrab::PduStorage;
/// Maximum number of SubDevices that can be stored. This must be a power of 2 greater than 1.
pub const MAX_SUBDEVICES: usize = 16;
/// Maximum PDU data payload size - set this to the max PDI size or higher.
pub const MAX_PDU_DATA: usize = PduStorage::element_size(256);
/// Maximum number of EtherCAT frames that can be in flight at any one time.
pub const PDI_LEN: usize = 32;
pub const MAX_PDU_DATA: usize = PduStorage::element_size(PDI_LEN * 16);
pub const MAX_FRAMES: usize = 16;
/// Maximum total PDI length.
pub const PDI_LEN: usize = 256;
+140 -100
View File
@@ -55,7 +55,7 @@ pub async fn setup_loop(interface: &str, app_state: Arc<AppState>) -> Result<(),
#[cfg(not(feature = "development-build"))]
let _ = set_realtime_priority();
#[cfg(not(all(target_os = "linux", feature = "io-uring")))]
// #[cfg(not(all(target_os = "linux", feature = "io-uring")))]
{
use ethercrab::std::tx_rx_task;
use futures::executor::block_on;
@@ -67,16 +67,16 @@ pub async fn setup_loop(interface: &str, app_state: Arc<AppState>) -> Result<(),
.await
}));
}
#[cfg(all(target_os = "linux", feature = "io-uring"))]
/* #[cfg(all(target_os = "linux", feature = "io-uring"))]
{
use ethercrab::std::tx_rx_task_io_uring;
let _ = tx_rx_task_io_uring(&interface, tx, rx)
.expect("Failed to spawn TX/RX task (io_uring)");
}
}*/
})
.expect("Building thread");
std::thread::sleep(Duration::from_millis(500)); // let TX/RX thread breathe
// Create maindevice
let maindevice = MainDevice::new(
pdu,
@@ -90,15 +90,15 @@ pub async fn setup_loop(interface: &str, app_state: Arc<AppState>) -> Result<(),
// Default 0ms
wait_loop_delay: Duration::from_millis(0),
// Default 100ms
mailbox_echo: Duration::from_millis(100),
mailbox_echo: Duration::from_millis(1000),
// Default 1000ms
mailbox_response: Duration::from_millis(1000),
mailbox_response: Duration::from_millis(2000),
},
MainDeviceConfig {
// Default RetryBehaviour::None
retry_behaviour: RetryBehaviour::Count(5),
// Default 10_000
dc_static_sync_iterations: 10_000,
// Default 10000
dc_static_sync_iterations: 0,
// Default None
retry_behaviour: ethercrab::RetryBehaviour::Count(3),
},
);
@@ -133,91 +133,130 @@ pub async fn setup_loop(interface: &str, app_state: Arc<AppState>) -> Result<(),
))?,
};
// create devices
let devices =
devices_from_subdevices::<MAX_SUBDEVICES, PDI_LEN>(&mut group_preop, &maindevice)?;
let subdevices = group_preop.iter(&maindevice).collect::<Vec<_>>();
/* let subdevices = group_preop.iter(&maindevice).collect::<Vec<_>>();
// extract device identifications
let device_identifications = read_device_identifications(&subdevices, &maindevice)
.await
.into_iter()
.enumerate()
.map(|(i, result)| (i, result.ok()))
.map(
|(subdevice_index, device_machine_identification)| DeviceIdentification {
device_machine_identification,
device_hardware_identification: DeviceHardwareIdentification::Ethercat(
DeviceHardwareIdentificationEthercat { subdevice_index },
),
},
)
.collect::<Vec<_>>();
let devices = device_identifications
.into_iter()
.zip(devices)
.zip(&subdevices)
.map(|((a, b), c)| (a, b, c))
.collect::<Vec<_>>();
// filter devices and if Option<DeviceMachineIdentification> is Some
// return identified_devices, identified_device_identifications, identified_subdevices
let (identified_device_identifications, identified_devices, identified_subdevices): (
Vec<_>,
Vec<_>,
Vec<_>,
) = devices
.iter()
.filter(|(device_identification, _, _)| {
match device_identification {
DeviceIdentification {
device_machine_identification: Some(_),
..
} => true,
_ => false
}
})
.map(|(device_identification, device, subdevice)| {
(
device_identification.clone(),
device.clone(),
*subdevice,
let master = subdevices.get(0).unwrap();
let rxpdo_count: u8 = master.sdo_read(0x1C12, 0x00).await?;
let txpdo_count: u8 = master.sdo_read(0x1C13, 0x00).await?;
tracing::info!("RxPDO count {}, TxPDO count {}", rxpdo_count, txpdo_count);*/
// master.sdo_write(0x1C12, 0x00, 0u16).await?;
// master.sdo_write(0x1C13, 0x00, 0u16).await?;
// Map 0x1AFF "Device Status PDO" as first TxPDO
// master.sdo_write(0x1C13, 0x01, 0x1AFF).await?;
// Map 0x1A00 "TxPDO Mapping Terminal 1" as second TxPDO
//master.sdo_write(0x1C13, 0x02, 0x1A00).await?;
// Set number of TxPDO entries
//master.sdo_write(0x1C13, 0x00, 2u16).await?;
// SM2 = Outputs (MasterWrite)
//master.sdo_write(SM_OUTPUT, 0x01, 0x00u16).await?;
// SM3 = Inputs (MasterRead)
//master.sdo_write(SM_INPUT, 0x01, 0x00u16).await?;
let mut group_safe = match group_preop.into_safe_op(&maindevice).await {
Ok(group) => group,
Err(_) => return Ok(()),
};
// create devices
let devices = devices_from_subdevices::<MAX_SUBDEVICES, PDI_LEN>(&mut group_safe, &maindevice)?;
let subdevices = group_safe.iter(&maindevice).collect::<Vec<_>>();
/*
// extract device identifications
let device_identifications = read_device_identifications(&subdevices, &maindevice)
.await
.into_iter()
.enumerate()
.map(|(i, result)| (i, result.ok()))
.map(
|(subdevice_index, device_machine_identification)| DeviceIdentification {
device_machine_identification,
device_hardware_identification: DeviceHardwareIdentification::Ethercat(
DeviceHardwareIdentificationEthercat { subdevice_index },
),
},
)
})
.fold(
(Vec::new(), Vec::new(), Vec::new()),
|mut acc, (identified_device_identification, identified_device, identified_subdevice)| {
acc.0.push(identified_device_identification);
acc.1.push(identified_device);
acc.2.push(identified_subdevice);
acc
},
);
// construct machines
{
let mut machines_guard = app_state.machines.write().await;
machines_guard.set_ethercat_devices::<MAX_SUBDEVICES, PDI_LEN>(
&identified_device_identifications,
&MACHINE_REGISTRY,
&MachineNewHardwareEthercat {
ethercat_devices: &identified_devices,
subdevices: &identified_subdevices,
},
app_state.socketio_setup.socket_queue_tx.clone(),
Arc::downgrade(&app_state.machines),
);
}
.collect::<Vec<_>>();
let devices = device_identifications
.into_iter()
.zip(devices)
.zip(&subdevices)
.map(|((a, b), c)| (a, b, c))
.collect::<Vec<_>>();
// filter devices and if Option<DeviceMachineIdentification> is Some
// return identified_devices, identified_device_identifications, identified_subdevices
let (identified_device_identifications, identified_devices, identified_subdevices): (
Vec<_>,
Vec<_>,
Vec<_>,
) = devices
.iter()
.filter(|(device_identification, _, _)| {
match device_identification {
DeviceIdentification {
device_machine_identification: Some(_),
..
} => true,
_ => false
}
})
.map(|(device_identification, device, subdevice)| {
(
device_identification.clone(),
device.clone(),
*subdevice,
)
})
.fold(
(Vec::new(), Vec::new(), Vec::new()),
|mut acc, (identified_device_identification, identified_device, identified_subdevice)| {
acc.0.push(identified_device_identification);
acc.1.push(identified_device);
acc.2.push(identified_subdevice);
acc
},
);
// construct machines
{
let mut machines_guard = app_state.machines.write().await;
machines_guard.set_ethercat_devices::<MAX_SUBDEVICES, PDI_LEN>(
&identified_device_identifications,
&MACHINE_REGISTRY,
&MachineNewHardwareEthercat {
ethercat_devices: &identified_devices,
subdevices: &identified_subdevices,
},
app_state.socketio_setup.socket_queue_tx.clone(),
Arc::downgrade(&app_state.machines),
);
}
// remove subdevice from devices tuple
let devices = devices
.iter()
.map(|(device_identification, device, _)| (device_identification.clone(), device.clone()))
.collect::<Vec<_>>();
*/
for subdevice in subdevices.iter() {
if subdevice.name() == "EL5152" {
println!("Erro reg: {:?}", subdevice.sdo_read::<u8>(0x1001, 0).await?);
// let al_status: u16 = subdevice.sdo_read(0x0134, 0x00).await?;
// println!("AL status: 0x{:04x}", al_status);
// println!("{:?}", subdevice.name());
/* if subdevice.name() == "EL5152" {
subdevice.sdo_write(SM_OUTPUT, 0x1, 0x00u16).await?; //set sync mode (1) for free run (0)
subdevice.sdo_write(SM_INPUT, 0x1, 0x00u16).await?; //set sync mode (1) for free run (0)
}
}*/
/*if subdevice.name().starts_with("750") {
// pseudo-code, depends on your crate API
subdevice.sdo_write(0x1C32, 0x1, 0x00u16).await?; // set SM2 mode
subdevice.sdo_write(0x1C33, 0x1, 0x00u16).await?; // set SM3 mode
}*/
}
// Notify client via socketio
let app_state_clone = app_state.clone();
@@ -231,29 +270,30 @@ pub async fn setup_loop(interface: &str, app_state: Arc<AppState>) -> Result<(),
let event = MachinesEventBuilder().build(app_state_clone.clone());
main_namespace.emit(MainNamespaceEvents::MachinesEvent(event));
});
// Put group in operational state
let group_op = match group_preop.into_op(&maindevice).await {
Ok(group_op) => {
tracing::info!("Group in OP state");
group_op
}
Err(err) => Err(anyhow::anyhow!(
"[{}::setup_loop] Failed to put group in OP state: {:?}",
module_path!(),
err
))?,
};
let group_op = group_safe.into_op(&maindevice).await?;
/*
// Put group in operational state
let group_op = match group_safe.into_op(&maindevice).await {
Ok(group_op) => {
tracing::info!("Group in OP state");
group_op
}
Err(err) => Err(anyhow::anyhow!(
"[{}::setup_loop] Failed to put group in OP state: {:?}",
module_path!(),
err
))?,
};
*/
// Write all this stuff to `app_state`
{
/*{
let mut ethercat_setup_guard = app_state.ethercat_setup.write().await;
*ethercat_setup_guard = Some(EthercatSetup {
devices,
group: group_op,
maindevice,
});
}
}*/
// Notify client via socketio
let app_state_clone = app_state.clone();