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 r = self.sync_counter.fetch_update(|old| {
304 let new = old + 1;
305 if new >= transmission_type {
306 Some(0)
307 } else {
308 Some(new)
309 }
310 });
311
312 let cnt = match r {
316 Ok(old) => old,
317 Err(old) => old,
318 } + 1;
319
320 cnt == transmission_type
321 } else {
322 false
323 }
324 }
325
326 pub fn read_events(&self) -> bool {
328 if !self.valid.load() {
329 return false;
330 }
331
332 for i in 0..self.mapping_params.len() {
333 let param = self.mapping_params[i].load();
334 if param.is_none() {
335 break;
336 }
337 let param = param.unwrap();
338 if param.object.data.read_event_flag(param.sub) {
339 return true;
340 }
341 }
342 false
343 }
344
345 fn nmt_state(&self) -> NmtState {
346 self.nmt_state.nmt_state()
347 }
348
349 pub(crate) fn clear_events(&self) {
350 for i in 0..self.mapping_params.len() {
351 let param = self.mapping_params[i].load();
352 if param.is_none() {
353 break;
354 }
355 let param = param.unwrap();
356 param.object.data.clear_events();
357 }
358 }
359
360 pub(crate) fn store_pdo_data(&self, data: &[u8]) {
361 let mut offset = 0;
362 let valid_maps = self.valid_maps.load() as usize;
363 for (i, param) in self.mapping_params.iter().enumerate() {
364 if i >= valid_maps {
365 break;
366 }
367 let param = param.load();
368 if param.is_none() {
369 break;
370 }
371 let param = param.unwrap();
372 let length = param.length as usize;
373 if offset + length > data.len() {
374 break;
375 }
376 let data_to_write = &data[offset..offset + length];
377 param.object.data.write(param.sub, data_to_write).ok();
380 offset += length;
381 }
382 }
383
384 pub(crate) fn send_pdo(&self) {
385 let mut data = [0u8; 8];
386 let mut offset = 0;
387 let valid_maps = self.valid_maps.load() as usize;
388 for (i, param) in self.mapping_params.iter().enumerate() {
389 if i >= valid_maps {
390 break;
391 }
392 let param = param.load();
393 if param.is_none() {
396 break;
397 }
398 let param = param.unwrap();
399 let length = param.length as usize;
400 if offset + length > data.len() {
401 break;
402 }
403 param
406 .object
407 .data
408 .read(param.sub, 0, &mut data[offset..offset + length])
409 .ok();
410 offset += length;
411 }
412 self.buffered_value.store(Some(data));
415 }
416
417 fn try_create_mapping_entry(&self, mapping: PdoMapping) -> Result<MappingEntry<'a>, AbortCode> {
426 let PdoMapping {
427 index,
428 sub,
429 size: length,
430 } = mapping;
431 if (length % 8) != 0 {
433 return Err(AbortCode::IncompatibleParameter);
435 }
436 let entry = find_object_entry(self.od, index).ok_or(AbortCode::NoSuchObject)?;
437 let sub_info = entry.data.sub_info(sub)?;
438 if sub_info.size < length as usize / 8 {
439 return Err(AbortCode::IncompatibleParameter);
440 }
441 Ok(MappingEntry {
442 object: entry,
443 sub,
444 length: length / 8,
445 })
446 }
447
448 pub fn init_defaults(&'a self, node_id: NodeId) {
450 if self.defaults.is_none() {
451 return;
452 }
453 let defaults = self.defaults.unwrap();
454
455 self.node_id.store(node_id);
456 for (i, m) in defaults.mappings.iter().enumerate() {
457 if i >= self.mapping_params.len() {
458 return;
459 }
460 if let Ok(entry) = self.try_create_mapping_entry(PdoMapping::from_object_value(*m)) {
461 self.mapping_params[i].store(Some(entry));
462 }
463 }
464 self.valid_maps.store(defaults.mappings.len() as u8);
465
466 self.valid.store(defaults.valid());
467 self.cob_id.store(None);
469 self.rtr_disabled.store(defaults.rtr_disabled());
470 self.transmission_type.store(defaults.transmission_type);
471 }
472}
473
474struct PdoCobSubObject<'a> {
475 pdo: &'a Pdo<'a>,
476}
477
478impl<'a> PdoCobSubObject<'a> {
479 pub const fn new(pdo: &'a Pdo<'a>) -> Self {
480 Self { pdo }
481 }
482
483 pub fn should_persist(&self) -> bool {
487 self.pdo.cob_id.load().is_some()
488 }
489}
490
491impl SubObjectAccess for PdoCobSubObject<'_> {
492 fn read(&self, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
493 let cob_id = self.pdo.cob_id();
494 let mut value = cob_id.raw();
495 if cob_id.is_extended() {
496 value |= 1 << 29;
497 }
498 if self.pdo.rtr_disabled.load() {
499 value |= 1 << 30;
500 }
501 if !self.pdo.valid.load() {
502 value |= 1 << 31;
503 }
504
505 let bytes = value.to_le_bytes();
506 if offset < bytes.len() {
507 let read_len = buf.len().min(bytes.len() - offset);
508 buf[0..read_len].copy_from_slice(&bytes[offset..offset + read_len]);
509 Ok(read_len)
510 } else {
511 Ok(0)
512 }
513 }
514
515 fn read_size(&self) -> usize {
516 4
517 }
518
519 fn write(&self, data: &[u8]) -> Result<(), AbortCode> {
520 let nmt_state = self.pdo.nmt_state();
523 if nmt_state != NmtState::PreOperational && nmt_state != NmtState::Bootup {
524 return Err(AbortCode::GeneralError);
525 }
526 if data.len() < 4 {
527 Err(AbortCode::DataTypeMismatchLengthLow)
528 } else if data.len() > 4 {
529 Err(AbortCode::DataTypeMismatchLengthHigh)
530 } else {
531 let value = u32::from_le_bytes(data.try_into().unwrap());
532 let not_valid = (value & (1 << 31)) != 0;
533 let no_rtr = (value & (1 << 30)) != 0;
534 let extended_id = (value & (1 << 29)) != 0;
535
536 let can_id = if extended_id {
537 CanId::Extended(value & 0x1FFFFFFF)
538 } else {
539 CanId::Std((value & 0x7FF) as u16)
540 };
541 self.pdo.cob_id.store(Some(can_id));
542 self.pdo.valid.store(!not_valid);
543 self.pdo.rtr_disabled.store(no_rtr);
544 Ok(())
545 }
546 }
547}
548
549struct PdoTransmissionTypeSubObject<'a> {
550 pdo: &'a Pdo<'a>,
551}
552
553impl<'a> PdoTransmissionTypeSubObject<'a> {
554 pub const fn new(pdo: &'a Pdo<'a>) -> Self {
555 Self { pdo }
556 }
557}
558
559impl SubObjectAccess for PdoTransmissionTypeSubObject<'_> {
560 fn read(&self, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
561 if offset > 1 {
562 return Ok(0);
563 }
564 buf[0] = self.pdo.transmission_type();
565 Ok(1)
566 }
567
568 fn read_size(&self) -> usize {
569 1
570 }
571
572 fn write(&self, data: &[u8]) -> Result<(), AbortCode> {
573 let nmt_state = self.pdo.nmt_state();
576 if nmt_state != NmtState::PreOperational && nmt_state != NmtState::Bootup {
577 return Err(AbortCode::GeneralError);
578 }
579 if data.is_empty() {
580 Err(AbortCode::DataTypeMismatchLengthLow)
581 } else {
582 self.pdo.set_transmission_type(data[0]);
583 Ok(())
584 }
585 }
586}
587
588#[allow(missing_debug_implementations)]
590pub struct PdoCommObject<'a> {
591 cob: PdoCobSubObject<'a>,
592 transmission_type: PdoTransmissionTypeSubObject<'a>,
593}
594
595impl<'a> PdoCommObject<'a> {
596 pub const fn new(pdo: &'a Pdo<'a>) -> Self {
598 let cob = PdoCobSubObject::new(pdo);
599 let transmission_type = PdoTransmissionTypeSubObject::new(pdo);
600 Self {
601 cob,
602 transmission_type,
603 }
604 }
605}
606
607impl ProvidesSubObjects for PdoCommObject<'_> {
608 fn get_sub_object(&self, sub: u8) -> Option<(SubInfo, &dyn SubObjectAccess)> {
609 match sub {
610 0 => Some((
611 SubInfo::MAX_SUB_NUMBER,
612 const { &ConstField::new(2u8.to_le_bytes()) },
613 )),
614 1 => Some((
615 SubInfo::new_u32()
616 .rw_access()
617 .persist(self.cob.should_persist()),
618 &self.cob,
619 )),
620 2 => Some((
621 SubInfo::new_u8().rw_access().persist(true),
622 &self.transmission_type,
623 )),
624 _ => None,
625 }
626 }
627
628 fn object_code(&self) -> ObjectCode {
629 ObjectCode::Record
630 }
631}
632
633#[allow(missing_debug_implementations)]
635pub struct PdoMappingObject<'a> {
636 pdo: &'a Pdo<'a>,
637}
638
639impl<'a> PdoMappingObject<'a> {
640 pub const fn new(pdo: &'a Pdo<'a>) -> Self {
642 Self { pdo }
643 }
644}
645
646impl ObjectAccess for PdoMappingObject<'_> {
647 fn read(&self, sub: u8, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
648 if sub == 0 {
649 if offset < 1 && !buf.is_empty() {
650 buf[0] = self.pdo.valid_maps.load();
651 Ok(1)
652 } else {
653 Ok(0)
654 }
655 } else if sub <= self.pdo.mapping_params.len() as u8 {
656 let value = if let Some(param) = self.pdo.mapping_params[(sub - 1) as usize].load() {
657 ((param.object.index as u32) << 16)
658 + ((param.sub as u32) << 8)
659 + param.length as u32 * 8
660 } else {
661 0u32
662 };
663 let bytes = value.to_le_bytes();
664 let read_len = buf.len().min(bytes.len() - offset);
665 buf[..read_len].copy_from_slice(&bytes[offset..offset + read_len]);
666 Ok(read_len)
667 } else {
668 Err(AbortCode::NoSuchSubIndex)
669 }
670 }
671
672 fn read_size(&self, sub: u8) -> Result<usize, AbortCode> {
673 if sub == 0 {
674 Ok(1)
675 } else if sub <= N_MAPPING_PARAMS as u8 {
676 Ok(4)
677 } else {
678 Err(AbortCode::NoSuchSubIndex)
679 }
680 }
681
682 fn write(&self, sub: u8, data: &[u8]) -> Result<(), AbortCode> {
683 let nmt_state = self.pdo.nmt_state();
686 if nmt_state != NmtState::PreOperational && nmt_state != NmtState::Bootup {
687 return Err(AbortCode::GeneralError);
688 }
689 if sub == 0 {
690 self.pdo.valid_maps.store(data[0]);
691 Ok(())
692 } else if sub <= self.pdo.mapping_params.len() as u8 {
693 if data.len() != 4 {
694 return Err(AbortCode::DataTypeMismatch);
695 }
696 let value = u32::from_le_bytes(data.try_into().unwrap());
697
698 let mapping = PdoMapping::from_object_value(value);
699
700 self.pdo.mapping_params[(sub - 1) as usize]
701 .store(Some(self.pdo.try_create_mapping_entry(mapping)?));
702 Ok(())
703 } else {
704 Err(AbortCode::NoSuchSubIndex)
705 }
706 }
707
708 fn object_code(&self) -> ObjectCode {
709 ObjectCode::Record
710 }
711
712 fn sub_info(&self, sub: u8) -> Result<SubInfo, AbortCode> {
713 if sub == 0 {
714 Ok(SubInfo {
715 size: 1,
716 data_type: DataType::UInt8,
717 access_type: AccessType::Rw,
718 pdo_mapping: PdoMappable::None,
719 persist: true,
720 })
721 } else if sub <= self.pdo.mapping_params.len() as u8 {
722 Ok(SubInfo {
723 size: 4,
724 data_type: DataType::UInt32,
725 access_type: AccessType::Rw,
726 pdo_mapping: PdoMappable::None,
727 persist: true,
728 })
729 } else {
730 Err(AbortCode::NoSuchSubIndex)
731 }
732 }
733}
734
735#[cfg(test)]
736mod tests {
737 use super::*;
738 use crate::object_dict::ScalarField;
739
740 #[derive(Default)]
741 struct TestObject {
742 value: ScalarField<u32>,
743 }
744
745 impl ProvidesSubObjects for TestObject {
746 fn get_sub_object(&self, sub: u8) -> Option<(SubInfo, &dyn SubObjectAccess)> {
747 match sub {
748 0 => Some((SubInfo::new_u32(), &self.value)),
749 _ => None,
750 }
751 }
752
753 fn object_code(&self) -> ObjectCode {
754 ObjectCode::Var
755 }
756 }
757
758 #[test]
759 pub fn test_changes_denied_while_operational() {
761 let object1000 = TestObject::default();
762 let od = &[ODEntry {
763 index: 0x1000,
764 data: &object1000,
765 }];
766 let nmt_state = AtomicCell::new(NmtState::PreOperational);
767
768 let pdo = Pdo::new(od, &nmt_state);
769
770 let comm_obj = PdoCommObject::new(&pdo);
771 let mapping_obj = PdoMappingObject::new(&pdo);
772
773 mapping_obj
775 .write(1, &((0x1000 << 16) | 32 as u32).to_le_bytes())
776 .unwrap();
777 mapping_obj.write(0, &[1]).unwrap();
778 comm_obj.write(1, &(1u32 << 31).to_le_bytes()).unwrap();
779
780 nmt_state.store(NmtState::Operational);
781
782 let result = mapping_obj.write(1, &0u32.to_le_bytes());
784 assert_eq!(Err(AbortCode::GeneralError), result);
785 let result = comm_obj.write(1, &0u32.to_le_bytes());
786 assert_eq!(Err(AbortCode::GeneralError), result);
787 let result = comm_obj.write(2, &0u32.to_le_bytes());
788 assert_eq!(Err(AbortCode::GeneralError), result);
789 }
790}