1use crate::{
4 mailbox::{Mailbox, MailboxType, MailboxError, MailboxHeader},
5 sdo::Sdo,
6 data::{self, PduData, Storage, Cursor},
7 error::{EthercatError, EthercatResult},
8 };
9use bilge::prelude::*;
10use tokio::sync::Mutex;
11use std::sync::Arc;
12
13
14
15const MAILBOX_MAX_SIZE: usize = 1024;
16const EXPEDITED_MAX_SIZE: usize = 4;
18const SDO_SEGMENT_MAX_SIZE: usize = MAILBOX_MAX_SIZE
21 - <MailboxHeader as PduData>::Packed::LEN
22 - <CoeHeader as PduData>::Packed::LEN
23 - <SdoSegmentHeader as PduData>::Packed::LEN;
24
25pub struct Can {
51 mailbox: Arc<Mutex<Mailbox>>,
52}
53impl Can {
54 pub fn new(mailbox: Arc<Mutex<Mailbox>>) -> Can {
55 Can {mailbox}
56 }
57 pub async fn sdo_read<T: PduData>(&mut self, sdo: &Sdo<T>, priority: u2)
59 -> EthercatResult<T, CanError>
60 {
61 let mut data = T::Packed::uninit();
62 Ok(T::unpack(self.sdo_read_slice(&sdo.downcast(), priority, data.as_mut()).await?)?)
63 }
64
65 pub async fn sdo_read_slice<'b>(&mut self, sdo: &Sdo, priority: u2, data: &'b mut [u8])
66 -> EthercatResult<&'b mut [u8], CanError>
67 {
68 let mut mailbox = self.mailbox.lock().await;
69 let mut buffer = [0; MAILBOX_MAX_SIZE];
70
71 let mut frame = Cursor::new(buffer.as_mut_slice());
73 frame.pack(&CoeHeader::new(u9::new(0), CanService::SdoRequest)).unwrap();
74 frame.pack(&SdoHeader::new(
75 false, false, u2::new(0), sdo.sub.is_complete(),
79 u3::from(SdoCommandRequest::Upload),
80 sdo.index,
81 sdo.sub.unwrap(),
82 )).unwrap();
83 frame.write(&[0; 4]).unwrap();
84 mailbox.write(MailboxType::Can, priority, frame.finish()).await?;
85
86 let (header, frame) = Self::receive_sdo_response(
88 &mut mailbox,
89 &mut buffer,
90 SdoCommandResponse::Upload,
91 sdo,
92 ).await?;
93 if ! header.sized()
94 {return Err(Error::Protocol("got SDO response without data size"))}
95
96
97 if header.expedited() {
98 let total = EXPEDITED_MAX_SIZE - u8::from(header.size()) as usize;
100 if total > data.len()
101 {return Err(Error::Master("data buffer is too small for requested SDO"))}
102 data[.. total].copy_from_slice(Cursor::new(frame)
103 .read(total)
104 .map_err(|_| Error::Protocol("inconsistent expedited response data size"))?
105 );
106 Ok(&mut data[.. total])
107 }
108 else {
109 let mut frame = Cursor::new(frame);
111 let total = frame.unpack::<u32>()
112 .map_err(|_| Error::Protocol("unable unpack sdo size from SDO response"))?
113 .try_into().expect("SDO is too big for master memory");
114 if total > data.len()
115 {return Err(Error::Master("read buffer is too small for requested SDO"))}
116
117 let mut received = Cursor::new(&mut data.as_mut()[.. total]);
118 let mut toggle = false;
119 received.write(frame.remain())
120 .map_err(|_| Error::Protocol("received more data than declared from SDO"))?;
121
122 while received.remain().len() != 0 {
125 {
127 let mut frame = Cursor::new(buffer.as_mut_slice());
128 frame.pack(&CoeHeader::new(u9::new(0), CanService::SdoRequest)).unwrap();
129 frame.pack(&SdoSegmentHeader::new(
130 false,
131 u3::new(0),
132 toggle,
133 u3::from(SdoCommandRequest::UploadSegment),
134 )).unwrap();
135 frame.write(&[0; 7]).unwrap();
136 mailbox.write(MailboxType::Can, priority, frame.finish()).await?;
137 }
138
139 {
141 let (header, segment) = Self::receive_sdo_segment(
142 &mut mailbox,
143 &mut buffer,
144 SdoCommandResponse::UploadSegment,
145 toggle,
146 ).await?;
147 let segment = &segment[.. received.remain().len()];
148 received.write(segment)
149 .map_err(|_| Error::Protocol("received more data than declared from SDO"))?;
150
151 if ! header.more () {break}
152 }
153
154 toggle = ! toggle;
155 }
156 Ok(received.finish())
157 }
158
159 }
161 pub async fn sdo_write<T: PduData>(&mut self, sdo: &Sdo<T>, priority: u2, data: T)
163 -> EthercatResult<(), CanError>
164 {
165 let mut packed = T::Packed::uninit();
166 data.pack(packed.as_mut())
167 .expect("unable to pack data for sending");
168 self.sdo_write_slice(&sdo.downcast(), priority, packed.as_ref()).await
169 }
170 pub async fn sdo_write_slice(&mut self, sdo: &Sdo, priority: u2, data: &[u8])
171 -> EthercatResult<(), CanError>
172 {
173 let mut mailbox = self.mailbox.lock().await;
174 let mut buffer = [0; MAILBOX_MAX_SIZE];
175 if data.len() <= EXPEDITED_MAX_SIZE {
176 {
179 let mut frame = Cursor::new(buffer.as_mut_slice());
180 frame.pack(&CoeHeader::new(u9::new(0), CanService::SdoRequest)).unwrap();
181 frame.pack(&SdoHeader::new(
182 true,
183 true,
184 u2::new((EXPEDITED_MAX_SIZE - data.len()) as u8),
185 sdo.sub.is_complete(),
186 u3::from(SdoCommandRequest::Download),
187 sdo.index,
188 sdo.sub.unwrap(),
189 )).unwrap();
190 frame.write(data).unwrap();
191 frame.write(&[0; 4][data.len() ..]).unwrap();
192 mailbox.write(MailboxType::Can, priority, frame.finish()).await?;
193 }
194
195 Self::receive_sdo_response(
197 &mut mailbox,
198 &mut buffer,
199 SdoCommandResponse::Download,
200 sdo,
201 ).await?;
202 }
203 else {
204 let mut data = Cursor::new(data.as_ref());
206
207 {
209 let mut frame = Cursor::new(buffer.as_mut_slice());
210 frame.pack(&CoeHeader::new(u9::new(0), CanService::SdoRequest)).unwrap();
211 frame.pack(&SdoHeader::new(
212 true,
213 false,
214 u2::new(0),
215 sdo.sub.is_complete(),
216 u3::from(SdoCommandRequest::Download),
217 sdo.index,
218 sdo.sub.unwrap(),
219 )).unwrap();
220 frame.pack(&(data.remain().len() as u32)).unwrap();
221 let segment = data.remain().len().min(SDO_SEGMENT_MAX_SIZE);
222 frame.write(data.read(segment).unwrap()).unwrap();
223 mailbox.write(MailboxType::Can, priority, frame.finish()).await?;
224 }
225
226 Self::receive_sdo_response(
228 &mut mailbox,
229 &mut buffer,
230 SdoCommandResponse::Download,
231 sdo,
232 ).await?;
233
234 let mut toggle = false;
236 while data.remain().len() != 0 {
237 {
239 let segment = data.remain().len().min(SDO_SEGMENT_MAX_SIZE);
240 let mut frame = Cursor::new(buffer.as_mut_slice());
241 frame.pack(&CoeHeader::new(u9::new(0), CanService::SdoRequest)).unwrap();
242 frame.pack(&SdoSegmentHeader::new(
243 data.remain().len() != 0,
244 u3::new(0),
245 toggle,
246 u3::from(SdoCommandRequest::DownloadSegment),
247 )).unwrap();
248 frame.write(data.read(segment).unwrap()).unwrap();
249 mailbox.write(MailboxType::Can, priority, frame.finish()).await?;
250 }
251
252 Self::receive_sdo_segment(
254 &mut mailbox,
255 &mut buffer,
256 SdoCommandResponse::DownloadSegment,
257 toggle,
258 ).await?;
259 toggle = !toggle;
260 }
261 }
262 Ok(())
263
264 }
266
267 async fn receive_sdo_response<'b, T: PduData>(
269 mailbox: &mut Mailbox,
270 buffer: &'b mut [u8],
271 expected: SdoCommandResponse,
272 sdo: &Sdo<T>,
273 ) -> EthercatResult<(SdoHeader, &'b [u8]), CanError>
274 {
275 let mut frame = Cursor::new(mailbox.read(MailboxType::Can, buffer).await?);
276
277 let check_header = |header: SdoHeader| {
278 if header.index() != sdo.index {return Err(Error::Protocol("slave answered about wrong item"))}
279 if header.sub() != sdo.sub.unwrap() {return Err(Error::Protocol("slave answered about wrong subitem"))}
280 Ok(())
281 };
282
283 match frame.unpack::<CoeHeader>()
284 .map_err(|_| Error::Protocol("unable to unpack COE frame header"))?
285 .service()
286 {
287 CanService::SdoResponse => {
288 let header = frame.unpack::<SdoHeader>()
289 .map_err(|_| Error::Protocol("unable to unpack SDO response header"))?;
290 if SdoCommandResponse::try_from(header.command()) != Ok(expected)
291 {return Err(Error::Protocol("slave answered with wrong operation"))}
292 check_header(header)?;
293 Ok((header, frame.remain()))
294 },
295 CanService::SdoRequest => {
296 let header = frame.unpack::<SdoHeader>()
297 .map_err(|_| Error::Protocol("unable to unpack SDO request header"))?;
298 if SdoCommandRequest::try_from(header.command()) != Ok(SdoCommandRequest::Abort)
299 {return Err(Error::Protocol("slave answered a COE request"))}
300 check_header(header)?;
301 let error = frame.unpack::<SdoAbortCode>()
302 .map_err(|_| Error::Protocol("unable to unpack SDO error code"))?;
303 Err(Error::Slave(mailbox.slave(), CanError::Sdo(error)))
304 },
305 _ => {return Err(Error::Protocol("unexpected COE service during SDO operation"))},
306 }
307 }
308
309 async fn receive_sdo_segment<'b>(
310 mailbox: &mut Mailbox,
311 buffer: &'b mut [u8],
312 expected: SdoCommandResponse,
313 toggle: bool,
314 ) -> EthercatResult<(SdoSegmentHeader, &'b [u8]), CanError>
315 {
316 let mut frame = Cursor::new(mailbox.read(MailboxType::Can, buffer).await?);
317
318 match frame.unpack::<CoeHeader>()
319 .map_err(|_| Error::Protocol("unable to unpack COE frame header"))?
320 .service()
321 {
322 CanService::SdoResponse => {
323 let header = frame.unpack::<SdoSegmentHeader>()
324 .map_err(|_| Error::Protocol("unable to unpack segment response header"))?;
325 if SdoCommandResponse::try_from(header.command()) != Ok(expected)
326 {return Err(Error::Protocol("slave answered with a COE request"))}
327 if header.toggle() != toggle
328 {return Err(Error::Protocol("bad toggle bit in segment received"))}
329 Ok((header, frame.remain()))
330 },
331 CanService::SdoRequest => {
332 let header = frame.unpack::<SdoHeader>()
333 .map_err(|_| Error::Protocol("unable to unpack request header"))?;
334 if SdoCommandRequest::try_from(header.command()) != Ok(SdoCommandRequest::Abort)
335 {return Err(Error::Protocol("slave answered a COE request"))}
336 let error = frame.unpack::<SdoAbortCode>()
337 .map_err(|_| Error::Protocol("unable to unpack SDO error code"))?;
338 Err(Error::Slave(mailbox.slave(), CanError::Sdo(error)))
339 },
340 _ => {return Err(Error::Protocol("unexpected COE service during SDO segment operation"))},
341 }
342 }
343
344 pub fn pdo_read() {todo!()}
345 pub fn pdo_write() {todo!()}
346
347 pub fn info_dictionnary() {todo!()}
348 pub fn info_sdo() {todo!()}
349 pub fn info_subitem() {todo!()}
350}
351
352
353
354#[bitsize(16)]
355#[derive(TryFromBits, DebugBits, Copy, Clone)]
356pub struct CoeHeader {
357 pub number: u9,
359 reserved: u3,
360 pub service: CanService,
362}
363data::bilge_pdudata!(CoeHeader, u16);
364
365#[bitsize(4)]
373#[derive(TryFromBits, Debug, Copy, Clone, Eq, PartialEq)]
374pub enum CanService {
375 Emergency = 0x1,
376 SdoRequest = 0x2,
377 SdoResponse = 0x3,
378 TransmitPdo = 0x4,
379 ReceivePdo = 0x5,
380 TransmitPdoRemoteRequest = 0x6,
381 ReceivePdoRemoteRequest = 0x7,
382 SdoInformation = 0x8,
383}
384data::bilge_pdudata!(CanService, u4);
385
386
387#[bitsize(32)]
416#[derive(TryFromBits, DebugBits, Copy, Clone)]
417pub struct SdoHeader {
418 pub sized: bool,
420 pub expedited: bool,
422 pub size: u2,
425 pub complete: bool,
427 pub command: u3,
429 pub index: u16,
431 pub sub: u8,
438}
439data::bilge_pdudata!(SdoHeader, u32);
440
441#[bitsize(8)]
442#[derive(TryFromBits, DebugBits, Copy, Clone)]
443pub struct SdoSegmentHeader {
444 pub more: bool,
445 pub size: u3,
446 pub toggle: bool,
447 pub command: u3,
448}
449data::bilge_pdudata!(SdoSegmentHeader, u8);
450
451#[bitsize(3)]
455#[derive(TryFromBits, Debug, Copy, Clone, Eq, PartialEq)]
456pub enum SdoCommandRequest {
457 Download = 0x1,
458 DownloadSegment = 0x0,
459 Upload = 0x2,
460 UploadSegment = 0x3,
461 Abort = 0x4,
462}
463data::bilge_pdudata!(SdoCommandRequest, u3);
464
465#[bitsize(3)]
469#[derive(TryFromBits, Debug, Copy, Clone, Eq, PartialEq)]
470pub enum SdoCommandResponse {
471 Download = 0x3,
472 DownloadSegment = 0x1,
473 Upload = 0x2,
474 UploadSegment = 0x0,
475 Abort = 0x4,
476}
477data::bilge_pdudata!(SdoCommandResponse, u3);
478
479#[bitsize(32)]
480#[derive(TryFromBits, Debug, Copy, Clone, Eq, PartialEq)]
481pub enum SdoAbortCode {
482 BadToggle = 0x05_03_00_00,
484 Timeout = 0x05_04_00_00,
486 UnsupportedCommand = 0x05_04_00_01,
488 OufOfMemory = 0x05_04_00_05,
490 UnsupportedAccess = 0x06_01_00_00,
492 WriteOnly = 0x06_01_00_01,
494 ReadOnly = 0x06_01_00_02,
496 WriteError = 0x06_01_00_03,
498 VariableLength = 0x06_01_00_04,
500 ObjectTooBig = 0x06_01_00_05,
502 LockedByPdo = 0x06_01_00_06,
504 InvalidIndex = 0x06_02_00_00,
506 CannotMap = 0x06_04_00_41,
508 PdoTooSmall = 0x06_04_00_42,
510 IncompatibleParameter = 0x06_04_00_43,
512 IncompatibleDevice = 0x06_04_00_47,
514 HardwareError = 0x06_06_00_00,
516 InvalidLength = 0x06_07_00_10,
518 ServiceTooBig = 0x06_07_00_12,
520 ServiceTooSmall = 0x06_07_00_13,
522 InvalidSubIndex = 0x06_09_00_11,
524 ValueOutOfRange = 0x06_09_00_30,
526 ValueTooHigh = 0x06_09_00_31,
528 ValueTooLow = 0x06_09_00_32,
530 InvalidRange = 0x06_09_00_36,
532 GeneralError = 0x08_00_00_00,
534 Refused = 0x08_00_00_20,
540 ApplicationRefused = 0x08_00_00_21,
547 StateRefused = 0x08_00_00_22,
553 DictionnaryEmpty = 0x08_00_00_23,
555}
556data::bilge_pdudata!(SdoAbortCode, u32);
557
558impl SdoAbortCode {
559 pub fn object_related(self) -> bool {u32::from(self) >> 24 == 0x06}
560 pub fn subitem_related(self) -> bool {u32::from(self) >> 16 == 0x06_09}
561 pub fn mapping_related(self) -> bool {u32::from(self) >> 16 == 0x06_04}
562 pub fn device_related(self) -> bool {u32::from(self) >> 24 == 0x08}
563 pub fn protocol_related(self) -> bool {u32::from(self) >> 24 == 0x05}
564}
565
566type Error = EthercatError<CanError>;
568
569#[derive(Copy, Clone, Debug, Eq, PartialEq)]
570pub enum CanError {
571 Mailbox(MailboxError),
572 Sdo(SdoAbortCode),
573}
575impl From<MailboxError> for CanError {
576 fn from(src: MailboxError) -> Self {CanError::Mailbox(src)}
577}
578impl From<EthercatError<MailboxError>> for EthercatError<CanError> {
579 fn from(src: EthercatError<MailboxError>) -> Self {src.into()}
580}
581impl From<EthercatError<()>> for EthercatError<CanError> {
582 fn from(src: EthercatError<()>) -> Self {src.upgrade()}
583}
584