1use snafu::Snafu;
4
5use crate::{
6 lss::{LssRequest, LssResponse},
7 nmt::{InvalidNmtStateError, NmtState},
8 sdo::{SdoRequest, SdoResponse},
9};
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq)]
15pub enum CanId {
16 Extended(u32),
18 Std(u16),
20}
21
22impl core::fmt::Display for CanId {
23 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
24 match self {
25 CanId::Extended(id) => write!(f, "Extended(0x{id:x})"),
26 CanId::Std(id) => write!(f, "Std(0x{id:x})"),
27 }
28 }
29}
30
31impl CanId {
32 pub const fn extended(id: u32) -> CanId {
34 CanId::Extended(id)
35 }
36
37 pub const fn std(id: u16) -> CanId {
39 CanId::Std(id)
40 }
41
42 pub const fn raw(&self) -> u32 {
44 match self {
45 CanId::Extended(id) => *id,
46 CanId::Std(id) => *id as u32,
47 }
48 }
49
50 pub const fn is_extended(&self) -> bool {
52 match self {
53 CanId::Extended(_) => true,
54 CanId::Std(_) => false,
55 }
56 }
57}
58
59const MAX_DATA_LENGTH: usize = 8;
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq)]
63pub struct CanMessage {
64 pub data: [u8; MAX_DATA_LENGTH],
68 pub dlc: u8,
70 pub rtr: bool,
72 pub id: CanId,
74}
75
76impl Default for CanMessage {
77 fn default() -> Self {
78 Self {
79 data: [0; MAX_DATA_LENGTH],
80 dlc: 0,
81 id: CanId::Std(0),
82 rtr: false,
83 }
84 }
85}
86
87impl CanMessage {
88 pub fn new(id: CanId, data: &[u8]) -> Self {
90 let dlc = data.len() as u8;
91 if dlc > MAX_DATA_LENGTH as u8 {
92 panic!(
93 "Data length exceeds maximum size of {} bytes",
94 MAX_DATA_LENGTH
95 );
96 }
97 let mut buf = [0u8; MAX_DATA_LENGTH];
98 buf[0..dlc as usize].copy_from_slice(data);
99 let rtr = false;
100
101 Self {
102 id,
103 dlc,
104 data: buf,
105 rtr,
106 }
107 }
108
109 pub fn new_rtr(id: CanId) -> Self {
113 Self {
114 id,
115 rtr: true,
116 ..Default::default()
117 }
118 }
119
120 pub fn id(&self) -> CanId {
122 self.id
123 }
124
125 pub fn data(&self) -> &[u8] {
127 &self.data[0..self.dlc as usize]
128 }
129
130 pub fn is_rtr(&self) -> bool {
132 self.rtr
133 }
134}
135
136#[derive(Clone, Copy, Debug, Snafu)]
141#[repr(u8)]
142pub enum CanError {
143 Bit = 1,
146 Stuff = 2,
149 Form = 3,
151 Ack = 4,
153 Crc = 5,
155 Other,
157}
158
159impl CanError {
160 pub fn from_raw(raw: u8) -> Self {
162 match raw {
163 1 => Self::Bit,
164 2 => Self::Stuff,
165 3 => Self::Form,
166 4 => Self::Ack,
167 5 => Self::Crc,
168 _ => Self::Other,
169 }
170 }
171}
172
173#[derive(Copy, Clone, Debug, PartialEq)]
175#[cfg_attr(feature = "defmt", derive(defmt::Format))]
176#[repr(u8)]
177pub enum NmtCommandSpecifier {
178 Start = 1,
180 Stop = 2,
182 EnterPreOp = 128,
184 ResetApp = 129,
186 ResetComm = 130,
188}
189
190impl NmtCommandSpecifier {
191 pub fn from_byte(b: u8) -> Result<Self, MessageError> {
193 match b {
194 1 => Ok(Self::Start),
195 2 => Ok(Self::Stop),
196 128 => Ok(Self::EnterPreOp),
197 129 => Ok(Self::ResetApp),
198 130 => Ok(Self::ResetComm),
199 _ => Err(MessageError::InvalidField),
200 }
201 }
202}
203
204pub const NMT_CMD_ID: CanId = CanId::Std(0);
206pub const SYNC_ID: CanId = CanId::Std(0x80);
208pub const LSS_RESP_ID: CanId = CanId::Std(0x7E4);
210pub const LSS_REQ_ID: CanId = CanId::Std(0x7E5);
212pub const HEARTBEAT_ID: u16 = 0x700;
214pub const SDO_REQ_BASE: u16 = 0x600;
216pub const SDO_RESP_BASE: u16 = 0x580;
218
219#[derive(Clone, Copy, Debug)]
221#[cfg_attr(feature = "defmt", derive(defmt::Format))]
222pub struct NmtCommand {
223 pub cs: NmtCommandSpecifier,
225 pub node: u8,
227}
228
229impl TryFrom<CanMessage> for NmtCommand {
230 type Error = MessageError;
231
232 fn try_from(msg: CanMessage) -> Result<Self, Self::Error> {
233 let payload = msg.data();
234 if msg.id() != NMT_CMD_ID {
235 Err(MessageError::UnexpectedId {
236 cob_id: msg.id(),
237 expected: NMT_CMD_ID,
238 })
239 } else if payload.len() >= 2 {
240 let cmd = NmtCommandSpecifier::from_byte(payload[0])?;
241 let node = payload[1];
242 Ok(NmtCommand { cs: cmd, node })
243 } else {
244 Err(MessageError::MessageTooShort)
245 }
246 }
247}
248
249impl From<NmtCommand> for CanMessage {
250 fn from(cmd: NmtCommand) -> Self {
251 let mut msg = CanMessage {
252 id: NMT_CMD_ID,
253 dlc: 2,
254 ..Default::default()
255 };
256 msg.data[0] = cmd.cs as u8;
257 msg.data[1] = cmd.node;
258 msg
259 }
260}
261
262#[derive(Clone, Copy, Debug)]
264#[cfg_attr(feature = "defmt", derive(defmt::Format))]
265pub struct Heartbeat {
266 pub node: u8,
268 pub toggle: bool,
270 pub state: NmtState,
272}
273
274impl From<Heartbeat> for CanMessage {
275 fn from(value: Heartbeat) -> Self {
276 let mut msg = CanMessage {
277 id: CanId::Std(HEARTBEAT_ID | value.node as u16),
278 dlc: 1,
279 ..Default::default()
280 };
281 msg.data[0] = value.state as u8;
282 if value.toggle {
283 msg.data[0] |= 1 << 7;
284 }
285 msg
286 }
287}
288#[derive(Clone, Copy, Debug, Default)]
294#[cfg_attr(feature = "defmt", derive(defmt::Format))]
295pub struct SyncObject {
296 count: Option<u8>,
297}
298
299impl SyncObject {
300 pub fn new(count: Option<u8>) -> Self {
302 Self { count }
303 }
304}
305
306impl From<SyncObject> for CanMessage {
307 fn from(value: SyncObject) -> Self {
308 if let Some(count) = value.count {
309 CanMessage::new(SYNC_ID, &[count])
310 } else {
311 CanMessage::new(SYNC_ID, &[])
312 }
313 }
314}
315
316impl From<CanMessage> for SyncObject {
317 fn from(msg: CanMessage) -> Self {
318 if msg.id() == SYNC_ID {
319 let count = if msg.data().is_empty() {
320 None
321 } else {
322 Some(msg.data()[0])
323 };
324 Self { count }
325 } else {
326 panic!("Invalid message ID for SyncObject");
327 }
328 }
329}
330
331impl TryFrom<CanMessage> for ZencanMessage {
332 type Error = MessageError;
333
334 fn try_from(msg: CanMessage) -> Result<Self, Self::Error> {
335 let cob_id = msg.id();
336 if cob_id == NMT_CMD_ID {
337 Ok(ZencanMessage::NmtCommand(msg.try_into()?))
338 } else if cob_id.raw() & !0x7f == HEARTBEAT_ID as u32 {
339 let node = (cob_id.raw() & 0x7f) as u8;
340 let toggle = (msg.data[0] & (1 << 7)) != 0;
341 let state: NmtState = (msg.data[0] & 0x7f)
342 .try_into()
343 .map_err(|e: InvalidNmtStateError| MessageError::InvalidNmtState { value: e.0 })?;
344 Ok(ZencanMessage::Heartbeat(Heartbeat {
345 node,
346 toggle,
347 state,
348 }))
349 } else if cob_id.raw() & 0xff80 == 0x580 {
350 let resp: SdoResponse = msg
352 .try_into()
353 .map_err(|_| MessageError::MalformedMsg { cob_id })?;
354 Ok(ZencanMessage::SdoResponse(resp))
355 } else if cob_id.raw() >= 0x580 && cob_id.raw() <= 0x580 + 256 {
356 let req: SdoRequest = msg
358 .data()
359 .try_into()
360 .map_err(|_| MessageError::MalformedMsg { cob_id })?;
361 Ok(ZencanMessage::SdoRequest(req))
362 } else if cob_id == SYNC_ID {
363 Ok(ZencanMessage::Sync(msg.into()))
364 } else if cob_id == LSS_REQ_ID {
365 let req: LssRequest = msg
366 .data()
367 .try_into()
368 .map_err(|_| MessageError::MalformedMsg { cob_id })?;
369 Ok(ZencanMessage::LssRequest(req))
370 } else if cob_id == LSS_RESP_ID {
371 let resp: LssResponse = msg
372 .data()
373 .try_into()
374 .map_err(|_| MessageError::MalformedMsg { cob_id })?;
375 Ok(ZencanMessage::LssResponse(resp))
376 } else {
377 Err(MessageError::UnrecognizedId { cob_id })
378 }
379 }
380}
381
382#[derive(Clone, Copy, Debug)]
384#[cfg_attr(feature = "defmt", derive(defmt::Format))]
385#[allow(missing_docs)]
386pub enum ZencanMessage {
387 NmtCommand(NmtCommand),
388 Sync(SyncObject),
389 Heartbeat(Heartbeat),
390 SdoRequest(SdoRequest),
391 SdoResponse(SdoResponse),
392 LssRequest(LssRequest),
393 LssResponse(LssResponse),
394}
395
396#[derive(Debug, Clone, Copy, PartialEq, Snafu)]
398pub enum MessageError {
399 MessageTooShort,
401 MalformedMsg {
403 cob_id: CanId,
405 },
406 #[snafu(display("Unexpected message ID found: {cob_id:?}, expected: {expected:?}"))]
408 UnexpectedId {
409 cob_id: CanId,
411 expected: CanId,
413 },
414 InvalidField,
416 UnrecognizedId {
420 cob_id: CanId,
422 },
423 InvalidNmtState {
425 value: u8,
427 },
428 #[snafu(display("Unexpected LSS command: {value}"))]
430 UnexpectedLssCommand {
431 value: u8,
433 },
434}