1use std::num::TryFromIntError;
23
24use binrw::{binrw, BinRead, BinWrite};
25use socketcan::{EmbeddedFrame, Frame, Id, Socket};
26
27pub mod enums;
28
29trait FrameRW {
30 fn encode(&self, frame: &mut socketcan::CanFrame);
31 fn decode(frame: &socketcan::CanFrame) -> Result<Self, CanOpenError>
32 where
33 Self: Sized;
34}
35
36fn id_as_raw_std(frame: &socketcan::CanFrame) -> Result<u16, CanOpenError> {
41 if let Id::Standard(sid) = frame.id() {
42 Ok(sid.as_raw())
43 } else {
44 Err(CanOpenError::CanVersion(
45 "got extended (29bit) id, expected standard (11bit) id".to_owned(),
46 ))
47 }
48}
49
50fn u16_as_id(id: u16) -> socketcan::StandardId {
52 socketcan::StandardId::new(id).unwrap()
53}
54
55#[binrw]
56#[brw(little)]
57#[derive(Clone, Debug)]
58pub struct Nmt {
59 pub function: NmtFunction,
60 pub target_node: u8,
61}
62
63impl Nmt {
64 pub fn new(function: NmtFunction, target_node: u8) -> Self {
65 Self {
66 function,
67 target_node,
68 }
69 }
70}
71
72impl FrameRW for Nmt {
73 fn decode(frame: &socketcan::CanFrame) -> Result<Nmt, CanOpenError> {
74 let mut c = std::io::Cursor::new(frame.data());
75 Nmt::read(&mut c).map_err(|binrw_err| CanOpenError::ParseError(binrw_err.to_string()))
76 }
77
78 fn encode(&self, frame: &mut socketcan::CanFrame) {
79 frame.set_id(u16_as_id(0x000));
80 let mut c = std::io::Cursor::new(Vec::new());
81 self.write(&mut c).unwrap();
82 frame.set_data(c.get_ref()).unwrap();
83 }
84}
85
86#[binrw]
87#[br(repr(u8))]
88#[bw(repr(u8))]
89#[derive(Clone, Debug)]
90pub enum NmtFunction {
91 StartRemoteNode = 0x01,
92 StopRemoteNode = 0x02,
93 EnterPreOperational = 0x80,
94 ResetNode = 0x81,
95 ResetCommunication = 0x82,
96}
97
98#[binrw]
99#[brw(little)]
100#[derive(Debug)]
101pub struct Emergency {
102 #[brw(ignore)]
103 node_id: u8,
104
105 #[br(temp)]
106 #[bw(calc = enums::EmergencyErrorCode::encode(error_code))]
107 error_code_raw: u16,
108
109 #[br(try_calc = enums::EmergencyErrorCode::decode(error_code_raw))]
110 #[bw(ignore)]
111 error_code: enums::EmergencyErrorCode,
112
113 #[br(temp)]
114 #[bw(calc = enums::EmergencyErrorRegister::encode(error_register))]
115 error_register_raw: u8,
116
117 #[br(calc = enums::EmergencyErrorRegister::decode(error_register_raw))]
118 #[bw(ignore)]
119 error_register: Vec<enums::EmergencyErrorRegister>,
120
121 vendor_specific: [u8; 5],
122}
123
124impl Emergency {
125 pub fn new(
126 node_id: u8,
127 error_code: enums::EmergencyErrorCode,
128 error_register: Vec<enums::EmergencyErrorRegister>,
129 vendor_specific: &[u8],
130 ) -> Self {
131 Self {
132 node_id,
133 error_code,
134 error_register,
135 vendor_specific: Self::to_vendor_specific(vendor_specific),
136 }
137 }
138
139 fn to_vendor_specific(data: &[u8]) -> [u8; 5] {
140 let mut arr = [0u8; 5];
141 arr[0..data.len()].copy_from_slice(data);
142 arr
143 }
144}
145
146impl FrameRW for Emergency {
147 fn decode(frame: &socketcan::CanFrame) -> Result<Emergency, CanOpenError> {
148 let data = frame.data();
149 if data.len() < 8 {
150 return Err(CanOpenError::ParseError(
151 "not a valid Emergency message, need at least 8 bytes".to_owned(),
152 ));
153 }
154 let id = id_as_raw_std(frame)?;
155 Emergency::read(&mut std::io::Cursor::new(data))
156 .map_err(|e| CanOpenError::ParseError(format!("binrw err: {e}")))
157 .map(|mut m| {
158 m.node_id = (id - 0x080) as u8;
159 m
160 })
161 }
162
163 fn encode(&self, frame: &mut socketcan::CanFrame) {
164 frame.set_id(u16_as_id(0x80 + self.node_id as u16));
165 let mut c = std::io::Cursor::new(Vec::new());
166 self.write(&mut c).unwrap();
167 frame.set_data(c.get_ref()).unwrap();
168 }
169}
170
171#[derive(Clone, Debug)]
172pub struct Sdo {
173 pub node_id: u8, pub command: SdoCmd, pub reqres: ReqRes,
176}
177
178#[derive(Clone, Debug)]
179pub enum SdoCmd {
180 DownloadSegmentTx(SdoCmdDownloadSegmentTx),
181 InitiateDownloadTx(SdoCmdInitiateDownloadTx),
182 InitiateUploadTx(SdoCmdInitiateUploadTx),
183 UploadSegmentTx(SdoCmdUploadSegmentTx),
184 BlockUploadTx,
185 BlockDownloadTx,
186
187 DownloadSegmentRx(SdoCmdDownloadSegmentRx),
188 InitiateDownloadRx(SdoCmdInitiateDownloadRx),
189 InitiateUploadRx(SdoCmdInitiateUploadRx),
190 UploadSegmentRx(SdoCmdUploadSegmentRx),
191 BlockUploadRx,
192 BlockDownloadRx,
193
194 AbortTransfer(SdoCmdAbortTransfer),
195}
196
197impl SdoCmd {
198 #[allow(clippy::match_like_matches_macro)]
199 pub fn is_response_to(a: &Self, b: &Self) -> bool {
200 use SdoCmd::*;
201 match (a, b) {
202 (DownloadSegmentRx(_), DownloadSegmentTx(_)) => true,
203 (InitiateDownloadRx(_), InitiateDownloadTx(_)) => true,
204 (InitiateUploadRx(_), InitiateUploadTx(_)) => true,
205 (UploadSegmentRx(_), UploadSegmentTx(_)) => true,
206 (BlockUploadRx, BlockUploadTx) => true,
207 (BlockDownloadRx, BlockDownloadTx) => true,
208 _ => false,
209 }
210 }
211}
212
213#[derive(Clone, Debug)]
214pub struct SdoCmdInitiateDownloadRx {
215 pub index: u16,
216 pub sub_index: u8,
217 pub payload: SdoCmdInitiatePayload,
219}
220
221#[derive(Clone, Debug)]
222pub enum SdoCmdInitiatePayload {
223 Expedited(Box<[u8]>), Segmented(Option<u32>), }
227
228impl SdoCmdInitiatePayload {
229 fn encode(&self, frame: &mut socketcan::CanFrame) {
230 let mut data = [0u8; 8];
231 data.copy_from_slice(frame.data());
232 let mut command_byte = data[0];
233 match self {
234 SdoCmdInitiatePayload::Expedited(exp_data) => {
235 let l = match exp_data.len() {
236 1 => 0b11,
237 2 => 0b10,
238 3 => 0b01,
239 4 => 0b00,
240 _ => unreachable!(),
241 } << 2;
242 command_byte |= l;
243 command_byte |= 0b11;
244 data[4..4 + exp_data.len()].copy_from_slice(exp_data);
245 }
246 SdoCmdInitiatePayload::Segmented(Some(size)) => {
247 command_byte |= 0b01;
248 data[4..8].copy_from_slice(&size.to_le_bytes());
249 }
250 SdoCmdInitiatePayload::Segmented(None) => command_byte |= 0b00,
251 };
252 data[0] = command_byte;
253
254 frame.set_data(&data).unwrap();
255 }
256
257 fn decode(frame: &socketcan::CanFrame) -> Result<Self, CanOpenError> {
258 let size_indicated = frame.data()[0] & 0b1 != 0;
259 let expedited = frame.data()[0] & 0b10 != 0;
260 if expedited {
261 let l = if size_indicated {
263 match (frame.data()[0] & 0b1100) >> 2 {
264 0b11 => 1,
265 0b10 => 2,
266 0b01 => 3,
267 0b00 => 4,
268 _ => {
270 return Err(CanOpenError::ParseError(
271 "logic bug while decoding sdo".to_owned(),
272 ))
273 }
274 }
275 } else {
276 4
278 };
279
280 let mut data = Vec::with_capacity(l);
281 data.extend_from_slice(&frame.data()[4..4 + l]);
282 let payload = SdoCmdInitiatePayload::Expedited(data.into());
283 Ok(payload)
284 } else {
285 let size = if size_indicated {
286 let size = u32::from_le_bytes(frame.data()[4..8].try_into().unwrap());
287 Some(size)
288 } else {
289 None
290 };
291 Ok(SdoCmdInitiatePayload::Segmented(size))
292 }
293 }
294}
295
296impl SdoCmdInitiateDownloadRx {
297 fn encode(&self, frame: &mut socketcan::CanFrame) {
298 let mut data = [0u8; 8];
299 let command_byte = 0b00100000;
300
301 data[0] = command_byte;
302 data[1..3].copy_from_slice(&self.index.to_le_bytes());
303 data[3] = self.sub_index;
304 frame.set_data(&data).unwrap();
305
306 self.payload.encode(frame);
307 }
308
309 fn decode(frame: &socketcan::CanFrame) -> Result<Self, CanOpenError> {
310 let index = u16::from_le_bytes(
311 frame.data()[1..3]
312 .try_into()
313 .map_err(|_| CanOpenError::ParseError("not enough data".to_owned()))?,
314 );
315 let sub_index = frame.data()[3];
316 let payload = SdoCmdInitiatePayload::decode(frame)?;
317 Ok(Self {
318 index,
319 sub_index,
320 payload,
321 })
322 }
323}
324
325#[derive(Clone, Debug)]
326pub struct SdoCmdInitiateDownloadTx {
327 pub index: u16,
328 pub sub_index: u8,
329}
330
331impl SdoCmdInitiateDownloadTx {
332 fn encode(&self, frame: &mut socketcan::CanFrame) {
333 let mut data = [0u8; 8];
334 data[0] = 0b01100000;
335 data[1..3].copy_from_slice(&self.index.to_le_bytes());
336 data[3] = self.sub_index;
337 frame.set_data(&data).unwrap();
338 }
339
340 fn decode(frame: &socketcan::CanFrame) -> Result<Self, CanOpenError> {
341 let index = u16::from_le_bytes(
342 frame.data()[1..3]
343 .try_into()
344 .map_err(|_| CanOpenError::ParseError("not enough data".to_owned()))?,
345 );
346 let sub_index = frame.data()[3];
347
348 Ok(Self { index, sub_index })
349 }
350}
351
352#[derive(Clone, Debug)]
353pub struct SdoCmdDownloadSegmentRx {
354 pub toggle: bool,
355 pub data: Box<[u8]>,
356 pub last: bool,
357}
358
359impl SdoCmdDownloadSegmentRx {
360 fn encode(&self, frame: &mut socketcan::CanFrame) {
361 let mut data = [0u8; 8];
362 data[0] =
363 ((self.toggle as u8) << 4) | ((7 - self.data.len() as u8) << 1) | (self.last as u8);
364 data[1..1 + self.data.len()].copy_from_slice(&self.data);
365 frame.set_data(&data).unwrap();
366 }
367
368 fn decode(frame: &socketcan::CanFrame) -> Result<Self, CanOpenError> {
369 let command_byte = frame.data()[0];
370 let toggle = command_byte & 0b10000 != 0;
371 let size = 7 - (0b111 & (command_byte >> 1)) as usize;
372 let last = command_byte & 0b1 != 0;
373 let mut data = Vec::new();
374 data.extend_from_slice(&frame.data()[1..1 + size]);
375
376 Ok(Self {
377 toggle,
378 last,
379 data: data.into(),
380 })
381 }
382}
383
384#[derive(Clone, Debug)]
385pub struct SdoCmdDownloadSegmentTx {
386 pub toggle: bool,
387}
388
389impl SdoCmdDownloadSegmentTx {
390 fn encode(&self, frame: &mut socketcan::CanFrame) {
391 let mut data = [0u8; 8];
392 data[0] = 0b001 << 5 | ((self.toggle as u8) << 4);
393 frame.set_data(&data).unwrap();
394 }
395
396 fn decode(frame: &socketcan::CanFrame) -> Result<Self, CanOpenError> {
397 let command_byte = frame.data()[0];
398 let toggle = command_byte & 0b10000 != 0;
399 Ok(Self { toggle })
400 }
401}
402
403#[derive(Clone, Debug)]
404pub struct SdoCmdInitiateUploadRx {
405 pub index: u16,
406 pub sub_index: u8,
407}
408
409impl SdoCmdInitiateUploadRx {
410 fn encode(&self, frame: &mut socketcan::CanFrame) {
411 let mut data = [0u8; 8];
412 let command_byte = 0b010 << 5;
413 data[0] = command_byte;
414 data[1..3].copy_from_slice(&self.index.to_le_bytes());
415 data[3] = self.sub_index;
416 frame.set_data(&data).unwrap();
417 }
418
419 fn decode(frame: &socketcan::CanFrame) -> Result<Self, CanOpenError> {
420 let index = u16::from_le_bytes(
421 frame.data()[1..3]
422 .try_into()
423 .map_err(|_| CanOpenError::ParseError("not enough data".to_owned()))?,
424 );
425 let sub_index = frame.data()[3];
426 Ok(Self { index, sub_index })
427 }
428}
429
430#[derive(Clone, Debug)]
431pub struct SdoCmdInitiateUploadTx {
432 pub index: u16,
433 pub sub_index: u8,
434 pub payload: SdoCmdInitiatePayload,
435}
436
437impl SdoCmdInitiateUploadTx {
438 fn encode(&self, frame: &mut socketcan::CanFrame) {
439 let mut data = [0u8; 8];
440 let command_byte = 0b010 << 5;
441 data[0] = command_byte;
442 data[1..3].copy_from_slice(&self.index.to_le_bytes());
443 data[3] = self.sub_index;
444 frame.set_data(&data).unwrap();
445 self.payload.encode(frame);
446 }
447
448 fn decode(frame: &socketcan::CanFrame) -> Result<Self, CanOpenError> {
449 let index = u16::from_le_bytes(
450 frame.data()[1..3]
451 .try_into()
452 .map_err(|_| CanOpenError::ParseError("not enough data".to_owned()))?,
453 );
454 let sub_index = frame.data()[3];
455 let payload = SdoCmdInitiatePayload::decode(frame)?;
456 Ok(Self {
457 index,
458 sub_index,
459 payload,
460 })
461 }
462}
463
464#[derive(Clone, Debug)]
465pub struct SdoCmdUploadSegmentRx {
466 pub toggle: bool,
467}
468
469impl SdoCmdUploadSegmentRx {
470 fn encode(&self, frame: &mut socketcan::CanFrame) {
471 let mut data = [0u8; 8];
472 data[0] = 0b011 << 5 | ((self.toggle as u8) << 4);
473 frame.set_data(&data).unwrap();
474 }
475
476 fn decode(frame: &socketcan::CanFrame) -> Result<Self, CanOpenError> {
477 let command_byte = frame.data()[0];
478 let toggle = command_byte & 0b10000 != 0;
479 Ok(Self { toggle })
480 }
481}
482
483#[derive(Clone, Debug)]
484pub struct SdoCmdUploadSegmentTx {
485 pub toggle: bool,
486 pub data: Box<[u8]>,
487 pub last: bool,
488}
489
490impl SdoCmdUploadSegmentTx {
491 fn encode(&self, frame: &mut socketcan::CanFrame) {
492 let mut data = [0u8; 8];
493 data[0] =
494 ((self.toggle as u8) << 4) | ((7 - self.data.len() as u8) << 1) | (self.last as u8);
495 data[1..1 + self.data.len()].copy_from_slice(&self.data);
496 frame.set_data(&data).unwrap();
497 }
498
499 fn decode(frame: &socketcan::CanFrame) -> Result<Self, CanOpenError> {
500 let command_byte = frame.data()[0];
501 let toggle = command_byte & 0b10000 != 0;
502 let size = 7 - (0b111 & (command_byte >> 1)) as usize;
503 let last = command_byte & 0b1 != 0;
504 let mut data = Vec::new();
505 data.extend_from_slice(&frame.data()[1..1 + size]);
506
507 Ok(Self {
508 toggle,
509 last,
510 data: data.into(),
511 })
512 }
513}
514
515#[derive(Clone, Debug)]
516pub struct SdoCmdAbortTransfer {
517 pub index: u16,
518 pub sub_index: u8,
519 pub abort_code: enums::AbortCode,
521}
522
523impl SdoCmdAbortTransfer {
524 fn encode(&self, frame: &mut socketcan::CanFrame) {
525 let mut data = [0u8; 8];
526 let command_byte = 0b100 << 5;
527 data[0] = command_byte;
528 data[1..3].copy_from_slice(&self.index.to_le_bytes());
529 data[3] = self.sub_index;
530 data[4..8].copy_from_slice(&self.abort_code.encode().to_le_bytes());
531 frame.set_data(&data).unwrap();
532 }
533
534 fn decode(frame: &socketcan::CanFrame) -> Result<Self, CanOpenError> {
535 let index = u16::from_le_bytes(
536 frame.data()[1..3]
537 .try_into()
538 .map_err(|_| CanOpenError::ParseError("not enough data".to_owned()))?,
539 );
540 let sub_index = frame.data()[3];
541 let abort_code_u32 = u32::from_le_bytes(frame.data()[4..8].try_into().unwrap());
542 let abort_code = enums::AbortCode::decode(abort_code_u32)
543 .ok_or_else(|| CanOpenError::ParseError(format!("invalid abort code: {abort_code_u32}")))?;
544
545 Ok(Self {
546 index,
547 sub_index,
548 abort_code,
549 })
550 }
551}
552
553#[derive(Clone, Debug, PartialEq)]
571enum SdoCmdSpec {
572 DownloadSegment,
573 InitiateDownload,
574 InitiateUpload,
575 UploadSegment,
576 AbortTransfer,
577 BlockUpload,
578 BlockDownload,
579}
580
581impl SdoCmdSpec {
582 pub fn from_byte(byte: u8, reqres: ReqRes) -> Result<SdoCmdSpec, CanOpenError> {
583 use SdoCmdSpec::*;
584 let v = match (reqres, byte >> 5) {
585 (ReqRes::Req, 0x00) => DownloadSegment,
588 (ReqRes::Req, 0x01) => InitiateDownload,
589 (ReqRes::Req, 0x02) => InitiateUpload,
590 (ReqRes::Req, 0x03) => UploadSegment,
591
592 (ReqRes::Res, 0x00) => UploadSegment,
593 (ReqRes::Res, 0x01) => DownloadSegment,
594 (ReqRes::Res, 0x02) => InitiateUpload,
595 (ReqRes::Res, 0x03) => InitiateDownload,
596
597 (_, 0x04) => AbortTransfer,
598 (_, 0x05) => BlockUpload,
599 (_, 0x06) => BlockDownload,
600 _ => {
601 return Err(CanOpenError::ParseError(format!(
602 "bad client command specifier: {}",
603 byte
604 )))
605 }
606 };
607 Ok(v)
608 }
609}
610
611impl Sdo {
612 pub fn new_write(node_id: u8, index: u16, sub_index: u8, data: Box<[u8]>) -> Sdo {
613 Sdo {
614 node_id,
615 reqres: ReqRes::Req,
616 command: SdoCmd::InitiateDownloadRx(SdoCmdInitiateDownloadRx {
617 index,
618 sub_index,
619 payload: SdoCmdInitiatePayload::Expedited(data),
620 }),
621 }
622 }
623 pub fn new_write_resp(node_id: u8, index: u16, sub_index: u8) -> Sdo {
624 Sdo {
625 node_id,
626 reqres: ReqRes::Res,
627 command: SdoCmd::InitiateDownloadTx(SdoCmdInitiateDownloadTx { index, sub_index }),
628 }
629 }
630}
631
632impl FrameRW for Sdo {
633 fn decode(frame: &socketcan::CanFrame) -> Result<Sdo, CanOpenError> {
634 let data = frame.data();
635
636 let id = id_as_raw_std(frame)?;
637 if !(0x580..=0x5FF).contains(&id) && !(0x600..=0x67F).contains(&id) {
638 return Err(CanOpenError::BadMessage(format!(
639 "{id} is not an SDO can id"
640 ))); }
642
643 let node_id = (id & 0x7F) as u8;
644 let reqres = ReqRes::from_u16_sdo(id);
645
646 let command_spec = SdoCmdSpec::from_byte(data[0], reqres)?;
647 let command = match (reqres, command_spec) {
648 (ReqRes::Req, SdoCmdSpec::InitiateDownload) => {
649 SdoCmd::InitiateDownloadRx(SdoCmdInitiateDownloadRx::decode(frame)?)
650 }
651 (ReqRes::Req, SdoCmdSpec::DownloadSegment) => {
652 SdoCmd::DownloadSegmentRx(SdoCmdDownloadSegmentRx::decode(frame)?)
653 }
654 (ReqRes::Res, SdoCmdSpec::InitiateDownload) => {
655 SdoCmd::InitiateDownloadTx(SdoCmdInitiateDownloadTx::decode(frame)?)
656 }
657 (ReqRes::Res, SdoCmdSpec::DownloadSegment) => {
658 SdoCmd::DownloadSegmentTx(SdoCmdDownloadSegmentTx::decode(frame)?)
659 }
660 (ReqRes::Req, SdoCmdSpec::InitiateUpload) => {
661 SdoCmd::InitiateUploadRx(SdoCmdInitiateUploadRx::decode(frame)?)
662 }
663 (ReqRes::Req, SdoCmdSpec::UploadSegment) => {
664 SdoCmd::UploadSegmentRx(SdoCmdUploadSegmentRx::decode(frame)?)
665 }
666 (ReqRes::Res, SdoCmdSpec::InitiateUpload) => {
667 SdoCmd::InitiateUploadTx(SdoCmdInitiateUploadTx::decode(frame)?)
668 }
669 (ReqRes::Res, SdoCmdSpec::UploadSegment) => {
670 SdoCmd::UploadSegmentTx(SdoCmdUploadSegmentTx::decode(frame)?)
671 }
672 (_, SdoCmdSpec::AbortTransfer) => {
673 SdoCmd::AbortTransfer(SdoCmdAbortTransfer::decode(frame)?)
674 }
675 _ => return Err(CanOpenError::NotYetImplemented("block transfer".to_owned())),
676 };
677 let sdo = Sdo {
678 node_id,
679 command,
680 reqres,
681 };
682 Ok(sdo)
683 }
684
685 fn encode(&self, frame: &mut socketcan::CanFrame) {
686 frame.set_id(u16_as_id((self.node_id as u16) + self.reqres.to_u16_sdo()));
687 match &self.command {
688 SdoCmd::InitiateUploadRx(inner) => inner.encode(frame),
689 SdoCmd::InitiateDownloadRx(inner) => inner.encode(frame),
690 SdoCmd::UploadSegmentRx(inner) => inner.encode(frame),
691 SdoCmd::DownloadSegmentRx(inner) => inner.encode(frame),
692 SdoCmd::InitiateUploadTx(inner) => inner.encode(frame),
693 SdoCmd::InitiateDownloadTx(inner) => inner.encode(frame),
694 SdoCmd::UploadSegmentTx(inner) => inner.encode(frame),
695 SdoCmd::DownloadSegmentTx(inner) => inner.encode(frame),
696 SdoCmd::AbortTransfer(inner) => inner.encode(frame),
697 _ => todo!(),
698 };
699 }
700}
701
702#[derive(Debug, Copy, Clone)]
703#[repr(u8)]
704pub enum GuardStatus {
705 Boot = 0x00,
706 Stopped = 0x04,
707 Operational = 0x05,
708 PreOperational = 0x7F,
709}
710
711impl TryFrom<u8> for GuardStatus {
712 type Error = String;
713 fn try_from(value: u8) -> Result<Self, Self::Error> {
714 match value {
715 0x00 => Ok(GuardStatus::Boot),
716 0x04 => Ok(GuardStatus::Stopped),
717 0x05 => Ok(GuardStatus::Operational),
718 0x7F => Ok(GuardStatus::PreOperational),
719 _ => Err(format!("{value:x} not a valid guard status")),
720 }
721 }
722}
723
724#[binrw]
725#[brw(little)]
726#[derive(Debug)]
727pub struct Guard {
728 #[brw(ignore)]
729 node_id: u8,
730
731 #[br(temp)]
732 #[bw(calc = (*status as u8) | ((*toggle as u8) << 7))]
733 raw_byte: u8,
734
735 #[br(calc = raw_byte & 0x80 != 0)]
736 #[bw(ignore)]
737 toggle: bool,
738
739 #[br(try_calc = (raw_byte & 0x7F).try_into())]
740 #[bw(ignore)]
741 status: GuardStatus,
742}
743
744impl Guard {
745 pub fn new(node_id: u8, toggle: bool, status: GuardStatus) -> Self {
746 Self {
747 node_id,
748 toggle,
749 status,
750 }
751 }
752}
753
754impl FrameRW for Guard {
755 fn decode(frame: &socketcan::CanFrame) -> Result<Guard, CanOpenError> {
756 let data = frame.data();
757 if data.is_empty() {
758 return Err(CanOpenError::ParseError("data too short".to_owned()));
759 }
760
761 let id = id_as_raw_std(frame)?;
762 if !(0x700..=0x77F).contains(&id) {
763 return Err(CanOpenError::BadMessage("wrong id".to_owned()));
764 }
765 Guard::read(&mut std::io::Cursor::new(&data))
766 .map_err(|e| CanOpenError::ParseError(format!("no parse: {e}")))
767 }
768
769 fn encode(&self, frame: &mut socketcan::CanFrame) {
770 frame.set_id(u16_as_id(0x700 + self.node_id as u16));
771 let mut c = std::io::Cursor::new(Vec::new());
772 self.write(&mut c).unwrap();
773 frame.set_data(c.get_ref()).unwrap();
774 }
775}
776
777#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
781pub enum ReqRes {
782 #[default]
783 Req,
784 Res,
785}
786impl ReqRes {
787 pub fn to_u16_sdo(&self) -> u16 {
788 match self {
789 ReqRes::Req => 0x600,
790 ReqRes::Res => 0x580,
791 }
792 }
793 pub fn from_u16_sdo(id: u16) -> Self {
798 if id & 0x780 == 0x580 {
799 ReqRes::Res
800 } else {
801 ReqRes::Req
802 }
803 }
804}
805
806#[derive(Debug)]
807pub struct Pdo {
808 node_id: u8,
809 pdo_index: u8, reqres: ReqRes,
811 data: Vec<u8>, }
813
814impl Pdo {
815 pub fn new(node_id: u8, pdo_index: u8, data: &[u8]) -> Result<Self, CanOpenError> {
816 if !(1..=8).contains(&data.len()) {
817 return Err(CanOpenError::BadMessage(format!(
818 "got {} bytes of PDO data, expected between 1 and 8 bytes",
819 data.len()
820 )));
821 }
822 Ok(Self {
823 node_id,
824 pdo_index,
825 reqres: ReqRes::Req,
826 data: data.to_owned(),
827 })
828 }
829}
830
831impl FrameRW for Pdo {
832 fn decode(frame: &socketcan::CanFrame) -> Result<Pdo, CanOpenError> {
833 let id = id_as_raw_std(frame)?;
834 let data = frame.data().to_vec();
835
836 let reqres = if id & 0x80 == 0 { ReqRes::Res } else { ReqRes::Req };
838
839 let pdo_index = (((id & 0x700) >> 8) as u8) - if reqres == ReqRes::Req { 1u8 } else { 0u8 };
841
842 let node_id = (id & 0x7F) as u8;
843
844 Ok(Pdo {
845 pdo_index,
846 reqres,
847 node_id,
848 data,
849 })
850 }
851
852 fn encode(&self, frame: &mut socketcan::CanFrame) {
853 let id = (self.pdo_index as u16 + if self.reqres == ReqRes::Req { 1 } else { 0 }) << 8;
854 frame.set_id(u16_as_id(self.node_id as u16 + id));
855 frame.set_data(&self.data).unwrap();
857 }
858}
859
860#[derive(Debug)]
861pub struct Sync;
865
866impl FrameRW for Sync {
867 fn decode(frame: &socketcan::CanFrame) -> Result<Sync, CanOpenError> {
868 let id = id_as_raw_std(frame)?;
869 if id != 0x80 {
870 Err(CanOpenError::BadMessage(format!("not a SYNC cob-id: {id}")))
871 } else if !frame.data().is_empty() {
872 Err(CanOpenError::BadMessage(format!(
873 "data section of SYNC message should be empty, found {} bytes",
874 frame.data().len()
875 )))
876 } else {
877 Ok(Sync {})
878 }
879 }
880
881 fn encode(&self, frame: &mut socketcan::CanFrame) {
882 frame.set_id(u16_as_id(0x80));
883 frame.set_data(&[]).unwrap();
884 }
885}
886
887#[derive(Debug)]
888pub enum Message {
889 Nmt(Nmt),
890 Sync(Sync),
891 Emergency(Emergency),
892 Pdo(Pdo),
893 Sdo(Sdo),
894 Guard(Guard),
895}
896
897use thiserror::Error;
898
899#[derive(Error, Debug)]
900pub enum CanOpenError {
901 #[error("Overflow error: {0}")]
902 OverflowError(String),
903
904 #[error("Timed out after {0} ms")]
905 Timeout(u64),
906
907 #[error("FrameRW protocl is not {0}")]
908 BadMessage(String),
909
910 #[error("Connection error: {0}")]
911 ConnectionError(String),
912
913 #[error("CAN version mismatch: {0}")]
914 CanVersion(String),
915
916 #[error("Parse error: {0}")]
917 ParseError(String),
918
919 #[error("Unknown message type with COB-ID: {0}")]
920 UnknownFrameRWType(u32),
921
922 #[error("Not yet implemented: {0}")]
923 NotYetImplemented(String),
924
925 #[error("SDO AbortTransfer error, abort code: {0:?}")]
926 SdoAbortTransfer(enums::AbortCode),
927
928 #[error("IO Error: {0}")]
929 IOError(std::io::Error),
930}
931
932#[derive(Debug)]
938pub struct Conn {
939 socket: socketcan::CanSocket,
940}
941
942impl Conn {
943 pub fn new(interface_name: &str) -> Result<Self, CanOpenError> {
944 let socket = socketcan::CanSocket::open(interface_name).expect("no iface");
945 Ok(Conn { socket })
946 }
947
948 pub fn recv(&self) -> Result<Message, CanOpenError> {
949 let frame = self.socket.read_frame().map_err(CanOpenError::IOError)?;
950 Self::decode(&frame)
951 }
952
953 pub fn set_read_timeout(&self, t: std::time::Duration) -> Result<(), CanOpenError> {
954 self.socket
955 .set_read_timeout(t)
956 .map_err(CanOpenError::IOError)
957 }
958
959 pub fn set_write_timeout(&self, t: std::time::Duration) -> Result<(), CanOpenError> {
960 self.socket
961 .set_write_timeout(t)
962 .map_err(CanOpenError::IOError)
963 }
964
965 fn send_sdo_acked(&self, message: Sdo, node_id: u8) -> Result<Sdo, CanOpenError> {
966 self.send(&Message::Sdo(message.clone()))?;
967 loop {
968 let resp = self.recv()?;
969 if Self::is_sdo_ack(&resp, &message.command, node_id)? {
970 match resp {
971 Message::Sdo(sdo) => return Ok(sdo),
972 _ => unreachable!(),
973 }
974 };
975 }
976 }
977
978 fn is_sdo_ack(message: &Message, command: &SdoCmd, node_id: u8) -> Result<bool, CanOpenError> {
979 match message {
980 Message::Sdo(sdo) if sdo.node_id == node_id => match &sdo.command {
981 SdoCmd::AbortTransfer(e) => Err(CanOpenError::SdoAbortTransfer(e.abort_code.clone())),
982 cmd if SdoCmd::is_response_to(command, cmd) => Ok(true),
983 _ => Ok(false),
984 },
985 _ => Ok(false),
986 }
987 }
988
989 pub fn sdo_write(
990 &mut self,
991 node_id: u8,
992 index: u16,
993 sub_index: u8,
994 data: &[u8],
995 ) -> Result<(), CanOpenError> {
996 match data.len() {
997 0 => Ok(()),
999 1..=4 => {
1001 let message = Sdo {
1002 node_id,
1003 reqres: ReqRes::Req,
1004 command: SdoCmd::InitiateDownloadRx(SdoCmdInitiateDownloadRx {
1005 index,
1006 sub_index,
1007 payload: SdoCmdInitiatePayload::Expedited(data.into()),
1008 }),
1009 };
1010 self.send_sdo_acked(message, node_id)?;
1011 Ok(())
1012 }
1013 n => {
1015 let mut toggle = false;
1016 let init_message = Sdo {
1017 node_id,
1018 reqres: ReqRes::Req,
1019 command: SdoCmd::InitiateDownloadRx(SdoCmdInitiateDownloadRx {
1020 index,
1021 sub_index,
1022 payload: SdoCmdInitiatePayload::Segmented(Some(
1023 data.len().try_into().map_err(|e: TryFromIntError| {
1024 CanOpenError::OverflowError(e.to_string())
1025 })?,
1026 )),
1027 }),
1028 };
1029 self.send_sdo_acked(init_message, node_id)?;
1030
1031 for (idx_seg_start, _) in data.iter().enumerate().step_by(7) {
1032 let idx_seg_end = std::cmp::min(idx_seg_start + 7, n);
1033 let last = idx_seg_start + 7 >= n;
1034 let message = Sdo {
1035 node_id,
1036 reqres: ReqRes::Req,
1037 command: SdoCmd::DownloadSegmentRx(SdoCmdDownloadSegmentRx {
1038 toggle,
1039 data: data[idx_seg_start..idx_seg_end].into(),
1040 last,
1041 }),
1042 };
1043 toggle = !toggle;
1044 self.send_sdo_acked(message, node_id)?;
1045 }
1046 Ok(())
1047 }
1048 }
1049 }
1050
1051 pub fn sdo_read(
1052 &mut self,
1053 node_id: u8,
1054 index: u16,
1055 sub_index: u8,
1056 ) -> Result<Box<[u8]>, CanOpenError> {
1057 let res = self.send_sdo_acked(
1058 Sdo {
1059 node_id,
1060 command: SdoCmd::InitiateUploadRx(SdoCmdInitiateUploadRx { index, sub_index }),
1061 reqres: ReqRes::Req,
1062 },
1063 node_id,
1064 )?;
1065
1066 match res.command {
1067 SdoCmd::InitiateUploadTx(SdoCmdInitiateUploadTx {
1068 index: _,
1069 sub_index: _,
1070 payload: SdoCmdInitiatePayload::Expedited(data),
1071 }) => Ok(data),
1072 SdoCmd::InitiateUploadTx(SdoCmdInitiateUploadTx {
1073 index: _,
1074 sub_index: _,
1075 payload: SdoCmdInitiatePayload::Segmented(maybe_len),
1076 }) => {
1077 let mut buffer = Vec::new();
1078 let mut toggle = false;
1079 if let Some(len) = maybe_len {
1080 buffer.reserve(len as usize);
1081 };
1082 loop {
1083 let seg = self.send_sdo_acked(
1084 Sdo {
1085 reqres: ReqRes::Req,
1086 node_id,
1087 command: SdoCmd::UploadSegmentRx(SdoCmdUploadSegmentRx { toggle }),
1088 },
1089 node_id,
1090 )?;
1091 if let Sdo {
1092 reqres: _,
1093 node_id: _,
1094 command: SdoCmd::UploadSegmentTx(command),
1095 } = seg
1096 {
1097 buffer.extend_from_slice(&command.data);
1098 if command.last {
1099 break;
1100 }
1101 }
1102 toggle = !toggle;
1103 }
1104 Ok(buffer.into())
1105 }
1106 _ => unreachable!(),
1107 }
1108 }
1109
1110 pub fn send(&self, message: &Message) -> Result<(), CanOpenError> {
1111 let mut frame = socketcan::CanFrame::new(
1112 socketcan::Id::Standard(socketcan::StandardId::new(0).unwrap()),
1113 &[],
1114 )
1115 .unwrap();
1116 match message {
1117 Message::Sdo(sdo) => sdo.encode(&mut frame),
1118 Message::Pdo(pdo) => pdo.encode(&mut frame),
1119 Message::Sync(sync) => sync.encode(&mut frame),
1120 Message::Nmt(nmt) => nmt.encode(&mut frame),
1121 Message::Emergency(emergency) => emergency.encode(&mut frame),
1122 Message::Guard(guard) => guard.encode(&mut frame),
1123 }
1124 self.socket
1125 .write_frame(&frame)
1126 .map_err(CanOpenError::IOError)
1127 }
1128
1129 fn decode(frame: &socketcan::CanFrame) -> Result<Message, CanOpenError> {
1130 let id = id_as_raw_std(frame).unwrap();
1131 let protocol_id = id & 0xFF80;
1135 let node_id = id & 0x007F;
1137 let p = match protocol_id {
1138 0x000 => Message::Nmt(Nmt::decode(frame)?),
1139 0x080 if node_id == 0 => Message::Sync(Sync::decode(frame)?),
1140 0x080 => Message::Emergency(Emergency::decode(frame)?),
1141 0x180..=0x500 => Message::Pdo(Pdo::decode(frame)?),
1142 0x580..=0x600 => Message::Sdo(Sdo::decode(frame)?),
1143 0x700 => Message::Guard(Guard::decode(frame)?),
1144 _ => todo!(),
1145 };
1146 Ok(p)
1147 }
1148}