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 pub count: Option<u8>,
298}
299
300impl SyncObject {
301 pub fn new(count: Option<u8>) -> Self {
303 Self { count }
304 }
305}
306
307impl From<SyncObject> for CanMessage {
308 fn from(value: SyncObject) -> Self {
309 if let Some(count) = value.count {
310 CanMessage::new(SYNC_ID, &[count])
311 } else {
312 CanMessage::new(SYNC_ID, &[])
313 }
314 }
315}
316
317impl From<CanMessage> for SyncObject {
318 fn from(msg: CanMessage) -> Self {
319 if msg.id() == SYNC_ID {
320 let count = if msg.data().is_empty() {
321 None
322 } else {
323 Some(msg.data()[0])
324 };
325 Self { count }
326 } else {
327 panic!("Invalid message ID for SyncObject");
328 }
329 }
330}
331
332impl TryFrom<CanMessage> for ZencanMessage {
333 type Error = MessageError;
334
335 fn try_from(msg: CanMessage) -> Result<Self, Self::Error> {
336 let cob_id = msg.id();
337 if cob_id == NMT_CMD_ID {
338 Ok(ZencanMessage::NmtCommand(msg.try_into()?))
339 } else if cob_id.raw() & !0x7f == HEARTBEAT_ID as u32 {
340 let node = (cob_id.raw() & 0x7f) as u8;
341 let toggle = (msg.data[0] & (1 << 7)) != 0;
342 let state: NmtState = (msg.data[0] & 0x7f)
343 .try_into()
344 .map_err(|e: InvalidNmtStateError| MessageError::InvalidNmtState { value: e.0 })?;
345 Ok(ZencanMessage::Heartbeat(Heartbeat {
346 node,
347 toggle,
348 state,
349 }))
350 } else if cob_id.raw() & 0xff80 == 0x580 {
351 let resp: SdoResponse = msg
353 .try_into()
354 .map_err(|_| MessageError::MalformedMsg { cob_id })?;
355 Ok(ZencanMessage::SdoResponse(resp))
356 } else if cob_id.raw() >= 0x580 && cob_id.raw() <= 0x580 + 256 {
357 let req: SdoRequest = msg
359 .data()
360 .try_into()
361 .map_err(|_| MessageError::MalformedMsg { cob_id })?;
362 Ok(ZencanMessage::SdoRequest(req))
363 } else if cob_id == SYNC_ID {
364 Ok(ZencanMessage::Sync(msg.into()))
365 } else if cob_id == LSS_REQ_ID {
366 let req: LssRequest = msg
367 .data()
368 .try_into()
369 .map_err(|_| MessageError::MalformedMsg { cob_id })?;
370 Ok(ZencanMessage::LssRequest(req))
371 } else if cob_id == LSS_RESP_ID {
372 let resp: LssResponse = msg
373 .data()
374 .try_into()
375 .map_err(|_| MessageError::MalformedMsg { cob_id })?;
376 Ok(ZencanMessage::LssResponse(resp))
377 } else {
378 Err(MessageError::UnrecognizedId { cob_id })
379 }
380 }
381}
382
383#[derive(Clone, Copy, Debug)]
385#[cfg_attr(feature = "defmt", derive(defmt::Format))]
386#[allow(missing_docs)]
387pub enum ZencanMessage {
388 NmtCommand(NmtCommand),
389 Sync(SyncObject),
390 Heartbeat(Heartbeat),
391 SdoRequest(SdoRequest),
392 SdoResponse(SdoResponse),
393 LssRequest(LssRequest),
394 LssResponse(LssResponse),
395}
396
397#[derive(Debug, Clone, Copy, PartialEq, Snafu)]
399pub enum MessageError {
400 MessageTooShort,
402 MalformedMsg {
404 cob_id: CanId,
406 },
407 #[snafu(display("Unexpected message ID found: {cob_id:?}, expected: {expected:?}"))]
409 UnexpectedId {
410 cob_id: CanId,
412 expected: CanId,
414 },
415 InvalidField,
417 UnrecognizedId {
421 cob_id: CanId,
423 },
424 InvalidNmtState {
426 value: u8,
428 },
429 #[snafu(display("Unexpected LSS command: {value}"))]
431 UnexpectedLssCommand {
432 value: u8,
434 },
435}