1use crate::object_dict::{
44 find_object_entry, ConstField, ODEntry, ObjectAccess, ProvidesSubObjects, SubObjectAccess,
45};
46use zencan_common::{
47 objects::{AccessType, DataType, ObjectCode, PdoMappable, SubInfo},
48 pdo::PdoMapping,
49 sdo::AbortCode,
50 AtomicCell, CanId, NodeId,
51};
52
53const N_MAPPING_PARAMS: usize = 8;
58
59#[derive(Clone, Copy)]
60struct MappingEntry {
62 pub object: &'static ODEntry<'static>,
64 pub sub: u8,
66 pub length: u8,
68}
69
70#[allow(missing_debug_implementations)]
71#[derive(Copy, Clone)]
73pub struct PdoDefaults {
74 cob_id: u32,
75 flags: u8,
76 transmission_type: u8,
77 mappings: &'static [u32],
78}
79
80impl Default for PdoDefaults {
81 fn default() -> Self {
82 Self::DEFAULT
83 }
84}
85
86#[allow(missing_docs)]
87impl PdoDefaults {
88 const ADD_NODE_ID_FLAG: usize = 0;
89 const VALID_FLAG: usize = 1;
90 const RTR_DISABLED_FLAG: usize = 2;
91 const IS_EXTENDED_FLAG: usize = 3;
92
93 pub const DEFAULT: PdoDefaults = Self {
95 cob_id: 0,
96 flags: 0,
97 transmission_type: 0,
98 mappings: &[],
99 };
100
101 pub const fn new(
103 cob_id: u32,
104 extended: bool,
105 add_node_id: bool,
106 valid: bool,
107 rtr_disabled: bool,
108 transmission_type: u8,
109 mappings: &'static [u32],
110 ) -> Self {
111 let mut flags = 0u8;
113 if valid {
114 flags |= 1 << Self::VALID_FLAG;
115 }
116 if rtr_disabled {
117 flags |= 1 << Self::RTR_DISABLED_FLAG;
118 }
119 if add_node_id {
120 flags |= 1 << Self::ADD_NODE_ID_FLAG;
121 }
122 if extended {
123 flags |= 1 << Self::IS_EXTENDED_FLAG;
124 }
125
126 Self {
127 cob_id,
128 flags,
129 transmission_type,
130 mappings,
131 }
132 }
133
134 pub const fn valid(&self) -> bool {
135 self.flags & (1 << Self::VALID_FLAG) != 0
136 }
137
138 pub const fn rtr_disabled(&self) -> bool {
139 self.flags & (1 << Self::RTR_DISABLED_FLAG) != 0
140 }
141
142 pub const fn add_node_id(&self) -> bool {
143 self.flags & (1 << Self::ADD_NODE_ID_FLAG) != 0
144 }
145
146 pub const fn extended(&self) -> bool {
147 self.flags & (1 << Self::IS_EXTENDED_FLAG) != 0
148 }
149
150 pub const fn can_id(&self, node_id: u8) -> CanId {
151 let id = if self.add_node_id() {
152 self.cob_id + node_id as u32
153 } else {
154 self.cob_id
155 };
156 if self.extended() {
157 CanId::Extended(id)
158 } else {
159 CanId::Std(id as u16)
160 }
161 }
162}
163
164#[allow(missing_debug_implementations)]
166pub struct Pdo {
167 od: &'static [ODEntry<'static>],
171 node_id: AtomicCell<NodeId>,
173 cob_id: AtomicCell<Option<CanId>>,
175 valid: AtomicCell<bool>,
177 rtr_disabled: AtomicCell<bool>,
179 transmission_type: AtomicCell<u8>,
186 sync_counter: AtomicCell<u8>,
188 pub buffered_value: AtomicCell<Option<[u8; 8]>>,
190 valid_maps: AtomicCell<u8>,
194 mapping_params: [AtomicCell<Option<MappingEntry>>; N_MAPPING_PARAMS],
198 defaults: Option<&'static PdoDefaults>,
200}
201
202impl Pdo {
203 pub const fn new(od: &'static [ODEntry<'static>]) -> Self {
205 let cob_id = AtomicCell::new(None);
206 let node_id = AtomicCell::new(NodeId::Unconfigured);
207 let valid = AtomicCell::new(false);
208 let rtr_disabled = AtomicCell::new(false);
209 let transmission_type = AtomicCell::new(0);
210 let sync_counter = AtomicCell::new(0);
211 let buffered_value = AtomicCell::new(None);
212 let valid_maps = AtomicCell::new(0);
213 let mapping_params = [const { AtomicCell::new(None) }; N_MAPPING_PARAMS];
214 let defaults = None;
215 Self {
216 od,
217 node_id,
218 cob_id,
219 valid,
220 rtr_disabled,
221 transmission_type,
222 sync_counter,
223 buffered_value,
224 valid_maps,
225 mapping_params,
226 defaults,
227 }
228 }
229
230 pub const fn new_with_defaults(
232 od: &'static [ODEntry<'static>],
233 defaults: &'static PdoDefaults,
234 ) -> Self {
235 let mut pdo = Pdo::new(od);
236 pdo.defaults = Some(defaults);
237 pdo
238 }
239
240 pub fn set_valid(&self, value: bool) {
242 self.valid.store(value);
243 }
244
245 pub fn valid(&self) -> bool {
247 self.valid.load()
248 }
249
250 pub fn set_transmission_type(&self, value: u8) {
252 self.transmission_type.store(value);
253 }
254
255 pub fn transmission_type(&self) -> u8 {
257 self.transmission_type.load()
258 }
259
260 pub fn cob_id(&self) -> CanId {
262 self.cob_id.load().unwrap_or(self.default_cob_id())
263 }
264
265 pub fn default_cob_id(&self) -> CanId {
267 if self.defaults.is_none() {
268 return CanId::std(0);
269 }
270 let defaults = self.defaults.unwrap();
271 let node_id = match self.node_id.load() {
272 NodeId::Unconfigured => 0,
273 NodeId::Configured(node_id) => node_id.raw(),
274 };
275 defaults.can_id(node_id)
276 }
277
278 pub fn sync_update(&self) -> bool {
282 if !self.valid.load() {
283 return false;
284 }
285
286 let transmission_type = self.transmission_type.load();
287 if transmission_type == 0 {
288 true
291 } else if transmission_type <= 240 {
292 let cnt = self.sync_counter.fetch_add(1) + 1;
293 cnt == transmission_type
294 } else {
295 false
296 }
297 }
298
299 pub fn read_events(&self) -> bool {
301 if !self.valid.load() {
302 return false;
303 }
304
305 for i in 0..self.mapping_params.len() {
306 let param = self.mapping_params[i].load();
307 if param.is_none() {
308 break;
309 }
310 let param = param.unwrap();
311 if param.object.data.read_event_flag(param.sub) {
312 return true;
313 }
314 }
315 false
316 }
317
318 pub(crate) fn clear_events(&self) {
319 for i in 0..self.mapping_params.len() {
320 let param = self.mapping_params[i].load();
321 if param.is_none() {
322 break;
323 }
324 let param = param.unwrap();
325 param.object.data.clear_events();
326 }
327 }
328
329 pub(crate) fn store_pdo_data(&self, data: &[u8]) {
330 let mut offset = 0;
331 let valid_maps = self.valid_maps.load() as usize;
332 for (i, param) in self.mapping_params.iter().enumerate() {
333 if i >= valid_maps {
334 break;
335 }
336 let param = param.load();
337 if param.is_none() {
338 break;
339 }
340 let param = param.unwrap();
341 let length = param.length as usize;
342 if offset + length > data.len() {
343 break;
344 }
345 let data_to_write = &data[offset..offset + length];
346 param.object.data.write(param.sub, data_to_write).ok();
349 offset += length;
350 }
351 }
352
353 pub(crate) fn send_pdo(&self) {
354 let mut data = [0u8; 8];
355 let mut offset = 0;
356 let valid_maps = self.valid_maps.load() as usize;
357 for (i, param) in self.mapping_params.iter().enumerate() {
358 if i >= valid_maps {
359 break;
360 }
361 let param = param.load();
362 if param.is_none() {
365 break;
366 }
367 let param = param.unwrap();
368 let length = param.length as usize;
369 if offset + length > data.len() {
370 break;
371 }
372 param
375 .object
376 .data
377 .read(param.sub, 0, &mut data[offset..offset + length])
378 .ok();
379 offset += length;
380 }
381 self.buffered_value.store(Some(data));
384 }
385
386 fn try_create_mapping_entry(&self, mapping: PdoMapping) -> Result<MappingEntry, AbortCode> {
395 let PdoMapping {
396 index,
397 sub,
398 size: length,
399 } = mapping;
400 if (length % 8) != 0 {
402 return Err(AbortCode::IncompatibleParameter);
404 }
405 let entry = find_object_entry(self.od, index).ok_or(AbortCode::NoSuchObject)?;
406 let sub_info = entry.data.sub_info(sub)?;
407 if sub_info.size < length as usize / 8 {
408 return Err(AbortCode::IncompatibleParameter);
409 }
410 Ok(MappingEntry {
411 object: entry,
412 sub,
413 length: length / 8,
414 })
415 }
416
417 pub fn init_defaults(&self, node_id: NodeId) {
419 if self.defaults.is_none() {
420 return;
421 }
422 let defaults = self.defaults.unwrap();
423
424 self.node_id.store(node_id);
425 for (i, m) in defaults.mappings.iter().enumerate() {
426 if i >= self.mapping_params.len() {
427 return;
428 }
429 if let Ok(entry) = self.try_create_mapping_entry(PdoMapping::from_object_value(*m)) {
430 self.mapping_params[i].store(Some(entry));
431 }
432 }
433 self.valid_maps.store(defaults.mappings.len() as u8);
434
435 self.valid.store(defaults.valid());
436 self.cob_id.store(None);
438 self.rtr_disabled.store(defaults.rtr_disabled());
439 self.transmission_type.store(defaults.transmission_type);
440 }
441}
442
443struct PdoCobSubObject {
444 pdo: &'static Pdo,
445}
446
447impl PdoCobSubObject {
448 pub const fn new(pdo: &'static Pdo) -> Self {
449 Self { pdo }
450 }
451
452 pub fn should_persist(&self) -> bool {
456 self.pdo.cob_id.load().is_some()
457 }
458}
459
460impl SubObjectAccess for PdoCobSubObject {
461 fn read(&self, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
462 let cob_id = self.pdo.cob_id();
463 let mut value = cob_id.raw();
464 if cob_id.is_extended() {
465 value |= 1 << 29;
466 }
467 if self.pdo.rtr_disabled.load() {
468 value |= 1 << 30;
469 }
470 if !self.pdo.valid.load() {
471 value |= 1 << 31;
472 }
473
474 let bytes = value.to_le_bytes();
475 if offset < bytes.len() {
476 let read_len = buf.len().min(bytes.len() - offset);
477 buf[0..read_len].copy_from_slice(&bytes[offset..offset + read_len]);
478 Ok(read_len)
479 } else {
480 Ok(0)
481 }
482 }
483
484 fn read_size(&self) -> usize {
485 4
486 }
487
488 fn write(&self, data: &[u8]) -> Result<(), AbortCode> {
489 if data.len() < 4 {
490 Err(AbortCode::DataTypeMismatchLengthLow)
491 } else if data.len() > 4 {
492 Err(AbortCode::DataTypeMismatchLengthHigh)
493 } else {
494 let value = u32::from_le_bytes(data.try_into().unwrap());
495 let not_valid = (value & (1 << 31)) != 0;
496 let no_rtr = (value & (1 << 30)) != 0;
497 let extended_id = (value & (1 << 29)) != 0;
498
499 let can_id = if extended_id {
500 CanId::Extended(value & 0x1FFFFFFF)
501 } else {
502 CanId::Std((value & 0x7FF) as u16)
503 };
504 self.pdo.cob_id.store(Some(can_id));
505 self.pdo.valid.store(!not_valid);
506 self.pdo.rtr_disabled.store(no_rtr);
507 Ok(())
508 }
509 }
510}
511
512struct PdoTransmissionTypeSubObject {
513 pdo: &'static Pdo,
514}
515
516impl PdoTransmissionTypeSubObject {
517 pub const fn new(pdo: &'static Pdo) -> Self {
518 Self { pdo }
519 }
520}
521
522impl SubObjectAccess for PdoTransmissionTypeSubObject {
523 fn read(&self, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
524 if offset > 1 {
525 return Ok(0);
526 }
527 buf[0] = self.pdo.transmission_type();
528 Ok(1)
529 }
530
531 fn read_size(&self) -> usize {
532 1
533 }
534
535 fn write(&self, data: &[u8]) -> Result<(), AbortCode> {
536 if data.is_empty() {
537 Err(AbortCode::DataTypeMismatchLengthLow)
538 } else {
539 self.pdo.set_transmission_type(data[0]);
540 Ok(())
541 }
542 }
543}
544
545#[allow(missing_debug_implementations)]
547pub struct PdoCommObject {
548 cob: PdoCobSubObject,
549 transmission_type: PdoTransmissionTypeSubObject,
550}
551
552impl PdoCommObject {
553 pub const fn new(pdo: &'static Pdo) -> Self {
555 let cob = PdoCobSubObject::new(pdo);
556 let transmission_type = PdoTransmissionTypeSubObject::new(pdo);
557 Self {
558 cob,
559 transmission_type,
560 }
561 }
562}
563
564impl ProvidesSubObjects for PdoCommObject {
565 fn get_sub_object(&self, sub: u8) -> Option<(SubInfo, &dyn SubObjectAccess)> {
566 match sub {
567 0 => Some((
568 SubInfo::MAX_SUB_NUMBER,
569 const { &ConstField::new(2u8.to_le_bytes()) },
570 )),
571 1 => Some((
572 SubInfo::new_u32()
573 .rw_access()
574 .persist(self.cob.should_persist()),
575 &self.cob,
576 )),
577 2 => Some((
578 SubInfo::new_u8().rw_access().persist(true),
579 &self.transmission_type,
580 )),
581 _ => None,
582 }
583 }
584
585 fn object_code(&self) -> ObjectCode {
586 ObjectCode::Record
587 }
588}
589
590#[allow(missing_debug_implementations)]
592pub struct PdoMappingObject {
593 pdo: &'static Pdo,
594}
595
596impl PdoMappingObject {
597 pub const fn new(pdo: &'static Pdo) -> Self {
599 Self { pdo }
600 }
601}
602
603impl ObjectAccess for PdoMappingObject {
604 fn read(&self, sub: u8, offset: usize, buf: &mut [u8]) -> Result<usize, AbortCode> {
605 if sub == 0 {
606 if offset < 1 && !buf.is_empty() {
607 buf[0] = self.pdo.valid_maps.load();
608 Ok(1)
609 } else {
610 Ok(0)
611 }
612 } else if sub <= self.pdo.mapping_params.len() as u8 {
613 let value = if let Some(param) = self.pdo.mapping_params[(sub - 1) as usize].load() {
614 ((param.object.index as u32) << 16)
615 + ((param.sub as u32) << 8)
616 + param.length as u32 * 8
617 } else {
618 0u32
619 };
620 let bytes = value.to_le_bytes();
621 let read_len = buf.len().min(bytes.len() - offset);
622 buf[..read_len].copy_from_slice(&bytes[offset..offset + read_len]);
623 Ok(read_len)
624 } else {
625 Err(AbortCode::NoSuchSubIndex)
626 }
627 }
628
629 fn read_size(&self, sub: u8) -> Result<usize, AbortCode> {
630 if sub == 0 {
631 Ok(1)
632 } else if sub <= N_MAPPING_PARAMS as u8 {
633 Ok(4)
634 } else {
635 Err(AbortCode::NoSuchSubIndex)
636 }
637 }
638
639 fn write(&self, sub: u8, data: &[u8]) -> Result<(), AbortCode> {
640 if sub == 0 {
641 self.pdo.valid_maps.store(data[0]);
642 Ok(())
643 } else if sub <= self.pdo.mapping_params.len() as u8 {
644 if data.len() != 4 {
645 return Err(AbortCode::DataTypeMismatch);
646 }
647 let value = u32::from_le_bytes(data.try_into().unwrap());
648
649 let mapping = PdoMapping::from_object_value(value);
650
651 self.pdo.mapping_params[(sub - 1) as usize]
652 .store(Some(self.pdo.try_create_mapping_entry(mapping)?));
653 Ok(())
654 } else {
655 Err(AbortCode::NoSuchSubIndex)
656 }
657 }
658
659 fn object_code(&self) -> ObjectCode {
660 ObjectCode::Record
661 }
662
663 fn sub_info(&self, sub: u8) -> Result<SubInfo, AbortCode> {
664 if sub == 0 {
665 Ok(SubInfo {
666 size: 1,
667 data_type: DataType::UInt8,
668 access_type: AccessType::Rw,
669 pdo_mapping: PdoMappable::None,
670 persist: true,
671 })
672 } else if sub <= self.pdo.mapping_params.len() as u8 {
673 Ok(SubInfo {
674 size: 4,
675 data_type: DataType::UInt32,
676 access_type: AccessType::Rw,
677 pdo_mapping: PdoMappable::None,
678 persist: true,
679 })
680 } else {
681 Err(AbortCode::NoSuchSubIndex)
682 }
683 }
684}