1use crate::{
44 node_state::NmtStateAccess,
45 object_dict::{
46 find_object_entry, ConstField, ODEntry, ObjectAccess, ProvidesSubObjects, SubObjectAccess,
47 },
48};
49use zencan_common::{
50 nmt::NmtState,
51 objects::{AccessType, DataType, ObjectCode, PdoMappable, SubInfo},
52 pdo::PdoMapping,
53 sdo::AbortCode,
54 AtomicCell, CanId, NodeId,
55};
56
57const N_MAPPING_PARAMS: usize = 8;
62
63#[derive(Clone, Copy)]
64struct MappingEntry<'a> {
66 pub object: &'a ODEntry<'a>,
68 pub sub: u8,
70 pub length: u8,
72}
73
74#[allow(missing_debug_implementations)]
75#[derive(Copy, Clone)]
77pub struct PdoDefaults<'a> {
78 cob_id: u32,
79 flags: u8,
80 transmission_type: u8,
81 mappings: &'a [u32],
82}
83
84impl Default for PdoDefaults<'_> {
85 fn default() -> Self {
86 Self::DEFAULT
87 }
88}
89
90#[allow(missing_docs)]
91impl<'a> PdoDefaults<'a> {
92 const ADD_NODE_ID_FLAG: usize = 0;
93 const VALID_FLAG: usize = 1;
94 const RTR_DISABLED_FLAG: usize = 2;
95 const IS_EXTENDED_FLAG: usize = 3;
96
97 pub const DEFAULT: PdoDefaults<'a> = Self {
99 cob_id: 0,
100 flags: 0,
101 transmission_type: 0,
102 mappings: &[],
103 };
104
105 pub const fn new(
107 cob_id: u32,
108 extended: bool,
109 add_node_id: bool,
110 valid: bool,
111 rtr_disabled: bool,
112 transmission_type: u8,
113 mappings: &'static [u32],
114 ) -> Self {
115 let mut flags = 0u8;
117 if valid {
118 flags |= 1 << Self::VALID_FLAG;
119 }
120 if rtr_disabled {
121 flags |= 1 << Self::RTR_DISABLED_FLAG;
122 }
123 if add_node_id {
124 flags |= 1 << Self::ADD_NODE_ID_FLAG;
125 }
126 if extended {
127 flags |= 1 << Self::IS_EXTENDED_FLAG;
128 }
129
130 Self {
131 cob_id,
132 flags,
133 transmission_type,
134 mappings,
135 }
136 }
137
138 pub const fn valid(&self) -> bool {
139 self.flags & (1 << Self::VALID_FLAG) != 0
140 }
141
142 pub const fn rtr_disabled(&self) -> bool {
143 self.flags & (1 << Self::RTR_DISABLED_FLAG) != 0
144 }
145
146 pub const fn add_node_id(&self) -> bool {
147 self.flags & (1 << Self::ADD_NODE_ID_FLAG) != 0
148 }
149
150 pub const fn extended(&self) -> bool {
151 self.flags & (1 << Self::IS_EXTENDED_FLAG) != 0
152 }
153
154 pub const fn can_id(&self, node_id: u8) -> CanId {
155 let id = if self.add_node_id() {
156 self.cob_id + node_id as u32
157 } else {
158 self.cob_id
159 };
160 if self.extended() {
161 CanId::Extended(id)
162 } else {
163 CanId::Std(id as u16)
164 }
165 }
166}
167
168#[allow(missing_debug_implementations)]
170pub struct Pdo<'a> {
171 od: &'a [ODEntry<'a>],
175 nmt_state: &'a dyn NmtStateAccess,
177 node_id: AtomicCell<NodeId>,
179 cob_id: AtomicCell<Option<CanId>>,
181 valid: AtomicCell<bool>,
183 rtr_disabled: AtomicCell<bool>,
185 transmission_type: AtomicCell<u8>,
192 sync_counter: AtomicCell<u8>,
194 pub buffered_value: AtomicCell<Option<[u8; 8]>>,
196 valid_maps: AtomicCell<u8>,
200 mapping_params: [AtomicCell<Option<MappingEntry<'a>>>; N_MAPPING_PARAMS],
204 defaults: Option<&'a PdoDefaults<'a>>,
206}
207
208impl<'a> Pdo<'a> {
209 pub const fn new(od: &'a [ODEntry<'a>], nmt_state: &'a dyn NmtStateAccess) -> Self {
211 let cob_id = AtomicCell::new(None);
212 let node_id = AtomicCell::new(NodeId::Unconfigured);
213 let valid = AtomicCell::new(false);
214 let rtr_disabled = AtomicCell::new(false);
215 let transmission_type = AtomicCell::new(0);
216 let sync_counter = AtomicCell::new(0);
217 let buffered_value = AtomicCell::new(None);
218 let valid_maps = AtomicCell::new(0);
219 let mapping_params = [const { AtomicCell::new(None) }; N_MAPPING_PARAMS];
220 let defaults = None;
221 Self {
222 od,
223 nmt_state,
224 node_id,
225 cob_id,
226 valid,
227 rtr_disabled,
228 transmission_type,
229 sync_counter,
230 buffered_value,
231 valid_maps,
232 mapping_params,
233 defaults,
234 }
235 }
236
237 pub const fn new_with_defaults(
239 od: &'static [ODEntry<'static>],
240 nmt_state: &'static dyn NmtStateAccess,
241 defaults: &'static PdoDefaults,
242 ) -> Self {
243 let mut pdo = Pdo::new(od, nmt_state);
244 pdo.defaults = Some(defaults);
245 pdo
246 }
247
248 pub fn set_valid(&self, value: bool) {
250 self.valid.store(value);
251 }
252
253 pub fn valid(&self) -> bool {
255 self.valid.load()
256 }
257
258 pub fn set_transmission_type(&self, value: u8) {
260 self.transmission_type.store(value);
261 }
262
263 pub fn transmission_type(&self) -> u8 {
265 self.transmission_type.load()
266 }
267
268 pub fn cob_id(&self) -> CanId {
270 self.cob_id.load().unwrap_or(self.default_cob_id())
271 }
272
273 pub fn default_cob_id(&self) -> CanId {
275 if self.defaults.is_none() {
276 return CanId::std(0);
277 }
278 let defaults = self.defaults.unwrap();
279 let node_id = match self.node_id.load() {
280 NodeId::Unconfigured => 0,
281 NodeId::Configured(node_id) => node_id.raw(),
282 };
283 defaults.can_id(node_id)
284 }
285
286 pub fn sync_update(&self) -> bool {
290 if !self.valid.load() {
291 return false;
292 }
293
294 let transmission_type = self.transmission_type.load();
295 if transmission_type == 0 {
296 true
299 } else if transmission_type <= 240 {
300 let cnt = self.sync_counter.fetch_add(1) + 1;
301 cnt == transmission_type
302 } else {
303 false
304 }
305 }
306
307 pub fn read_events(&self) -> bool {
309 if !self.valid.load() {
310 return false;
311 }
312
313 for i in 0..self.mapping_params.len() {
314 let param = self.mapping_params[i].load();
315 if param.is_none() {
316 break;
317 }
318 let param = param.unwrap();
319 if param.object.data.read_event_flag(param.sub) {
320 return true;
321 }
322 }
323 false
324 }
325
326 fn nmt_state(&self) -> NmtState {
327 self.nmt_state.nmt_state()
328 }
329
330 pub(crate) fn clear_events(&self) {
331 for i in 0..self.mapping_params.len() {
332 let param = self.mapping_params[i].load();
333 if param.is_none() {
334 break;
335 }
336 let param = param.unwrap();
337 param.object.data.clear_events();
338 }
339 }
340
341 pub(crate) fn store_pdo_data(&self, data: &[u8]) {
342 let mut offset = 0;
343 let valid_maps = self.valid_maps.load() as usize;
344 for (i, param) in self.mapping_params.iter().enumerate() {
345 if i >= valid_maps {
346 break;
347 }
348 let param = param.load();
349 if param.is_none() {
350 break;
351 }
352 let param = param.unwrap();
353 let length = param.length as usize;
354 if offset + length > data.len() {
355 break;
356 }
357 let data_to_write = &data[offset..offset + length];
358 param.object.data.write(param.sub, data_to_write).ok();
361 offset += length;
362 }
363 }
364
365 pub(crate) fn send_pdo(&self) {
366 let mut data = [0u8; 8];
367 let mut offset = 0;
368 let valid_maps = self.valid_maps.load() as usize;
369 for (i, param) in self.mapping_params.iter().enumerate() {
370 if i >= valid_maps {
371 break;
372 }
373 let param = param.load();
374 if param.is_none() {
377 break;
378 }
379 let param = param.unwrap();
380 let length = param.length as usize;
381 if offset + length > data.len() {
382 break;
383 }
384 param
387 .object
388 .data
389 .read(param.sub, 0, &mut data[offset..offset + length])
390 .ok();
391 offset += length;
392 }
393 self.buffered_value.store(Some(data));
396 }
397
398 fn try_create_mapping_entry(&self, mapping: PdoMapping) -> Result<MappingEntry<'a>, AbortCode> {
407 let PdoMapping {
408 index,
409 sub,
410 size: length,
411 } = mapping;
412 if (length % 8) != 0 {
414 return Err(AbortCode::IncompatibleParameter);
416 }
417 let entry = find_object_entry(self.od, index).ok_or(AbortCode::NoSuchObject)?;
418 let sub_info = entry.data.sub_info(sub)?;
419 if sub_info.size < length as usize / 8 {
420 return Err(AbortCode::IncompatibleParameter);
421 }
422 Ok(MappingEntry {
423 object: entry,
424 sub,
425 length: length / 8,
426 })
427 }
428
429 pub fn init_defaults(&'a self, node_id: NodeId) {
431 if self.defaults.is_none() {
432 return;
433 }
434 let defaults = self.defaults.unwrap();
435
436 self.node_id.store(node_id);
437 for (i, m) in defaults.mappings.iter().enumerate() {
438 if i >= self.mapping_params.len() {
439 return;
440 }
441 if let Ok(entry) = self.try_create_mapping_entry(PdoMapping::from_object_value(*m)) {
442 self.mapping_params[i].store(Some(entry));
443 }
444 }
445 self.valid_maps.store(defaults.mappings.len() as u8);
446
447 self.valid.store(defaults.valid());
448 self.cob_id.store(None);
450 self.rtr_disabled.store(defaults.rtr_disabled());
451 self.transmission_type.store(defaults.transmission_type);
452 }
453}
454
455struct PdoCobSubObject<'a> {
456 pdo: &'a Pdo<'a>,
457}
458
459impl<'a> PdoCobSubObject<'a> {
460 pub const fn new(pdo: &'a Pdo<'a>) -> Self {
461 Self { pdo }
462 }
463
464 pub fn should_persist(&self) -> bool {
468 self.pdo.cob_id.load().is_some()
469 }
470}
471
472impl SubObjectAccess for PdoCobSubObject<'_> {
473 fn read(&self, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
474 let cob_id = self.pdo.cob_id();
475 let mut value = cob_id.raw();
476 if cob_id.is_extended() {
477 value |= 1 << 29;
478 }
479 if self.pdo.rtr_disabled.load() {
480 value |= 1 << 30;
481 }
482 if !self.pdo.valid.load() {
483 value |= 1 << 31;
484 }
485
486 let bytes = value.to_le_bytes();
487 if offset < bytes.len() {
488 let read_len = buf.len().min(bytes.len() - offset);
489 buf[0..read_len].copy_from_slice(&bytes[offset..offset + read_len]);
490 Ok(read_len)
491 } else {
492 Ok(0)
493 }
494 }
495
496 fn read_size(&self) -> usize {
497 4
498 }
499
500 fn write(&self, data: &[u8]) -> Result<(), AbortCode> {
501 if self.pdo.nmt_state() != NmtState::PreOperational {
503 return Err(AbortCode::GeneralError);
504 }
505 if data.len() < 4 {
506 Err(AbortCode::DataTypeMismatchLengthLow)
507 } else if data.len() > 4 {
508 Err(AbortCode::DataTypeMismatchLengthHigh)
509 } else {
510 let value = u32::from_le_bytes(data.try_into().unwrap());
511 let not_valid = (value & (1 << 31)) != 0;
512 let no_rtr = (value & (1 << 30)) != 0;
513 let extended_id = (value & (1 << 29)) != 0;
514
515 let can_id = if extended_id {
516 CanId::Extended(value & 0x1FFFFFFF)
517 } else {
518 CanId::Std((value & 0x7FF) as u16)
519 };
520 self.pdo.cob_id.store(Some(can_id));
521 self.pdo.valid.store(!not_valid);
522 self.pdo.rtr_disabled.store(no_rtr);
523 Ok(())
524 }
525 }
526}
527
528struct PdoTransmissionTypeSubObject<'a> {
529 pdo: &'a Pdo<'a>,
530}
531
532impl<'a> PdoTransmissionTypeSubObject<'a> {
533 pub const fn new(pdo: &'a Pdo<'a>) -> Self {
534 Self { pdo }
535 }
536}
537
538impl SubObjectAccess for PdoTransmissionTypeSubObject<'_> {
539 fn read(&self, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
540 if offset > 1 {
541 return Ok(0);
542 }
543 buf[0] = self.pdo.transmission_type();
544 Ok(1)
545 }
546
547 fn read_size(&self) -> usize {
548 1
549 }
550
551 fn write(&self, data: &[u8]) -> Result<(), AbortCode> {
552 if self.pdo.nmt_state() != NmtState::PreOperational {
554 return Err(AbortCode::GeneralError);
555 }
556 if data.is_empty() {
557 Err(AbortCode::DataTypeMismatchLengthLow)
558 } else {
559 self.pdo.set_transmission_type(data[0]);
560 Ok(())
561 }
562 }
563}
564
565#[allow(missing_debug_implementations)]
567pub struct PdoCommObject<'a> {
568 cob: PdoCobSubObject<'a>,
569 transmission_type: PdoTransmissionTypeSubObject<'a>,
570}
571
572impl<'a> PdoCommObject<'a> {
573 pub const fn new(pdo: &'a Pdo<'a>) -> Self {
575 let cob = PdoCobSubObject::new(pdo);
576 let transmission_type = PdoTransmissionTypeSubObject::new(pdo);
577 Self {
578 cob,
579 transmission_type,
580 }
581 }
582}
583
584impl ProvidesSubObjects for PdoCommObject<'_> {
585 fn get_sub_object(&self, sub: u8) -> Option<(SubInfo, &dyn SubObjectAccess)> {
586 match sub {
587 0 => Some((
588 SubInfo::MAX_SUB_NUMBER,
589 const { &ConstField::new(2u8.to_le_bytes()) },
590 )),
591 1 => Some((
592 SubInfo::new_u32()
593 .rw_access()
594 .persist(self.cob.should_persist()),
595 &self.cob,
596 )),
597 2 => Some((
598 SubInfo::new_u8().rw_access().persist(true),
599 &self.transmission_type,
600 )),
601 _ => None,
602 }
603 }
604
605 fn object_code(&self) -> ObjectCode {
606 ObjectCode::Record
607 }
608}
609
610#[allow(missing_debug_implementations)]
612pub struct PdoMappingObject<'a> {
613 pdo: &'a Pdo<'a>,
614}
615
616impl<'a> PdoMappingObject<'a> {
617 pub const fn new(pdo: &'a Pdo<'a>) -> Self {
619 Self { pdo }
620 }
621}
622
623impl ObjectAccess for PdoMappingObject<'_> {
624 fn read(&self, sub: u8, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
625 if sub == 0 {
626 if offset < 1 && !buf.is_empty() {
627 buf[0] = self.pdo.valid_maps.load();
628 Ok(1)
629 } else {
630 Ok(0)
631 }
632 } else if sub <= self.pdo.mapping_params.len() as u8 {
633 let value = if let Some(param) = self.pdo.mapping_params[(sub - 1) as usize].load() {
634 ((param.object.index as u32) << 16)
635 + ((param.sub as u32) << 8)
636 + param.length as u32 * 8
637 } else {
638 0u32
639 };
640 let bytes = value.to_le_bytes();
641 let read_len = buf.len().min(bytes.len() - offset);
642 buf[..read_len].copy_from_slice(&bytes[offset..offset + read_len]);
643 Ok(read_len)
644 } else {
645 Err(AbortCode::NoSuchSubIndex)
646 }
647 }
648
649 fn read_size(&self, sub: u8) -> Result<usize, AbortCode> {
650 if sub == 0 {
651 Ok(1)
652 } else if sub <= N_MAPPING_PARAMS as u8 {
653 Ok(4)
654 } else {
655 Err(AbortCode::NoSuchSubIndex)
656 }
657 }
658
659 fn write(&self, sub: u8, data: &[u8]) -> Result<(), AbortCode> {
660 if self.pdo.nmt_state() != NmtState::PreOperational {
662 return Err(AbortCode::GeneralError);
663 }
664 if sub == 0 {
665 self.pdo.valid_maps.store(data[0]);
666 Ok(())
667 } else if sub <= self.pdo.mapping_params.len() as u8 {
668 if data.len() != 4 {
669 return Err(AbortCode::DataTypeMismatch);
670 }
671 let value = u32::from_le_bytes(data.try_into().unwrap());
672
673 let mapping = PdoMapping::from_object_value(value);
674
675 self.pdo.mapping_params[(sub - 1) as usize]
676 .store(Some(self.pdo.try_create_mapping_entry(mapping)?));
677 Ok(())
678 } else {
679 Err(AbortCode::NoSuchSubIndex)
680 }
681 }
682
683 fn object_code(&self) -> ObjectCode {
684 ObjectCode::Record
685 }
686
687 fn sub_info(&self, sub: u8) -> Result<SubInfo, AbortCode> {
688 if sub == 0 {
689 Ok(SubInfo {
690 size: 1,
691 data_type: DataType::UInt8,
692 access_type: AccessType::Rw,
693 pdo_mapping: PdoMappable::None,
694 persist: true,
695 })
696 } else if sub <= self.pdo.mapping_params.len() as u8 {
697 Ok(SubInfo {
698 size: 4,
699 data_type: DataType::UInt32,
700 access_type: AccessType::Rw,
701 pdo_mapping: PdoMappable::None,
702 persist: true,
703 })
704 } else {
705 Err(AbortCode::NoSuchSubIndex)
706 }
707 }
708}
709
710#[cfg(test)]
711mod tests {
712 use super::*;
713 use crate::object_dict::ScalarField;
714
715 #[derive(Default)]
716 struct TestObject {
717 value: ScalarField<u32>,
718 }
719
720 impl ProvidesSubObjects for TestObject {
721 fn get_sub_object(&self, sub: u8) -> Option<(SubInfo, &dyn SubObjectAccess)> {
722 match sub {
723 0 => Some((SubInfo::new_u32(), &self.value)),
724 _ => None,
725 }
726 }
727
728 fn object_code(&self) -> ObjectCode {
729 ObjectCode::Var
730 }
731 }
732
733 #[test]
734 pub fn test_changes_denied_while_operational() {
736 let object1000 = TestObject::default();
737 let od = &[ODEntry {
738 index: 0x1000,
739 data: &object1000,
740 }];
741 let nmt_state = AtomicCell::new(NmtState::PreOperational);
742
743 let pdo = Pdo::new(od, &nmt_state);
744
745 let comm_obj = PdoCommObject::new(&pdo);
746 let mapping_obj = PdoMappingObject::new(&pdo);
747
748 mapping_obj
750 .write(1, &((0x1000 << 16) | 32 as u32).to_le_bytes())
751 .unwrap();
752 mapping_obj.write(0, &[1]).unwrap();
753 comm_obj.write(1, &(1u32 << 31).to_le_bytes()).unwrap();
754
755 nmt_state.store(NmtState::Operational);
756
757 let result = mapping_obj.write(1, &0u32.to_le_bytes());
759 assert_eq!(Err(AbortCode::GeneralError), result);
760 let result = comm_obj.write(1, &0u32.to_le_bytes());
761 assert_eq!(Err(AbortCode::GeneralError), result);
762 let result = comm_obj.write(2, &0u32.to_le_bytes());
763 assert_eq!(Err(AbortCode::GeneralError), result);
764 }
765}