Skip to content

Commit 491adba

Browse files
committed
Merge branch 'mapping'
2 parents edba5b9 + 2338b9c commit 491adba

File tree

13 files changed

+332
-106
lines changed

13 files changed

+332
-106
lines changed

examples/sdo_mapping.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ async fn main() -> std::io::Result<()> {
2929
let mut slave = mapping.slave(1);
3030
let restatus = slave.register(SyncDirection::Read, registers::al::status);
3131
let mut channel = slave.channel(sdo::SyncChannel{ index: 0x1c12, direction: SyncDirection::Write, capacity: 10 });
32-
let mut pdo = channel.push(sdo::Pdo::with_capacity(0x1600, 10));
32+
let mut pdo = channel.push(sdo::Pdo::with_capacity(0x1600, false, 10));
3333
let control = pdo.push(Sdo::<u16>::complete(0x6040));
3434
let mut channel = slave.channel(sdo::SyncChannel{ index: 0x1c13, direction: SyncDirection::Read, capacity: 10 });
35-
let mut pdo = channel.push(sdo::Pdo::with_capacity(0x1a00, 10));
35+
let mut pdo = channel.push(sdo::Pdo::with_capacity(0x1a00, false, 10));
3636
let status = pdo.push(Sdo::<u16>::complete(0x6041));
3737
let error = pdo.push(Sdo::<u16>::complete(0x603f));
3838
let position = pdo.push(Sdo::<i32>::complete(0x6064));

examples/servodrive_run.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,22 @@ async fn main() -> std::io::Result<()> {
2323
std::thread::spawn(move || loop {
2424
master.send();
2525
})};
26-
std::thread::sleep(std::time::Duration::from_millis(500));
26+
std::thread::sleep(Duration::from_millis(500));
2727

2828
println!("create mapping");
2929
let config = mapping::Config::default();
3030
let mapping = Mapping::new(&config);
3131
let mut slave = mapping.slave(1);
3232
let _statuscom = slave.register(SyncDirection::Read, registers::al::status);
3333
let mut channel = slave.channel(sdo::SyncChannel{ index: 0x1c12, direction: SyncDirection::Write, capacity: 10 });
34-
let mut pdo = channel.push(sdo::Pdo::with_capacity(0x1600, 10));
34+
let mut pdo = channel.push(sdo::Pdo::with_capacity(0x1600, false, 10));
3535
let controlword = pdo.push(sdo::cia402::controlword);
3636
let target_mode = pdo.push(sdo::cia402::target::mode);
3737
let target_position = pdo.push(sdo::cia402::target::position);
3838
let _target_velocity = pdo.push(sdo::cia402::target::velocity);
3939
let _target_torque = pdo.push(sdo::cia402::target::torque);
4040
let mut channel = slave.channel(sdo::SyncChannel{ index: 0x1c13, direction: SyncDirection::Read, capacity: 10 });
41-
let mut pdo = channel.push(sdo::Pdo::with_capacity(0x1a00, 10));
41+
let mut pdo = channel.push(sdo::Pdo::with_capacity(0x1a00, false, 10));
4242
let statusword = pdo.push(sdo::cia402::statusword);
4343
let error = pdo.push(sdo::cia402::error);
4444
let _current_mode = pdo.push(sdo::cia402::current::mode);

examples/slaves_discovery.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ async fn main() -> std::io::Result<()> {
2222
std::thread::spawn(move || loop {
2323
unsafe {master.get_raw()}.send();
2424
})};
25-
std::thread::sleep(std::time::Duration::from_millis(500));
25+
std::thread::sleep(Duration::from_millis(500));
2626

2727
master.reset_addresses().await;
2828

src/mailbox.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ impl<'b> Mailbox<'b> {
177177
break
178178
}
179179
// write data
180+
// TODO: retry this solution with writing the last word instead of the last byte
180181
// we are forced to write the whole buffer (even if much bigger than data) because the slave will notice the data sent only if writing the complete buffer
181182
// and writing the last byte instead does not work trick it.
182183
// if mailbox_size - data.len() > 32 {

src/mapping.rs

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ use crate::{
5858
rawmaster::{RawMaster, PduCommand, SlaveAddress},
5959
data::{PduData, Field},
6060
sdo::{self, Sdo, SyncDirection},
61-
slave::Slave,
61+
slave::{Slave, CommunicationState},
6262
registers,
6363
};
6464
use core::{
@@ -143,11 +143,17 @@ impl Allocator {
143143
master,
144144
allocator: self,
145145
allocated: slot.size,
146-
config: slaves,
147146
offset: slot.position,
148147
size,
149-
read: vec![0; size as usize],
150-
write: vec![0; size as usize],
148+
149+
config: slaves,
150+
data: tokio::sync::Mutex::new(GroupData {
151+
master,
152+
offset: slot.position,
153+
size,
154+
read: vec![0; size as usize],
155+
write: vec![0; size as usize],
156+
}),
151157
}
152158
}
153159
/// check that a given mapping is compatible with the already configured slaves in the allocator
@@ -209,9 +215,15 @@ pub struct Group<'a> {
209215
master: &'a RawMaster,
210216
allocator: &'a Allocator,
211217
allocated: u32,
218+
offset: u32,
219+
size: u32,
212220

213221
/// configuration per slave
214222
config: HashMap<u16, Arc<ConfigSlave>>,
223+
data: tokio::sync::Mutex<GroupData<'a>>,
224+
}
225+
pub struct GroupData<'a> {
226+
master: &'a RawMaster,
215227
/// byte offset of this data group in the logical memory
216228
offset: u32,
217229
/// byte size of this data group in the logical memory
@@ -221,14 +233,14 @@ pub struct Group<'a> {
221233
/// data duplex: data to write to slave
222234
write: Vec<u8>,
223235
}
224-
impl Group<'_> {
236+
impl<'a> Group<'a> {
225237
pub fn contains(&self, slave: u16) -> bool {
226238
self.config.contains_key(&slave)
227239
}
228240
/**
229241
write on the given slave the matching configuration from the mapping
230242
231-
the slave is assumed to be in state [PreOperational](crate::CommunicationState::PreOperational), and can be switched to [SafeOperational](crate::CommunicationState::SafeOperational) after this step.
243+
the slave is assumed to be in state [PreOperational](CommunicationState::PreOperational), and can be switched to [SafeOperational](crate::CommunicationState::SafeOperational) after this step.
232244
*/
233245
pub async fn configure(&self, slave: &Slave<'_>) {
234246
let master = unsafe{ slave.raw_master() };
@@ -238,39 +250,59 @@ impl Group<'_> {
238250
};
239251
let config = &self.config[&address];
240252

253+
assert_eq!(slave.expected(), CommunicationState::PreOperational);
254+
241255
// range of physical memory to be mapped
242256
let physical = SLAVE_PHYSICAL_MAPPABLE;
243257

244258
let mut coe = slave.coe().await;
259+
let priority = u2::new(1);
245260

246261
// PDO mapping
247262
for pdo in config.pdos.values() {
248-
// pdo size must be set to zero before assigning items
249-
coe.sdo_write(&pdo.config.len(), u2::new(0), 0).await;
250-
for (i, sdo) in pdo.sdos.iter().enumerate() {
251-
// PDO mapping
252-
coe.sdo_write(&pdo.config.item(i), u2::new(0), sdo::PdoEntry::new(
253-
sdo.field.len.try_into().expect("field too big for a subitem"),
254-
sdo.sub.unwrap(),
255-
sdo.index,
256-
)).await;
263+
if pdo.config.fixed {
264+
// check that current sdo values are the requested ones
265+
for (i, sdo) in pdo.sdos.iter().enumerate() {
266+
assert_eq!(
267+
coe.sdo_read(&pdo.config.item(i), priority).await,
268+
sdo::PdoEntry::new(
269+
sdo.field.len.try_into().expect("field too big for a subitem"),
270+
sdo.sub.unwrap(),
271+
sdo.index,
272+
),
273+
"slave {} fixed pdo {}", address, pdo.config.item(i));
274+
}
275+
}
276+
else {
277+
// TODO: send as a complete SDO rather than subitems
278+
// pdo size must be set to zero before assigning items
279+
coe.sdo_write(&pdo.config.len(), priority, 0).await;
280+
for (i, sdo) in pdo.sdos.iter().enumerate() {
281+
// PDO mapping
282+
coe.sdo_write(&pdo.config.item(i), priority, sdo::PdoEntry::new(
283+
sdo.field.len.try_into().expect("field too big for a subitem"),
284+
sdo.sub.unwrap(),
285+
sdo.index,
286+
)).await;
287+
}
288+
coe.sdo_write(&pdo.config.len(), priority, pdo.sdos.len() as u8).await;
257289
}
258-
coe.sdo_write(&pdo.config.len(), u2::new(0), pdo.sdos.len() as u8).await;
259290
}
260291

261292
// sync mapping
262293
for channel in config.channels.values() {
263294

264295
let mut size = 0;
296+
// TODO: send as a complete SDO rather than subitems
265297
// channel size must be set to zero before assigning items
266-
coe.sdo_write(&channel.config.len(), u2::new(0), 0).await;
298+
coe.sdo_write(&channel.config.len(), priority, 0).await;
267299
for (j, &pdo) in channel.pdos.iter().enumerate() {
268-
coe.sdo_write(&channel.config.slot(j as u8), u2::new(0), pdo).await;
300+
coe.sdo_write(&channel.config.slot(j as u8), priority, pdo).await;
269301
size += config.pdos[&pdo].sdos.iter()
270302
.map(|sdo| (sdo.field.len / 8) as u16)
271303
.sum::<u16>();
272304
}
273-
coe.sdo_write(&channel.config.len(), u2::new(0), channel.pdos.len() as u8).await;
305+
coe.sdo_write(&channel.config.len(), priority, channel.pdos.len() as u8).await;
274306

275307
// enable sync channel
276308
master.fpwr(address, channel.config.register(), {
@@ -307,6 +339,12 @@ impl Group<'_> {
307339
}).await.one();
308340
}
309341
}
342+
/// obtain exclusive access (mutex) to the data buffers
343+
pub async fn data(&self) -> tokio::sync::MutexGuard<GroupData<'a>> {
344+
self.data.lock().await
345+
}
346+
}
347+
impl<'a> GroupData<'a> {
310348
/// read and write relevant data from master to segment
311349
pub async fn exchange(&mut self) -> &'_ mut [u8] {
312350
// TODO: add a fallback implementation in case the slave does not support *RW commands
@@ -347,6 +385,8 @@ impl fmt::Debug for Group<'_> {
347385
}
348386

349387

388+
389+
350390
/// struct holding configuration informations for multiple slaves, that can be shared between multiple mappings
351391
#[derive(Clone, Default, Debug, Eq, PartialEq)]
352392
pub struct Config {

src/master.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
socket::EthercatSocket,
33
rawmaster::{RawMaster, SlaveAddress, PduAnswer},
44
data::{PduData, Field},
5-
slave::Slave,
5+
slave::{Slave, CommunicationState},
66
mapping::{Allocator, Mapping, Group},
77
registers,
88
};
@@ -109,7 +109,7 @@ impl Master {
109109
110110
the change will be effective on every slave on this function return, however [Slave::expect] will need to be called in order to convert salve instances to their proper state
111111
*/
112-
pub async fn switch(&self, target: MixedState) {
112+
pub async fn switch(&self, target: CommunicationState) {
113113
self.raw.bwr(registers::al::control, {
114114
let mut config = registers::AlControlRequest::default();
115115
config.set_state(target.into());

src/rawmaster.rs

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ use crate::{
2121
};
2222

2323

24-
/// maximum frame size, currently limited to the size tolerated by its header (content size coded with 11 bits)
25-
const MAX_ETHERCAT_FRAME: usize = 2050;
24+
/// maximum frame size, currently limited to the ethernet frame size
25+
// size tolerated by its header (content size coded with 11 bits)
26+
// const MAX_ETHERCAT_FRAME: usize = 2050;
27+
// size tolerated by ethernet encapsulation
28+
const MAX_ETHERCAT_FRAME: usize = 1500;
2629
/// minimum PDU size
2730
const MIN_PDU: usize = 60;
2831
/// maximum number of PDU in an ethercat frame
@@ -64,8 +67,7 @@ pub struct RawMaster {
6467
// synchronization signal for multitask reception
6568
received: Notify,
6669
sendable: Condvar,
67-
send: Condvar,
68-
sent: Condvar,
70+
sent: Notify,
6971

7072
// communication state
7173
// states are locked using [std::sync::Mutex] since it is recommended by async-io
@@ -77,7 +79,7 @@ pub struct RawMaster {
7779
struct PduState {
7880
last_start: usize,
7981
last_end: usize,
80-
last_time: Instant,
82+
ready: bool,
8183
/// send buffer, it contains one ethercat frame
8284
send: [u8; MAX_ETHERCAT_FRAME],
8385
/// reception destination, each containing a reception buffer and additional infos
@@ -99,13 +101,12 @@ impl RawMaster {
99101
socket: Box::new(socket),
100102
received: Notify::new(),
101103
sendable: Condvar::new(),
102-
send: Condvar::new(),
103-
sent: Condvar::new(),
104+
sent: Notify::new(),
104105

105106
pdu_state: Mutex::new(PduState {
106107
last_start: EthercatHeader::packed_size(),
107108
last_end: 0,
108-
last_time: Instant::now(),
109+
ready: false,
109110
send: [0; MAX_ETHERCAT_FRAME],
110111
receive: [0; 2*MAX_ETHERCAT_PDU].map(|_| None),
111112
free: (0 .. 2*MAX_ETHERCAT_PDU).collect(),
@@ -257,17 +258,25 @@ impl RawMaster {
257258
let mut state = self.pdu_state.lock().unwrap();
258259

259260
while state.free.is_empty() {
260-
println!("no more token, waiting");
261+
let notification = self.received.notified();
261262
drop(state);
262-
self.received.notified().await;
263+
notification.await;
263264
state = self.pdu_state.lock().unwrap();
264265
}
265266

266267
// sending the buffer if necessary
267-
while MAX_ETHERCAT_FRAME < state.last_end + data.len() + PduHeader::packed_size() + PduFooter::packed_size() {
268-
println!("no more space, waiting");
269-
self.send.notify_one();
270-
state = self.sent.wait(state).unwrap();
268+
while self.socket.max_frame() < state.last_end + data.len() + PduHeader::packed_size() + PduFooter::packed_size() {
269+
assert!(self.socket.max_frame() >
270+
EthercatHeader::packed_size()
271+
+ data.len()
272+
+ PduHeader::packed_size()
273+
+ PduFooter::packed_size(), "data too big for an ethercat frame");
274+
state.ready = true;
275+
self.sendable.notify_one();
276+
let notification = self.sent.notified();
277+
drop(state);
278+
notification.await;
279+
state = self.pdu_state.lock().unwrap();
271280
}
272281

273282
// reserving a token number to ensure no other task will exchange a PDU with the same token and receive our data
@@ -292,7 +301,6 @@ impl RawMaster {
292301
header.pack(place).unwrap();
293302
}
294303
else {
295-
state.last_time = Instant::now();
296304
state.last_end = state.last_start;
297305
}
298306

@@ -331,14 +339,23 @@ impl RawMaster {
331339
};
332340

333341
// waiting for the answer
334-
while ! *ready {
335-
self.received.notified().await;
342+
loop {
343+
let notification = self.received.notified();
344+
if *ready {break}
345+
notification.await;
336346
}
337347

338348
let state = self.pdu_state.lock().unwrap();
339349
state.receive[token].as_ref().unwrap().answers
340350
}
341351

352+
/// trigger sending the buffered PDUs, they will be sent as soon as possible by [Self::send] instead of waiting for the frame to be full or for the timeout
353+
fn flush(&self) {
354+
let mut state = self.pdu_state.lock().unwrap();
355+
state.ready = true;
356+
self.sendable.notify_one();
357+
}
358+
342359
/// extract a received frame of PDUs and buffer each for reception by an eventual `self.pdu()` future waiting for it.
343360
fn pdu_receive(&self, state: &mut PduState, frame: &[u8]) {
344361
let mut frame = Cursor::new(frame);
@@ -390,9 +407,17 @@ impl RawMaster {
390407
}
391408
/// this is the socket sending handler
392409
pub fn send(&self) {
393-
let state = self.pdu_state.lock().unwrap();
394-
let state = self.sendable.wait(state).unwrap();
395-
let mut state = self.send.wait_timeout(state, self.pdu_merge_time).unwrap().0;
410+
let mut state = self.pdu_state.lock().unwrap();
411+
// wait indefinitely if no data to send
412+
while state.last_end == 0
413+
{state = self.sendable.wait(state).unwrap();}
414+
// wait for more data until a timeout once data is present
415+
if ! state.ready
416+
{state = self.sendable.wait_timeout_while(
417+
state,
418+
self.pdu_merge_time,
419+
|state| ! state.ready,
420+
).unwrap().0;}
396421

397422
// check header
398423
EthercatHeader::new(
@@ -405,9 +430,10 @@ impl RawMaster {
405430
// we assume it is not a long operation to copy those data into the system buffer
406431
self.socket.send(&state.send[.. state.last_end]).unwrap();
407432
// reset state
433+
state.ready = false;
408434
state.last_end = 0;
409435
state.last_start = EthercatHeader::packed_size();
410-
drop(state);
436+
self.sent.notify_waiters();
411437
}
412438
}
413439

0 commit comments

Comments
 (0)