1use snafu::Snafu;
4
5use crate::{
6 lss::{LssRequest, LssResponse},
7 sdo::{SdoRequest, SdoResponse},
8};
9
10#[derive(Copy, Clone, Debug, PartialEq, Eq)]
14pub enum CanId {
15 Extended(u32),
17 Std(u16),
19}
20
21impl core::fmt::Display for CanId {
22 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
23 match self {
24 CanId::Extended(id) => write!(f, "Extended(0x{id:x})"),
25 CanId::Std(id) => write!(f, "Std(0x{id:x})"),
26 }
27 }
28}
29
30impl CanId {
31 pub const fn extended(id: u32) -> CanId {
33 CanId::Extended(id)
34 }
35
36 pub const fn std(id: u16) -> CanId {
38 CanId::Std(id)
39 }
40
41 pub const fn raw(&self) -> u32 {
43 match self {
44 CanId::Extended(id) => *id,
45 CanId::Std(id) => *id as u32,
46 }
47 }
48
49 pub const fn is_extended(&self) -> bool {
51 match self {
52 CanId::Extended(_) => true,
53 CanId::Std(_) => false,
54 }
55 }
56}
57
58const MAX_DATA_LENGTH: usize = 8;
59
60#[derive(Clone, Copy, Debug, PartialEq, Eq)]
62pub struct CanMessage {
63 pub data: [u8; MAX_DATA_LENGTH],
67 pub dlc: u8,
69 pub rtr: bool,
71 pub id: CanId,
73}
74
75impl Default for CanMessage {
76 fn default() -> Self {
77 Self {
78 data: [0; MAX_DATA_LENGTH],
79 dlc: 0,
80 id: CanId::Std(0),
81 rtr: false,
82 }
83 }
84}
85
86impl CanMessage {
87 pub fn new(id: CanId, data: &[u8]) -> Self {
89 let dlc = data.len() as u8;
90 if dlc > MAX_DATA_LENGTH as u8 {
91 panic!(
92 "Data length exceeds maximum size of {} bytes",
93 MAX_DATA_LENGTH
94 );
95 }
96 let mut buf = [0u8; MAX_DATA_LENGTH];
97 buf[0..dlc as usize].copy_from_slice(data);
98 let rtr = false;
99
100 Self {
101 id,
102 dlc,
103 data: buf,
104 rtr,
105 }
106 }
107
108 pub fn new_rtr(id: CanId) -> Self {
112 Self {
113 id,
114 rtr: true,
115 ..Default::default()
116 }
117 }
118
119 pub fn id(&self) -> CanId {
121 self.id
122 }
123
124 pub fn data(&self) -> &[u8] {
126 &self.data[0..self.dlc as usize]
127 }
128
129 pub fn is_rtr(&self) -> bool {
131 self.rtr
132 }
133}
134
135#[derive(Clone, Copy, Debug, Snafu)]
140#[repr(u8)]
141pub enum CanError {
142 Bit = 1,
145 Stuff = 2,
148 Form = 3,
150 Ack = 4,
152 Crc = 5,
154 Other,
156}
157
158impl CanError {
159 pub fn from_raw(raw: u8) -> Self {
161 match raw {
162 1 => Self::Bit,
163 2 => Self::Stuff,
164 3 => Self::Form,
165 4 => Self::Ack,
166 5 => Self::Crc,
167 _ => Self::Other,
168 }
169 }
170}
171
172#[derive(Copy, Clone, Debug, PartialEq)]
174#[cfg_attr(feature = "defmt", derive(defmt::Format))]
175#[repr(u8)]
176pub enum NmtCommandSpecifier {
177 Start = 1,
179 Stop = 2,
181 EnterPreOp = 128,
183 ResetApp = 129,
185 ResetComm = 130,
187}
188
189impl NmtCommandSpecifier {
190 pub fn from_byte(b: u8) -> Result<Self, MessageError> {
192 match b {
193 1 => Ok(Self::Start),
194 2 => Ok(Self::Stop),
195 128 => Ok(Self::EnterPreOp),
196 129 => Ok(Self::ResetApp),
197 130 => Ok(Self::ResetComm),
198 _ => Err(MessageError::InvalidField),
199 }
200 }
201}
202
203pub const NMT_CMD_ID: CanId = CanId::Std(0);
205pub const SYNC_ID: CanId = CanId::Std(0x80);
207pub const LSS_RESP_ID: CanId = CanId::Std(0x7E4);
209pub const LSS_REQ_ID: CanId = CanId::Std(0x7E5);
211pub const HEARTBEAT_ID: u16 = 0x700;
213pub const SDO_REQ_BASE: u16 = 0x600;
215pub const SDO_RESP_BASE: u16 = 0x580;
217
218#[derive(Clone, Copy, Debug)]
220#[cfg_attr(feature = "defmt", derive(defmt::Format))]
221pub struct NmtCommand {
222 pub cs: NmtCommandSpecifier,
224 pub node: u8,
226}
227
228impl TryFrom<CanMessage> for NmtCommand {
229 type Error = MessageError;
230
231 fn try_from(msg: CanMessage) -> Result<Self, Self::Error> {
232 let payload = msg.data();
233 if msg.id() != NMT_CMD_ID {
234 Err(MessageError::UnexpectedId {
235 cob_id: msg.id(),
236 expected: NMT_CMD_ID,
237 })
238 } else if payload.len() >= 2 {
239 let cmd = NmtCommandSpecifier::from_byte(payload[0])?;
240 let node = payload[1];
241 Ok(NmtCommand { cs: cmd, node })
242 } else {
243 Err(MessageError::MessageTooShort)
244 }
245 }
246}
247
248impl From<NmtCommand> for CanMessage {
249 fn from(cmd: NmtCommand) -> Self {
250 let mut msg = CanMessage {
251 id: NMT_CMD_ID,
252 dlc: 2,
253 ..Default::default()
254 };
255 msg.data[0] = cmd.cs as u8;
256 msg.data[1] = cmd.node;
257 msg
258 }
259}
260
261#[derive(Copy, Clone, Debug, PartialEq)]
263#[cfg_attr(feature = "defmt", derive(defmt::Format))]
264#[repr(u8)]
265pub enum NmtState {
266 Bootup = 0,
270 Stopped = 4,
272 Operational = 5,
274 PreOperational = 127,
276}
277
278impl core::fmt::Display for NmtState {
279 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
280 match self {
281 NmtState::Bootup => write!(f, "Bootup"),
282 NmtState::Stopped => write!(f, "Stopped"),
283 NmtState::Operational => write!(f, "Operational"),
284 NmtState::PreOperational => write!(f, "PreOperational"),
285 }
286 }
287}
288
289#[derive(Clone, Copy, Debug)]
290pub struct InvalidNmtStateError(u8);
292
293impl TryFrom<u8> for NmtState {
294 type Error = InvalidNmtStateError;
295
296 fn try_from(value: u8) -> Result<Self, Self::Error> {
300 use NmtState::*;
301 match value {
302 x if x == Bootup as u8 => Ok(Bootup),
303 x if x == Stopped as u8 => Ok(Stopped),
304 x if x == Operational as u8 => Ok(Operational),
305 x if x == PreOperational as u8 => Ok(PreOperational),
306 _ => Err(InvalidNmtStateError(value)),
307 }
308 }
309}
310
311#[derive(Clone, Copy, Debug)]
313#[cfg_attr(feature = "defmt", derive(defmt::Format))]
314pub struct Heartbeat {
315 pub node: u8,
317 pub toggle: bool,
319 pub state: NmtState,
321}
322
323impl From<Heartbeat> for CanMessage {
324 fn from(value: Heartbeat) -> Self {
325 let mut msg = CanMessage {
326 id: CanId::Std(HEARTBEAT_ID | value.node as u16),
327 dlc: 1,
328 ..Default::default()
329 };
330 msg.data[0] = value.state as u8;
331 if value.toggle {
332 msg.data[0] |= 1 << 7;
333 }
334 msg
335 }
336}
337#[derive(Clone, Copy, Debug)]
343#[cfg_attr(feature = "defmt", derive(defmt::Format))]
344pub struct SyncObject {
345 count: u8,
346}
347
348impl SyncObject {
349 pub fn new(count: u8) -> Self {
351 Self { count }
352 }
353}
354
355impl Default for SyncObject {
356 fn default() -> Self {
357 Self { count: 1 }
358 }
359}
360
361impl From<SyncObject> for CanMessage {
362 fn from(value: SyncObject) -> Self {
363 CanMessage::new(SYNC_ID, &[value.count])
364 }
365}
366
367impl From<CanMessage> for SyncObject {
368 fn from(msg: CanMessage) -> Self {
369 if msg.id() == SYNC_ID {
370 let count = msg.data()[0];
371 Self { count }
372 } else {
373 panic!("Invalid message ID for SyncObject");
374 }
375 }
376}
377
378impl TryFrom<CanMessage> for ZencanMessage {
379 type Error = MessageError;
380
381 fn try_from(msg: CanMessage) -> Result<Self, Self::Error> {
382 let cob_id = msg.id();
383 if cob_id == NMT_CMD_ID {
384 Ok(ZencanMessage::NmtCommand(msg.try_into()?))
385 } else if cob_id.raw() & !0x7f == HEARTBEAT_ID as u32 {
386 let node = (cob_id.raw() & 0x7f) as u8;
387 let toggle = (msg.data[0] & (1 << 7)) != 0;
388 let state: NmtState = (msg.data[0] & 0x7f)
389 .try_into()
390 .map_err(|e: InvalidNmtStateError| MessageError::InvalidNmtState { value: e.0 })?;
391 Ok(ZencanMessage::Heartbeat(Heartbeat {
392 node,
393 toggle,
394 state,
395 }))
396 } else if cob_id.raw() & 0xff80 == 0x580 {
397 let resp: SdoResponse = msg
399 .try_into()
400 .map_err(|_| MessageError::MalformedMsg { cob_id })?;
401 Ok(ZencanMessage::SdoResponse(resp))
402 } else if cob_id.raw() >= 0x580 && cob_id.raw() <= 0x580 + 256 {
403 let req: SdoRequest = msg
405 .data()
406 .try_into()
407 .map_err(|_| MessageError::MalformedMsg { cob_id })?;
408 Ok(ZencanMessage::SdoRequest(req))
409 } else if cob_id == SYNC_ID {
410 Ok(ZencanMessage::Sync(msg.into()))
411 } else if cob_id == LSS_REQ_ID {
412 let req: LssRequest = msg
413 .data()
414 .try_into()
415 .map_err(|_| MessageError::MalformedMsg { cob_id })?;
416 Ok(ZencanMessage::LssRequest(req))
417 } else if cob_id == LSS_RESP_ID {
418 let resp: LssResponse = msg
419 .data()
420 .try_into()
421 .map_err(|_| MessageError::MalformedMsg { cob_id })?;
422 Ok(ZencanMessage::LssResponse(resp))
423 } else {
424 Err(MessageError::UnrecognizedId { cob_id })
425 }
426 }
427}
428
429#[derive(Clone, Copy, Debug)]
431#[cfg_attr(feature = "defmt", derive(defmt::Format))]
432#[allow(missing_docs)]
433pub enum ZencanMessage {
434 NmtCommand(NmtCommand),
435 Sync(SyncObject),
436 Heartbeat(Heartbeat),
437 SdoRequest(SdoRequest),
438 SdoResponse(SdoResponse),
439 LssRequest(LssRequest),
440 LssResponse(LssResponse),
441}
442
443#[derive(Debug, Clone, Copy, PartialEq, Snafu)]
445pub enum MessageError {
446 MessageTooShort,
448 MalformedMsg {
450 cob_id: CanId,
452 },
453 #[snafu(display("Unexpected message ID found: {cob_id:?}, expected: {expected:?}"))]
455 UnexpectedId {
456 cob_id: CanId,
458 expected: CanId,
460 },
461 InvalidField,
463 UnrecognizedId {
467 cob_id: CanId,
469 },
470 InvalidNmtState {
472 value: u8,
474 },
475 #[snafu(display("Unexpected LSS command: {value}"))]
477 UnexpectedLssCommand {
478 value: u8,
480 },
481}