use std::error::Error;
use core::time::Duration;
use futures_concurrency::future::Race;
use etherage::{
EthernetSocket, Master,
Slave, SlaveAddress, CommunicationState,
data::Field,
sdo::{self, Sdo, OperationMode},
mapping::{self, Mapping, Group},
registers::{self, SyncDirection},
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let master = Master::new(EthernetSocket::new("eno1")?);
let config = mapping::Config::default();
let mapping = Mapping::new(&config);
let mut slave = mapping.slave(1);
let _statuscom = slave.register(SyncDirection::Read, registers::al::status);
let mut channel = slave.channel(sdo::sync_manager.logical_write(), 0x1800 .. 0x1c00);
let mut pdo = channel.push(sdo::Pdo::with_capacity(0x1600, false, 10));
let controlword = pdo.push(sdo::cia402::controlword);
let target_mode = pdo.push(sdo::cia402::target::mode);
let target_position = pdo.push(sdo::cia402::target::position);
let offset_torque = pdo.push(sdo::cia402::offset::torque);
let mut channel = slave.channel(sdo::sync_manager.logical_read(), 0x1c00 .. 0x2000);
let mut pdo = channel.push(sdo::Pdo::with_capacity(0x1a00, false, 10));
let statusword = pdo.push(sdo::cia402::statusword);
let error = pdo.push(sdo::cia402::error);
let current_position = pdo.push(sdo::cia402::current::position);
drop(slave);
let group = master.group(&mapping).await;
let kpp = Sdo::<f32>::sub(0x2012, 1, 0); let kvp = Sdo::<f32>::sub(0x2012, 5, 0); let kvi = Sdo::<f32>::sub(0x2012, 6, 0);
let mut slave = Slave::new(&master, SlaveAddress::AutoIncremented(0)).await?;
slave.switch(CommunicationState::Init).await?;
slave.set_address(1).await?;
slave.init_coe().await?;
slave.switch(CommunicationState::PreOperational).await?;
group.configure(&slave).await?;
slave.switch(CommunicationState::SafeOperational).await?;
slave.switch(CommunicationState::Operational).await?;
let cycle = tokio::sync::Notify::new();
let period = 1e-3;
(
async {
let mut period = tokio::time::interval(Duration::from_secs_f32(period));
loop {
group.data().await.exchange().await;
cycle.notify_waiters();
period.tick().await;
}
},
async {
let initial = {
let mut group = group.data().await;
let initial = group.get(current_position);
group.set(target_mode, OperationMode::SynchronousPosition);
group.set(target_position, initial);
initial as f32
};
println!("switch on");
switch_on(statusword, controlword, error, &group, &cycle).await;
let position_frequency = 0.05; let position_amplitude = 200e3; let torque_frequency = 4.; let torque_amplitude = 400.; let mut t = 0.;
println!("move");
loop {
cycle.notified().await;
let mut group = group.data().await;
group.set(target_position, (initial + (t*position_frequency*2.*std::f32::consts::PI).sin() * position_amplitude) as _);
group.set(offset_torque, ((t*torque_frequency*2.*std::f32::consts::PI).sin() * torque_amplitude) as _);
t += period;
}
},
async {
use bilge::prelude::u2;
let priority = u2::new(0);
println!("initial {} {} {}",
slave.coe().await.sdo_read(&kpp, priority).await.unwrap(),
slave.coe().await.sdo_read(&kvp, priority).await.unwrap(),
slave.coe().await.sdo_read(&kvi, priority).await.unwrap(),
);
let regulator_period = 10.;
loop {
println!("low");
slave.coe().await.sdo_write(&kvp, priority, 0.5).await.unwrap();
slave.coe().await.sdo_write(&kvp, priority, 0.6).await.unwrap();
slave.coe().await.sdo_write(&kvi, priority, 0.1).await.unwrap();
tokio::time::sleep(Duration::from_secs_f32(regulator_period)).await;
println!("med");
slave.coe().await.sdo_write(&kvp, priority, 1.).await.unwrap();
slave.coe().await.sdo_write(&kvp, priority, 2.).await.unwrap();
slave.coe().await.sdo_write(&kvi, priority, 0.1).await.unwrap();
tokio::time::sleep(Duration::from_secs_f32(regulator_period)).await;
println!("high");
slave.coe().await.sdo_write(&kvp, priority, 4.).await.unwrap();
slave.coe().await.sdo_write(&kvp, priority, 6.).await.unwrap();
slave.coe().await.sdo_write(&kvi, priority, 1.).await.unwrap();
tokio::time::sleep(Duration::from_secs_f32(regulator_period)).await;
}
},
).race().await;
Ok(())
}
pub async fn switch_on(
statusword: Field<sdo::StatusWord>,
controlword: Field<sdo::ControlWord>,
error: Field<u16>,
group: &Group<'_>,
cycle: &tokio::sync::Notify,
) {
loop {
{
let mut group = group.data().await;
let status = group.get(statusword);
let mut control = sdo::ControlWord::default();
if status.operation_enabled() {
return;
}
else if status.fault() && ! status.switched_on() {
control.set_reset_fault(true);
}
else if ! status.quick_stop() {
control.set_quick_stop(true);
control.set_enable_voltage(true);
}
else if status.switch_on_disabled() {
control.set_quick_stop(true);
control.set_enable_voltage(true);
control.set_switch_on(false);
}
else if ! status.switched_on() && status.ready_switch_on() {
control.set_quick_stop(true);
control.set_enable_voltage(true);
control.set_switch_on(true);
control.set_enable_operation(false);
}
else if ! status.operation_enabled() && (status.ready_switch_on() | status.switched_on()) {
control.set_quick_stop(true);
control.set_enable_voltage(true);
control.set_switch_on(true);
control.set_enable_operation(true);
}
group.set(controlword, control);
println!("switch on {} {} {:#x}", status, control, group.get(error));
}
cycle.notified().await;
}
}