autosar_data_abstraction/communication/pdu/
mod.rs

1use crate::communication::{
2    AbstractPhysicalChannel, CommunicationDirection, ISignal, ISignalGroup, ISignalTriggering, PhysicalChannel,
3    TransferProperty,
4};
5use crate::{
6    AbstractionElement, ArPackage, AutosarAbstractionError, ByteOrder, EcuInstance, IdentifiableAbstractionElement,
7    abstraction_element, make_unique_name,
8};
9use autosar_data::{AutosarDataError, Element, ElementName, EnumItem};
10use std::str::FromStr;
11
12mod container_ipdu;
13mod isignal_ipdu;
14mod secured_ipdu;
15
16pub use container_ipdu::*;
17pub use isignal_ipdu::*;
18pub use secured_ipdu::*;
19
20//##################################################################
21
22/// This trait is implemented by all Pdus
23pub trait AbstractPdu: AbstractionElement + Into<Pdu> {
24    /// set the length of the PDU
25    fn set_length(&self, length: u32) -> Result<(), AutosarAbstractionError> {
26        self.element()
27            .get_or_create_sub_element(ElementName::Length)?
28            .set_character_data(length as u64)?;
29        Ok(())
30    }
31
32    /// get the length of the PDU
33    fn length(&self) -> Option<u32> {
34        self.element()
35            .get_sub_element(ElementName::Length)?
36            .character_data()?
37            .parse_integer()
38    }
39
40    /// list all `PduTriggerings` that trigger this PDU
41    fn pdu_triggerings(&self) -> Vec<PduTriggering> {
42        let model_result = self.element().model();
43        let path_result = self.element().path();
44        if let (Ok(model), Ok(path)) = (model_result, path_result) {
45            model
46                .get_references_to(&path)
47                .iter()
48                .filter_map(|e| {
49                    e.upgrade()
50                        .and_then(|ref_elem| ref_elem.named_parent().ok().flatten())
51                        .and_then(|elem| PduTriggering::try_from(elem).ok())
52                })
53                .collect()
54        } else {
55            vec![]
56        }
57    }
58}
59
60//##################################################################
61
62/// for now this is a marker trait to identify `IPdus`
63pub trait AbstractIpdu: AbstractPdu + Into<IPdu> {
64    /// set the `ContainedIPduProps` for this `IPdu`
65    ///
66    /// This is only relevant for `IPdus` that will be transmitted in `ContainerIPdus`
67    fn set_contained_ipdu_props(&self, props: Option<&ContainedIPduProps>) -> Result<(), AutosarAbstractionError> {
68        ContainedIPduProps::set_props(self.element(), props)
69    }
70
71    /// get the `ContainedIPduProps` for this `IPdu`
72    #[must_use]
73    fn contained_ipdu_props(&self) -> Option<ContainedIPduProps> {
74        ContainedIPduProps::get_props(self.element())
75    }
76}
77
78//##################################################################
79
80/// Network Management Pdu
81#[derive(Debug, Clone, PartialEq, Eq, Hash)]
82pub struct NmPdu(Element);
83abstraction_element!(NmPdu, NmPdu);
84impl IdentifiableAbstractionElement for NmPdu {}
85
86impl NmPdu {
87    pub(crate) fn new(name: &str, package: &ArPackage, length: u32) -> Result<Self, AutosarAbstractionError> {
88        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
89        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::NmPdu, name)?;
90        elem_pdu
91            .create_sub_element(ElementName::Length)?
92            .set_character_data(length.to_string())?;
93
94        Ok(Self(elem_pdu))
95    }
96
97    /// set the unused bit pattern for this NmPdu
98    pub fn set_unused_bit_pattern(&self, pattern: u8) -> Result<(), AutosarAbstractionError> {
99        self.element()
100            .get_or_create_sub_element(ElementName::UnusedBitPattern)?
101            .set_character_data(pattern.to_string())?;
102        Ok(())
103    }
104
105    /// get the unused bit pattern for this NmPdu
106    #[must_use]
107    pub fn unused_bit_pattern(&self) -> Option<u8> {
108        self.element()
109            .get_sub_element(ElementName::UnusedBitPattern)?
110            .character_data()?
111            .parse_integer()
112    }
113
114    /// map a signal to the `ISignalIPdu`
115    ///
116    /// If this signal is part of a signal group, then the group must be mapped first
117    pub fn map_signal(
118        &self,
119        signal: &ISignal,
120        start_position: u32,
121        byte_order: ByteOrder,
122        update_bit: Option<u32>,
123        transfer_property: TransferProperty,
124    ) -> Result<ISignalToIPduMapping, AutosarAbstractionError> {
125        let signal_name = signal
126            .name()
127            .ok_or(AutosarAbstractionError::InvalidParameter("invalid signal".to_string()))?;
128
129        verify_signal_mapping(self, signal, start_position, byte_order, update_bit, &signal_name)?;
130
131        // add a pdu triggering for the newly mapped PDU to each frame triggering of this frame
132        for pt in self.pdu_triggerings() {
133            let st = pt.create_signal_triggering(signal)?;
134            for pdu_port in pt.pdu_ports() {
135                if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
136                    st.connect_to_ecu(&ecu, direction)?;
137                }
138            }
139        }
140
141        // create and return the new mapping
142        let model = self.element().model()?;
143        let base_path = self.element().path()?;
144        let name = make_unique_name(&model, &base_path, &signal_name);
145
146        // the crucial difference between NmPdu and ISignalIPdu is here
147        // NmPdu uses ISignalToIPduMapping, while ISignalIPdu uses ISignalToPduMapping
148        let mappings = self
149            .element()
150            .get_or_create_sub_element(ElementName::ISignalToIPduMappings)?;
151
152        ISignalToIPduMapping::new_with_signal(
153            &name,
154            &mappings,
155            signal,
156            start_position,
157            byte_order,
158            update_bit,
159            transfer_property,
160        )
161    }
162
163    /// map a signal group to the PDU
164    pub fn map_signal_group(
165        &self,
166        signal_group: &ISignalGroup,
167    ) -> Result<ISignalToIPduMapping, AutosarAbstractionError> {
168        let signal_group_name = signal_group.name().ok_or(AutosarAbstractionError::InvalidParameter(
169            "invalid signal group".to_string(),
170        ))?;
171
172        // add a pdu triggering for the newly mapped PDU to each frame triggering of this frame
173        for pt in self.pdu_triggerings() {
174            let st = pt.create_signal_group_triggering(signal_group)?;
175            for pdu_port in pt.pdu_ports() {
176                if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
177                    st.connect_to_ecu(&ecu, direction)?;
178                }
179            }
180        }
181
182        // create and return the new mapping
183        let model = self.element().model()?;
184        let base_path = self.element().path()?;
185        let name = make_unique_name(&model, &base_path, &signal_group_name);
186
187        // the crucial difference between NmPdu and ISignalIPdu is here
188        // NmPdu uses ISignalToIPduMapping, while ISignalIPdu uses ISignalToPduMapping
189        let mappings = self
190            .element()
191            .get_or_create_sub_element(ElementName::ISignalToIPduMappings)?;
192
193        ISignalToIPduMapping::new_with_group(&name, &mappings, signal_group)
194    }
195}
196
197impl AbstractPdu for NmPdu {}
198impl SignalPdu for NmPdu {
199    fn map_signal(
200        &self,
201        signal: &ISignal,
202        start_position: u32,
203        byte_order: ByteOrder,
204        update_bit: Option<u32>,
205        transfer_property: TransferProperty,
206    ) -> Result<ISignalToIPduMapping, AutosarAbstractionError> {
207        NmPdu::map_signal(self, signal, start_position, byte_order, update_bit, transfer_property)
208    }
209
210    fn map_signal_group(&self, signal_group: &ISignalGroup) -> Result<ISignalToIPduMapping, AutosarAbstractionError> {
211        NmPdu::map_signal_group(self, signal_group)
212    }
213}
214
215impl From<NmPdu> for Pdu {
216    fn from(value: NmPdu) -> Self {
217        Pdu::NmPdu(value)
218    }
219}
220
221//##################################################################
222
223/// This is a Pdu of the transport layer. The main purpose of the TP layer is to segment and reassemble `IPdus`.
224#[derive(Debug, Clone, PartialEq, Eq, Hash)]
225pub struct NPdu(Element);
226abstraction_element!(NPdu, NPdu);
227impl IdentifiableAbstractionElement for NPdu {}
228
229impl NPdu {
230    pub(crate) fn new(name: &str, package: &ArPackage, length: u32) -> Result<Self, AutosarAbstractionError> {
231        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
232        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::NPdu, name)?;
233        elem_pdu
234            .create_sub_element(ElementName::Length)?
235            .set_character_data(length.to_string())?;
236
237        Ok(Self(elem_pdu))
238    }
239}
240
241impl AbstractPdu for NPdu {}
242
243impl AbstractIpdu for NPdu {}
244
245impl From<NPdu> for Pdu {
246    fn from(value: NPdu) -> Self {
247        Pdu::NPdu(value)
248    }
249}
250
251impl From<NPdu> for IPdu {
252    fn from(value: NPdu) -> Self {
253        IPdu::NPdu(value)
254    }
255}
256
257//##################################################################
258
259/// Represents the `IPdus` handled by Dcm
260#[derive(Debug, Clone, PartialEq, Eq, Hash)]
261pub struct DcmIPdu(Element);
262abstraction_element!(DcmIPdu, DcmIPdu);
263impl IdentifiableAbstractionElement for DcmIPdu {}
264
265impl DcmIPdu {
266    pub(crate) fn new(
267        name: &str,
268        package: &ArPackage,
269        length: u32,
270        diag_pdu_type: DiagPduType,
271    ) -> Result<Self, AutosarAbstractionError> {
272        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
273        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::DcmIPdu, name)?;
274        elem_pdu
275            .create_sub_element(ElementName::Length)?
276            .set_character_data(length.to_string())?;
277        let dcm_ipdu = Self(elem_pdu);
278        dcm_ipdu.set_diag_pdu_type(diag_pdu_type)?;
279
280        Ok(dcm_ipdu)
281    }
282
283    /// set the type of this DcmIPdu
284    pub fn set_diag_pdu_type(&self, pdu_type: DiagPduType) -> Result<(), AutosarAbstractionError> {
285        self.element()
286            .get_or_create_sub_element(ElementName::DiagPduType)?
287            .set_character_data::<EnumItem>(pdu_type.into())?;
288        Ok(())
289    }
290
291    /// get the type of this DcmIPdu
292    #[must_use]
293    pub fn diag_pdu_type(&self) -> Option<DiagPduType> {
294        let enum_item = self
295            .element()
296            .get_sub_element(ElementName::DiagPduType)?
297            .character_data()?
298            .enum_value()?;
299        DiagPduType::try_from(enum_item).ok()
300    }
301}
302
303impl AbstractPdu for DcmIPdu {}
304
305impl AbstractIpdu for DcmIPdu {}
306
307impl From<DcmIPdu> for Pdu {
308    fn from(value: DcmIPdu) -> Self {
309        Pdu::DcmIPdu(value)
310    }
311}
312
313impl From<DcmIPdu> for IPdu {
314    fn from(value: DcmIPdu) -> Self {
315        IPdu::DcmIPdu(value)
316    }
317}
318
319//##################################################################
320
321/// The type of a DcmIPdu
322#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
323pub enum DiagPduType {
324    /// Diagnostic Request
325    DiagRequest,
326    /// Diagnostic Response
327    DiagResponse,
328}
329
330impl TryFrom<EnumItem> for DiagPduType {
331    type Error = AutosarAbstractionError;
332
333    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
334        match value {
335            EnumItem::DiagRequest => Ok(DiagPduType::DiagRequest),
336            EnumItem::DiagResponse => Ok(DiagPduType::DiagResponse),
337            _ => Err(AutosarAbstractionError::ValueConversionError {
338                value: value.to_string(),
339                dest: "DiagPduType".to_string(),
340            }),
341        }
342    }
343}
344
345impl From<DiagPduType> for EnumItem {
346    fn from(value: DiagPduType) -> Self {
347        match value {
348            DiagPduType::DiagRequest => EnumItem::DiagRequest,
349            DiagPduType::DiagResponse => EnumItem::DiagResponse,
350        }
351    }
352}
353
354//##################################################################
355
356/// This element is used for AUTOSAR Pdus without additional attributes that are routed by a bus interface
357#[derive(Debug, Clone, PartialEq, Eq, Hash)]
358pub struct GeneralPurposePdu(Element);
359abstraction_element!(GeneralPurposePdu, GeneralPurposePdu);
360impl IdentifiableAbstractionElement for GeneralPurposePdu {}
361
362impl GeneralPurposePdu {
363    pub(crate) fn new(
364        name: &str,
365        package: &ArPackage,
366        length: u32,
367        category: GeneralPurposePduCategory,
368    ) -> Result<Self, AutosarAbstractionError> {
369        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
370        let pdu_elem = pkg_elements.create_named_sub_element(ElementName::GeneralPurposePdu, name)?;
371        let pdu = Self(pdu_elem);
372
373        pdu.set_length(length)?;
374        pdu.set_category(category)?;
375
376        Ok(pdu)
377    }
378
379    /// set the category of this PDU
380    pub fn set_category(&self, category: GeneralPurposePduCategory) -> Result<(), AutosarAbstractionError> {
381        self.element()
382            .get_or_create_sub_element(ElementName::Category)?
383            .set_character_data(category.to_string())?;
384        Ok(())
385    }
386
387    /// get the category of this PDU
388    #[must_use]
389    pub fn category(&self) -> Option<GeneralPurposePduCategory> {
390        let category_string = self
391            .element()
392            .get_sub_element(ElementName::Category)?
393            .character_data()?
394            .string_value()?;
395        GeneralPurposePduCategory::from_str(&category_string).ok()
396    }
397}
398
399impl AbstractPdu for GeneralPurposePdu {}
400
401impl From<GeneralPurposePdu> for Pdu {
402    fn from(value: GeneralPurposePdu) -> Self {
403        Pdu::GeneralPurposePdu(value)
404    }
405}
406
407//##################################################################
408
409/// The category of a `GeneralPurposePdu`
410///
411/// The Autosar standard defines the following categories:
412/// - `SD`
413/// - `GLOBAL_TIME`
414/// - `DOIP`
415#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
416pub enum GeneralPurposePduCategory {
417    /// Service Discovery
418    Sd,
419    /// Global Time Synchronization
420    GlobalTime,
421    /// Diagnostic over IP
422    DoIp,
423}
424
425impl std::fmt::Display for GeneralPurposePduCategory {
426    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
427        match self {
428            GeneralPurposePduCategory::Sd => write!(f, "SD"),
429            GeneralPurposePduCategory::GlobalTime => write!(f, "GLOBAL_TIME"),
430            GeneralPurposePduCategory::DoIp => write!(f, "DOIP"),
431        }
432    }
433}
434
435impl std::str::FromStr for GeneralPurposePduCategory {
436    type Err = AutosarAbstractionError;
437
438    fn from_str(s: &str) -> Result<Self, Self::Err> {
439        match s {
440            "SD" => Ok(GeneralPurposePduCategory::Sd),
441            "GLOBAL_TIME" => Ok(GeneralPurposePduCategory::GlobalTime),
442            "DOIP" => Ok(GeneralPurposePduCategory::DoIp),
443            _ => Err(AutosarAbstractionError::InvalidParameter(s.to_string())),
444        }
445    }
446}
447
448//##################################################################
449
450/// This element is used for AUTOSAR Pdus without attributes that are routed by the `PduR`
451#[derive(Debug, Clone, PartialEq, Eq, Hash)]
452pub struct GeneralPurposeIPdu(Element);
453abstraction_element!(GeneralPurposeIPdu, GeneralPurposeIPdu);
454impl IdentifiableAbstractionElement for GeneralPurposeIPdu {}
455
456impl GeneralPurposeIPdu {
457    pub(crate) fn new(
458        name: &str,
459        package: &ArPackage,
460        length: u32,
461        category: GeneralPurposeIPduCategory,
462    ) -> Result<Self, AutosarAbstractionError> {
463        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
464        let pdu_elem = pkg_elements.create_named_sub_element(ElementName::GeneralPurposeIPdu, name)?;
465        let pdu = Self(pdu_elem);
466
467        pdu.set_length(length)?;
468        pdu.set_category(category)?;
469
470        Ok(pdu)
471    }
472
473    /// set the category of this PDU
474    pub fn set_category(&self, category: GeneralPurposeIPduCategory) -> Result<(), AutosarAbstractionError> {
475        self.element()
476            .get_or_create_sub_element(ElementName::Category)?
477            .set_character_data(category.to_string())?;
478        Ok(())
479    }
480
481    /// get the category of this PDU
482    #[must_use]
483    pub fn category(&self) -> Option<GeneralPurposeIPduCategory> {
484        let category_string = self
485            .element()
486            .get_sub_element(ElementName::Category)?
487            .character_data()?
488            .string_value()?;
489        GeneralPurposeIPduCategory::from_str(&category_string).ok()
490    }
491}
492
493impl AbstractPdu for GeneralPurposeIPdu {}
494
495impl AbstractIpdu for GeneralPurposeIPdu {}
496
497impl From<GeneralPurposeIPdu> for Pdu {
498    fn from(value: GeneralPurposeIPdu) -> Self {
499        Pdu::GeneralPurposeIPdu(value)
500    }
501}
502
503impl From<GeneralPurposeIPdu> for IPdu {
504    fn from(value: GeneralPurposeIPdu) -> Self {
505        IPdu::GeneralPurposeIPdu(value)
506    }
507}
508
509//##################################################################
510
511/// The category of a `GeneralPurposeIPdu`
512///
513/// The Autosar standard defines the following categories:
514/// - XCP
515/// - `SOMEIP_SEGMENTED_IPDU`
516/// - DLT
517#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
518pub enum GeneralPurposeIPduCategory {
519    /// XCP
520    Xcp,
521    /// SOME/IP Segmented `IPdu`
522    SomeipSegmentedIpdu,
523    /// Diagnostic Log and Trace
524    Dlt,
525}
526
527impl std::fmt::Display for GeneralPurposeIPduCategory {
528    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
529        match self {
530            GeneralPurposeIPduCategory::Xcp => write!(f, "XCP"),
531            GeneralPurposeIPduCategory::SomeipSegmentedIpdu => write!(f, "SOMEIP_SEGMENTED_IPDU"),
532            GeneralPurposeIPduCategory::Dlt => write!(f, "DLT"),
533        }
534    }
535}
536
537impl std::str::FromStr for GeneralPurposeIPduCategory {
538    type Err = AutosarAbstractionError;
539
540    fn from_str(s: &str) -> Result<Self, Self::Err> {
541        match s {
542            "XCP" => Ok(GeneralPurposeIPduCategory::Xcp),
543            "SOMEIP_SEGMENTED_IPDU" => Ok(GeneralPurposeIPduCategory::SomeipSegmentedIpdu),
544            "DLT" => Ok(GeneralPurposeIPduCategory::Dlt),
545            _ => Err(AutosarAbstractionError::InvalidParameter(s.to_string())),
546        }
547    }
548}
549
550//##################################################################
551
552/// The multiplexed pdu contains one of serveral signal pdus
553#[derive(Debug, Clone, PartialEq, Eq, Hash)]
554pub struct MultiplexedIPdu(Element);
555abstraction_element!(MultiplexedIPdu, MultiplexedIPdu);
556impl IdentifiableAbstractionElement for MultiplexedIPdu {}
557
558impl MultiplexedIPdu {
559    pub(crate) fn new(name: &str, package: &ArPackage, length: u32) -> Result<Self, AutosarAbstractionError> {
560        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
561        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::MultiplexedIPdu, name)?;
562        elem_pdu
563            .create_sub_element(ElementName::Length)?
564            .set_character_data(length.to_string())?;
565
566        Ok(Self(elem_pdu))
567    }
568
569    /// set the ISignalIPdu containing the static part of this multiplexed ipdu
570    pub fn set_static_part(&self, static_ipdu: &ISignalIPdu) -> Result<(), AutosarAbstractionError> {
571        let prev_static_part = self.static_part();
572
573        self.element()
574            .get_or_create_sub_element(ElementName::StaticParts)?
575            .get_or_create_sub_element(ElementName::StaticPart)?
576            .get_or_create_sub_element(ElementName::IPduRef)?
577            .set_reference_target(static_ipdu.element())?;
578
579        self.update_pdu_triggerings(prev_static_part.as_ref(), static_ipdu)?;
580
581        Ok(())
582    }
583
584    /// get the ISignalIPdu containing the static part of this multiplexed ipdu
585    #[must_use]
586    pub fn static_part(&self) -> Option<ISignalIPdu> {
587        let ipdu_elem = self
588            .element()
589            .get_sub_element(ElementName::StaticParts)?
590            .get_sub_element(ElementName::StaticPart)?
591            .get_sub_element(ElementName::IPduRef)?
592            .get_reference_target()
593            .ok()?;
594        ISignalIPdu::try_from(ipdu_elem).ok()
595    }
596
597    /// add a dynamic part alternative to this multiplexed ipdu
598    /// `selector_code` is the value of the multiplexor that selects this dynamic part
599    /// `initial_dynamic_part` indicates whether this is the initial dynamic part; only one dynamic part can be initial
600    ///
601    /// All dynamic parts must have the same length
602    pub fn add_dynamic_part(
603        &self,
604        dynamic_ipdu: &ISignalIPdu,
605        selector_code: u16,
606        initial_dynamic_part: bool,
607    ) -> Result<DynamicPartAlternative, AutosarAbstractionError> {
608        let dp_alternatives = self
609            .element()
610            .get_or_create_sub_element(ElementName::DynamicParts)?
611            .get_or_create_sub_element(ElementName::DynamicPart)?
612            .get_or_create_sub_element(ElementName::DynamicPartAlternatives)?;
613
614        DynamicPartAlternative::new(&dp_alternatives, dynamic_ipdu, selector_code, initial_dynamic_part)
615    }
616
617    /// list all dynamic part alternatives of this multiplexed ipdu
618    pub fn dynamic_part_alternatives(&self) -> impl Iterator<Item = DynamicPartAlternative> {
619        let dp_alternatives_elem = self
620            .element()
621            .get_sub_element(ElementName::DynamicParts)
622            .and_then(|e| e.get_sub_element(ElementName::DynamicPart))
623            .and_then(|e| e.get_sub_element(ElementName::DynamicPartAlternatives));
624
625        dp_alternatives_elem
626            .into_iter()
627            .flat_map(|e| e.sub_elements())
628            .filter_map(|elem| DynamicPartAlternative::try_from(elem).ok())
629    }
630
631    /// set the selector field of this multiplexed ipdu
632    ///
633    /// The selector field should exist as a signal in each dynamic part
634    pub fn set_selector_field(
635        &self,
636        length: u8,
637        start_position: u32,
638        byte_order: ByteOrder,
639    ) -> Result<(), AutosarAbstractionError> {
640        if length == 0 || length > 16 {
641            return Err(AutosarAbstractionError::InvalidParameter(
642                "selector field length must be between 1 and 16".to_string(),
643            ));
644        }
645        self.element()
646            .get_or_create_sub_element(ElementName::SelectorFieldLength)?
647            .set_character_data(length as u64)?;
648        self.element()
649            .get_or_create_sub_element(ElementName::SelectorFieldStartPosition)?
650            .set_character_data(start_position as u64)?;
651        self.element()
652            .get_or_create_sub_element(ElementName::SelectorFieldByteOrder)?
653            .set_character_data::<EnumItem>(byte_order.into())?;
654        Ok(())
655    }
656
657    /// get the selector field of this multiplexed ipdu
658    pub fn selector_field(&self) -> Option<(u8, u32, ByteOrder)> {
659        let length = self
660            .element()
661            .get_sub_element(ElementName::SelectorFieldLength)?
662            .character_data()?
663            .parse_integer()?;
664        let start_position = self
665            .element()
666            .get_sub_element(ElementName::SelectorFieldStartPosition)?
667            .character_data()?
668            .parse_integer()?;
669        let byte_order_enum = self
670            .element()
671            .get_sub_element(ElementName::SelectorFieldByteOrder)?
672            .character_data()?
673            .enum_value()?;
674        let byte_order = ByteOrder::try_from(byte_order_enum).ok()?;
675        Some((length, start_position, byte_order))
676    }
677
678    // Update the pdu triggerings when the static part or a dynamic part is changed
679    // Also used when the multiplexed ipdu is newly mapped to a frame
680    pub(crate) fn update_pdu_triggerings(
681        &self,
682        old_ipdu: Option<&ISignalIPdu>,
683        new_ipdu: &ISignalIPdu,
684    ) -> Result<(), AutosarAbstractionError> {
685        // note: usually, the MultiplexedIPdu is triggered exactly once, and thus the pdu_triggerings list has exactly one entry
686        // remove the pdu triggering(s) of the previous ipdu
687        if let Some(old_ipdu) = old_ipdu {
688            for pt in old_ipdu.pdu_triggerings() {
689                // Todo: implement pt.remove()?;
690                if let Ok(Some(pt_parent)) = pt.element().parent() {
691                    let _ = pt_parent.remove_sub_element(pt.element().clone());
692                }
693            }
694        }
695
696        // create pdu triggerings for the new ipdu part in channels where this multiplexed ipdu is triggered
697        for multiplex_pt in self.pdu_triggerings() {
698            if let Ok(channel) = multiplex_pt.physical_channel() {
699                let new_pt = PduTriggering::new(&Pdu::ISignalIPdu(new_ipdu.clone()), &channel)?;
700                for pp in multiplex_pt.pdu_ports() {
701                    if let (Ok(ecu), Some(direction)) = (pp.ecu(), pp.communication_direction()) {
702                        let _ = new_pt.create_pdu_port(&ecu, direction);
703                    }
704                }
705            }
706        }
707
708        Ok(())
709    }
710}
711
712impl AbstractPdu for MultiplexedIPdu {}
713
714impl AbstractIpdu for MultiplexedIPdu {}
715
716impl From<MultiplexedIPdu> for Pdu {
717    fn from(value: MultiplexedIPdu) -> Self {
718        Pdu::MultiplexedIPdu(value)
719    }
720}
721
722impl From<MultiplexedIPdu> for IPdu {
723    fn from(value: MultiplexedIPdu) -> Self {
724        IPdu::MultiplexedIPdu(value)
725    }
726}
727
728//##################################################################
729
730/// A dynamic part alternative of a multiplexed PDU
731#[derive(Debug, Clone, PartialEq, Eq, Hash)]
732pub struct DynamicPartAlternative(Element);
733abstraction_element!(DynamicPartAlternative, DynamicPartAlternative);
734
735impl DynamicPartAlternative {
736    fn new(
737        parent: &Element,
738        dynamic_ipdu: &ISignalIPdu,
739        selector_code: u16,
740        initial_dynamic_part: bool,
741    ) -> Result<Self, AutosarAbstractionError> {
742        let dp_alt_elem = parent.create_sub_element(ElementName::DynamicPartAlternative)?;
743        let dp_alt = Self(dp_alt_elem);
744        dp_alt.set_ipdu(dynamic_ipdu)?;
745        dp_alt.set_selector_field_code(selector_code)?;
746        dp_alt.set_initial_dynamic_part(initial_dynamic_part)?;
747
748        Ok(dp_alt)
749    }
750
751    /// set the `ISignalIPdu` referenced by this dynamic part alternative
752    pub fn set_ipdu(&self, ipdu: &ISignalIPdu) -> Result<(), AutosarAbstractionError> {
753        let old_ipdu = self.ipdu();
754        self.element()
755            .get_or_create_sub_element(ElementName::IPduRef)?
756            .set_reference_target(ipdu.element())?;
757
758        self.multiplexed_ipdu()?
759            .update_pdu_triggerings(old_ipdu.as_ref(), ipdu)?;
760
761        Ok(())
762    }
763
764    /// get the `ISignalIPdu` referenced by this dynamic part alternative
765    #[must_use]
766    pub fn ipdu(&self) -> Option<ISignalIPdu> {
767        let ipdu_elem = self
768            .element()
769            .get_sub_element(ElementName::IPduRef)?
770            .get_reference_target()
771            .ok()?;
772        ISignalIPdu::try_from(ipdu_elem).ok()
773    }
774
775    /// set the selector field code of this dynamic part alternative
776    pub fn set_selector_field_code(&self, code: u16) -> Result<(), AutosarAbstractionError> {
777        self.element()
778            .get_or_create_sub_element(ElementName::SelectorFieldCode)?
779            .set_character_data(code as u64)?;
780        Ok(())
781    }
782
783    /// get the selector field code of this dynamic part alternative
784    #[must_use]
785    pub fn selector_field_code(&self) -> Option<u16> {
786        self.element()
787            .get_sub_element(ElementName::SelectorFieldCode)?
788            .character_data()?
789            .parse_integer()
790    }
791
792    /// set whether this is the initial dynamic part
793    pub fn set_initial_dynamic_part(&self, initial: bool) -> Result<(), AutosarAbstractionError> {
794        self.element()
795            .get_or_create_sub_element(ElementName::InitialDynamicPart)?
796            .set_character_data(initial)?;
797        Ok(())
798    }
799
800    /// check whether this is the initial dynamic part
801    #[must_use]
802    pub fn is_initial_dynamic_part(&self) -> Option<bool> {
803        self.element()
804            .get_sub_element(ElementName::InitialDynamicPart)?
805            .character_data()?
806            .parse_bool()
807    }
808
809    /// get the multiplexed ipdu containing this dynamic part alternative
810    pub fn multiplexed_ipdu(&self) -> Result<MultiplexedIPdu, AutosarAbstractionError> {
811        let parent_elem = self.element().named_parent()?;
812        let parent_elem = parent_elem.unwrap(); // unwrap is safe here because the parent must exist
813        MultiplexedIPdu::try_from(parent_elem)
814    }
815}
816
817//##################################################################
818
819/// Wrapper for all Pdu types. It is used as a return value for functions that can return any Pdu type
820#[derive(Debug, Clone, PartialEq, Eq, Hash)]
821pub enum Pdu {
822    /// The Pdu is an `ISignalIPdu`
823    ISignalIPdu(ISignalIPdu),
824    /// The Pdu is a Network Management Pdu
825    NmPdu(NmPdu),
826    /// The Pdu is a Transport Layer Pdu
827    NPdu(NPdu),
828    /// The Pdu is a Diagnostic Communication Management Pdu
829    DcmIPdu(DcmIPdu),
830    /// The Pdu is a General Purpose Pdu
831    GeneralPurposePdu(GeneralPurposePdu),
832    /// The Pdu is a General Purpose `IPdu`
833    GeneralPurposeIPdu(GeneralPurposeIPdu),
834    /// The Pdu is a Container `IPdu`
835    ContainerIPdu(ContainerIPdu),
836    /// The Pdu is a Secured `IPdu`
837    SecuredIPdu(SecuredIPdu),
838    /// The Pdu is a Multiplexed `IPdu`
839    MultiplexedIPdu(MultiplexedIPdu),
840}
841
842impl AbstractionElement for Pdu {
843    fn element(&self) -> &Element {
844        match self {
845            Pdu::ISignalIPdu(pdu) => pdu.element(),
846            Pdu::NmPdu(pdu) => pdu.element(),
847            Pdu::NPdu(pdu) => pdu.element(),
848            Pdu::DcmIPdu(pdu) => pdu.element(),
849            Pdu::GeneralPurposePdu(pdu) => pdu.element(),
850            Pdu::GeneralPurposeIPdu(pdu) => pdu.element(),
851            Pdu::ContainerIPdu(pdu) => pdu.element(),
852            Pdu::SecuredIPdu(pdu) => pdu.element(),
853            Pdu::MultiplexedIPdu(pdu) => pdu.element(),
854        }
855    }
856}
857
858impl TryFrom<Element> for Pdu {
859    type Error = AutosarAbstractionError;
860
861    fn try_from(element: Element) -> Result<Self, Self::Error> {
862        match element.element_name() {
863            ElementName::ISignalIPdu => Ok(ISignalIPdu::try_from(element)?.into()),
864            ElementName::NmPdu => Ok(NmPdu::try_from(element)?.into()),
865            ElementName::NPdu => Ok(NPdu::try_from(element)?.into()),
866            ElementName::DcmIPdu => Ok(DcmIPdu::try_from(element)?.into()),
867            ElementName::GeneralPurposePdu => Ok(GeneralPurposePdu::try_from(element)?.into()),
868            ElementName::GeneralPurposeIPdu => Ok(GeneralPurposeIPdu::try_from(element)?.into()),
869            ElementName::ContainerIPdu => Ok(ContainerIPdu::try_from(element)?.into()),
870            ElementName::SecuredIPdu => Ok(SecuredIPdu::try_from(element)?.into()),
871            ElementName::MultiplexedIPdu => Ok(MultiplexedIPdu::try_from(element)?.into()),
872            _ => Err(AutosarAbstractionError::ConversionError {
873                element,
874                dest: "Pdu".to_string(),
875            }),
876        }
877    }
878}
879
880impl IdentifiableAbstractionElement for Pdu {}
881impl AbstractPdu for Pdu {}
882
883//##################################################################
884
885/// Wrapper for all Pdu types. It is used as a return value for functions that can return any Pdu type
886#[derive(Debug, Clone, PartialEq, Eq, Hash)]
887pub enum IPdu {
888    /// The `IPdu` is an `ISignalIPdu`
889    ISignalIPdu(ISignalIPdu),
890    /// The Pdu is a Transport Layer Pdu
891    NPdu(NPdu),
892    /// The `IPdu` is a Diagnostic Communication Management Pdu
893    DcmIPdu(DcmIPdu),
894    /// The `IPdu` is a general purpose Pdu
895    GeneralPurposeIPdu(GeneralPurposeIPdu),
896    /// The `IPdu` is a Container `IPdu`
897    ContainerIPdu(ContainerIPdu),
898    /// The `IPdu` is a secured `IPdu`
899    SecuredIPdu(SecuredIPdu),
900    /// The `IPdu` is a multiplexed `IPdu`
901    MultiplexedIPdu(MultiplexedIPdu),
902}
903
904impl AbstractionElement for IPdu {
905    fn element(&self) -> &Element {
906        match self {
907            IPdu::ISignalIPdu(pdu) => pdu.element(),
908            IPdu::NPdu(pdu) => pdu.element(),
909            IPdu::DcmIPdu(pdu) => pdu.element(),
910            IPdu::GeneralPurposeIPdu(pdu) => pdu.element(),
911            IPdu::ContainerIPdu(pdu) => pdu.element(),
912            IPdu::SecuredIPdu(pdu) => pdu.element(),
913            IPdu::MultiplexedIPdu(pdu) => pdu.element(),
914        }
915    }
916}
917
918impl TryFrom<Element> for IPdu {
919    type Error = AutosarAbstractionError;
920
921    fn try_from(element: Element) -> Result<Self, Self::Error> {
922        match element.element_name() {
923            ElementName::ISignalIPdu => Ok(IPdu::ISignalIPdu(ISignalIPdu::try_from(element)?)),
924            ElementName::NPdu => Ok(IPdu::NPdu(NPdu::try_from(element)?)),
925            ElementName::DcmIPdu => Ok(IPdu::DcmIPdu(DcmIPdu::try_from(element)?)),
926            ElementName::GeneralPurposeIPdu => Ok(IPdu::GeneralPurposeIPdu(GeneralPurposeIPdu::try_from(element)?)),
927            ElementName::ContainerIPdu => Ok(IPdu::ContainerIPdu(ContainerIPdu::try_from(element)?)),
928            ElementName::SecuredIPdu => Ok(IPdu::SecuredIPdu(SecuredIPdu::try_from(element)?)),
929            ElementName::MultiplexedIPdu => Ok(IPdu::MultiplexedIPdu(MultiplexedIPdu::try_from(element)?)),
930            _ => Err(AutosarAbstractionError::ConversionError {
931                element,
932                dest: "IPdu".to_string(),
933            }),
934        }
935    }
936}
937
938impl From<IPdu> for Pdu {
939    fn from(value: IPdu) -> Self {
940        match value {
941            IPdu::ISignalIPdu(pdu) => Pdu::ISignalIPdu(pdu),
942            IPdu::NPdu(pdu) => Pdu::NPdu(pdu),
943            IPdu::DcmIPdu(pdu) => Pdu::DcmIPdu(pdu),
944            IPdu::GeneralPurposeIPdu(pdu) => Pdu::GeneralPurposeIPdu(pdu),
945            IPdu::ContainerIPdu(pdu) => Pdu::ContainerIPdu(pdu),
946            IPdu::SecuredIPdu(pdu) => Pdu::SecuredIPdu(pdu),
947            IPdu::MultiplexedIPdu(pdu) => Pdu::MultiplexedIPdu(pdu),
948        }
949    }
950}
951
952impl IdentifiableAbstractionElement for IPdu {}
953impl AbstractPdu for IPdu {}
954impl AbstractIpdu for IPdu {}
955
956//##################################################################
957
958/// a `PduTriggering` triggers a PDU in a frame or ethernet connection
959#[derive(Debug, Clone, PartialEq, Eq, Hash)]
960pub struct PduTriggering(Element);
961abstraction_element!(PduTriggering, PduTriggering);
962impl IdentifiableAbstractionElement for PduTriggering {}
963
964impl PduTriggering {
965    pub(crate) fn new(pdu: &Pdu, channel: &PhysicalChannel) -> Result<Self, AutosarAbstractionError> {
966        let model = channel.element().model()?;
967        let base_path = channel.element().path()?;
968        let pdu_name = pdu
969            .name()
970            .ok_or(AutosarAbstractionError::InvalidParameter("invalid pdu".to_string()))?;
971        let pt_name = format!("PT_{pdu_name}");
972        let pt_name = make_unique_name(&model, &base_path, &pt_name);
973
974        let triggerings = channel
975            .element()
976            .get_or_create_sub_element(ElementName::PduTriggerings)?;
977        let pt_elem = triggerings.create_named_sub_element(ElementName::PduTriggering, &pt_name)?;
978        pt_elem
979            .create_sub_element(ElementName::IPduRef)?
980            .set_reference_target(pdu.element())?;
981
982        let pt = Self(pt_elem);
983
984        if let Pdu::ISignalIPdu(isignal_ipdu) = pdu {
985            for signal_mapping in isignal_ipdu.mapped_signals() {
986                if let Some(signal) = signal_mapping.signal() {
987                    pt.create_signal_triggering(&signal)?;
988                } else if let Some(signal_group) = signal_mapping.signal_group() {
989                    pt.create_signal_group_triggering(&signal_group)?;
990                }
991            }
992        }
993
994        Ok(pt)
995    }
996
997    /// get the Pdu that is triggered by this pdu triggering
998    #[must_use]
999    pub fn pdu(&self) -> Option<Pdu> {
1000        let pdu_elem = self
1001            .element()
1002            .get_sub_element(ElementName::IPduRef)?
1003            .get_reference_target()
1004            .ok()?;
1005        Pdu::try_from(pdu_elem).ok()
1006    }
1007
1008    /// get the physical channel that contains this pdu triggering
1009    pub fn physical_channel(&self) -> Result<PhysicalChannel, AutosarAbstractionError> {
1010        let channel_elem = self.element().named_parent()?.ok_or(AutosarDataError::ItemDeleted)?;
1011        PhysicalChannel::try_from(channel_elem)
1012    }
1013
1014    /// create an `IPduPort` to connect a `PduTriggering` to an `EcuInstance`
1015    pub fn create_pdu_port(
1016        &self,
1017        ecu: &EcuInstance,
1018        direction: CommunicationDirection,
1019    ) -> Result<IPduPort, AutosarAbstractionError> {
1020        for pdu_port in self.pdu_ports() {
1021            if let (Ok(existing_ecu), Some(existing_direction)) = (pdu_port.ecu(), pdu_port.communication_direction())
1022                && existing_ecu == *ecu
1023                && existing_direction == direction
1024            {
1025                return Ok(pdu_port);
1026            }
1027        }
1028
1029        let channel = self.physical_channel()?;
1030        let connector = channel
1031            .ecu_connector(ecu)
1032            .ok_or(AutosarAbstractionError::InvalidParameter(
1033                "The ECU is not connected to the channel".to_string(),
1034            ))?;
1035
1036        let name = self.name().ok_or(AutosarDataError::ItemDeleted)?;
1037        let suffix = match direction {
1038            CommunicationDirection::In => "Rx",
1039            CommunicationDirection::Out => "Tx",
1040        };
1041        let port_name = format!("{name}_{suffix}",);
1042        let pp_elem = connector
1043            .element()
1044            .get_or_create_sub_element(ElementName::EcuCommPortInstances)?
1045            .create_named_sub_element(ElementName::IPduPort, &port_name)?;
1046        pp_elem
1047            .create_sub_element(ElementName::CommunicationDirection)?
1048            .set_character_data::<EnumItem>(direction.into())?;
1049
1050        self.element()
1051            .get_or_create_sub_element(ElementName::IPduPortRefs)?
1052            .create_sub_element(ElementName::IPduPortRef)?
1053            .set_reference_target(&pp_elem)?;
1054
1055        for st in self.signal_triggerings() {
1056            st.connect_to_ecu(ecu, direction)?;
1057        }
1058
1059        Ok(IPduPort(pp_elem))
1060    }
1061
1062    /// create an iterator over the `IPduPorts` that are connected to this `PduTriggering`
1063    pub fn pdu_ports(&self) -> impl Iterator<Item = IPduPort> + Send + use<> {
1064        self.element()
1065            .get_sub_element(ElementName::IPduPortRefs)
1066            .into_iter()
1067            .flat_map(|ipprefs| ipprefs.sub_elements())
1068            .filter_map(|ippref| {
1069                ippref
1070                    .get_reference_target()
1071                    .ok()
1072                    .and_then(|elem| IPduPort::try_from(elem).ok())
1073            })
1074    }
1075
1076    /// create an iterator over the `ISignalTriggerings` that are triggered by this `PduTriggering`
1077    pub fn signal_triggerings(&self) -> impl Iterator<Item = ISignalTriggering> + Send + use<> {
1078        self.element()
1079            .get_sub_element(ElementName::ISignalTriggerings)
1080            .into_iter()
1081            .flat_map(|ists| ists.sub_elements())
1082            .filter_map(|ist| {
1083                ist.get_sub_element(ElementName::ISignalTriggeringRef)
1084                    .and_then(|str| str.get_reference_target().ok())
1085                    .and_then(|elem| ISignalTriggering::try_from(elem).ok())
1086            })
1087    }
1088
1089    /// create a signal triggering for a signal and connect it to this `PduTriggering`
1090    pub(crate) fn create_signal_triggering(
1091        &self,
1092        signal: &ISignal,
1093    ) -> Result<ISignalTriggering, AutosarAbstractionError> {
1094        let channel = self.physical_channel()?;
1095        let st = ISignalTriggering::new(signal, &channel)?;
1096        let triggerings = self
1097            .element()
1098            .get_or_create_sub_element(ElementName::ISignalTriggerings)?;
1099        triggerings
1100            .create_sub_element(ElementName::ISignalTriggeringRefConditional)?
1101            .create_sub_element(ElementName::ISignalTriggeringRef)?
1102            .set_reference_target(st.element())?;
1103
1104        for pdu_port in self.pdu_ports() {
1105            if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
1106                st.connect_to_ecu(&ecu, direction)?;
1107            }
1108        }
1109
1110        Ok(st)
1111    }
1112
1113    /// create a signal triggering for a signal group and connect it to this `PduTriggering`
1114    pub(crate) fn create_signal_group_triggering(
1115        &self,
1116        signal_group: &ISignalGroup,
1117    ) -> Result<ISignalTriggering, AutosarAbstractionError> {
1118        let channel = self.physical_channel()?;
1119        let st = ISignalTriggering::new_group(signal_group, &channel)?;
1120        let triggerings = self
1121            .element()
1122            .get_or_create_sub_element(ElementName::ISignalTriggerings)?;
1123        triggerings
1124            .create_sub_element(ElementName::ISignalTriggeringRefConditional)?
1125            .create_sub_element(ElementName::ISignalTriggeringRef)?
1126            .set_reference_target(st.element())?;
1127
1128        for pdu_port in self.pdu_ports() {
1129            if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
1130                st.connect_to_ecu(&ecu, direction)?;
1131            }
1132        }
1133
1134        Ok(st)
1135    }
1136}
1137
1138//##################################################################
1139
1140/// The `IPduPort` allows an ECU to send or receive a PDU
1141#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1142pub struct IPduPort(Element);
1143abstraction_element!(IPduPort, IPduPort);
1144impl IdentifiableAbstractionElement for IPduPort {}
1145
1146impl IPduPort {
1147    /// get the ECU instance that contains this `IPduPort`
1148    pub fn ecu(&self) -> Result<EcuInstance, AutosarAbstractionError> {
1149        let comm_connector_elem = self.element().named_parent()?.unwrap();
1150        let ecu_elem = comm_connector_elem.named_parent()?.unwrap();
1151        EcuInstance::try_from(ecu_elem)
1152    }
1153
1154    /// set the communication direction of this `IPduPort`
1155    pub fn set_communication_direction(
1156        &self,
1157        direction: CommunicationDirection,
1158    ) -> Result<(), AutosarAbstractionError> {
1159        self.element()
1160            .get_or_create_sub_element(ElementName::CommunicationDirection)?
1161            .set_character_data::<EnumItem>(direction.into())?;
1162        Ok(())
1163    }
1164
1165    /// get the communication direction of this `IPduPort`
1166    #[must_use]
1167    pub fn communication_direction(&self) -> Option<CommunicationDirection> {
1168        self.element()
1169            .get_sub_element(ElementName::CommunicationDirection)?
1170            .character_data()?
1171            .enum_value()?
1172            .try_into()
1173            .ok()
1174    }
1175}
1176
1177//##################################################################
1178
1179/// The collction trigger defines whether a Pdu contributes to the triggering
1180/// of the data transmission if Pdu collection is enabled
1181#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1182pub enum PduCollectionTrigger {
1183    /// Pdu will trigger the transmission of the data.
1184    Always,
1185    /// Pdu will be buffered and will not trigger the transmission of the data
1186    Never,
1187}
1188
1189impl From<PduCollectionTrigger> for EnumItem {
1190    fn from(value: PduCollectionTrigger) -> Self {
1191        match value {
1192            PduCollectionTrigger::Always => EnumItem::Always,
1193            PduCollectionTrigger::Never => EnumItem::Never,
1194        }
1195    }
1196}
1197
1198impl TryFrom<EnumItem> for PduCollectionTrigger {
1199    type Error = AutosarAbstractionError;
1200
1201    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
1202        match value {
1203            EnumItem::Always => Ok(PduCollectionTrigger::Always),
1204            EnumItem::Never => Ok(PduCollectionTrigger::Never),
1205            _ => Err(AutosarAbstractionError::ValueConversionError {
1206                value: value.to_string(),
1207                dest: "PduCollectionTrigger".to_string(),
1208            }),
1209        }
1210    }
1211}
1212
1213//##################################################################
1214
1215#[cfg(test)]
1216mod test {
1217    use super::*;
1218    use crate::{
1219        AutosarModelAbstraction, ByteOrder, SystemCategory,
1220        communication::{AbstractFrame, AbstractFrameTriggering, CanAddressingMode, CanFrameType, TransferProperty},
1221    };
1222    use autosar_data::AutosarVersion;
1223
1224    #[test]
1225    fn test_pdus() {
1226        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1227        let package = model.get_or_create_package("/pkg").unwrap();
1228        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1229
1230        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
1231        let nm_pdu = system.create_nm_pdu("nm_pdu", &package, 1).unwrap();
1232        let n_pdu = system.create_n_pdu("n_pdu", &package, 1).unwrap();
1233        let dcm_ipdu = system
1234            .create_dcm_ipdu("dcm_ipdu", &package, 1, DiagPduType::DiagRequest)
1235            .unwrap();
1236        let gp_pdu = system
1237            .create_general_purpose_pdu("gp_pdu", &package, 1, GeneralPurposePduCategory::Sd)
1238            .unwrap();
1239        let gp_ipdu = system
1240            .create_general_purpose_ipdu("gp_ipdu", &package, 1, GeneralPurposeIPduCategory::Xcp)
1241            .unwrap();
1242        let container_ipdu = system
1243            .create_container_ipdu(
1244                "container_ipdu",
1245                &package,
1246                1,
1247                ContainerIPduHeaderType::ShortHeader,
1248                RxAcceptContainedIPdu::AcceptAll,
1249            )
1250            .unwrap();
1251        let secured_ipdu = system
1252            .create_secured_ipdu("secured_ipdu", &package, 1, &SecureCommunicationProps::default())
1253            .unwrap();
1254        let multiplexed_ipdu = system.create_multiplexed_ipdu("multiplexed_ipdu", &package, 1).unwrap();
1255
1256        assert_eq!(isignal_ipdu.length().unwrap(), 1);
1257        assert_eq!(nm_pdu.length().unwrap(), 1);
1258        assert_eq!(n_pdu.length().unwrap(), 1);
1259        assert_eq!(dcm_ipdu.length().unwrap(), 1);
1260        assert_eq!(gp_pdu.length().unwrap(), 1);
1261        assert_eq!(gp_ipdu.length().unwrap(), 1);
1262        assert_eq!(container_ipdu.length().unwrap(), 1);
1263        assert_eq!(secured_ipdu.length().unwrap(), 1);
1264        assert_eq!(multiplexed_ipdu.length().unwrap(), 1);
1265
1266        isignal_ipdu.set_length(2).unwrap();
1267        assert_eq!(isignal_ipdu.length().unwrap(), 2);
1268
1269        let frame = system.create_flexray_frame("frame1", &package, 64).unwrap();
1270        frame
1271            .map_pdu(&isignal_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
1272            .unwrap();
1273        frame
1274            .map_pdu(&nm_pdu, 8, ByteOrder::MostSignificantByteLast, None)
1275            .unwrap();
1276        frame
1277            .map_pdu(&n_pdu, 16, ByteOrder::MostSignificantByteLast, None)
1278            .unwrap();
1279        frame
1280            .map_pdu(&dcm_ipdu, 24, ByteOrder::MostSignificantByteLast, None)
1281            .unwrap();
1282        frame
1283            .map_pdu(&gp_pdu, 32, ByteOrder::MostSignificantByteLast, None)
1284            .unwrap();
1285        frame
1286            .map_pdu(&gp_ipdu, 40, ByteOrder::MostSignificantByteLast, None)
1287            .unwrap();
1288        frame
1289            .map_pdu(&container_ipdu, 48, ByteOrder::MostSignificantByteLast, None)
1290            .unwrap();
1291        frame
1292            .map_pdu(&secured_ipdu, 56, ByteOrder::MostSignificantByteLast, None)
1293            .unwrap();
1294        frame
1295            .map_pdu(&multiplexed_ipdu, 64, ByteOrder::MostSignificantByteLast, None)
1296            .unwrap();
1297
1298        let mut pdus_iter = frame.mapped_pdus();
1299        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "isignal_ipdu");
1300        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "nm_pdu");
1301        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "n_pdu");
1302        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "dcm_ipdu");
1303        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "gp_pdu");
1304        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "gp_ipdu");
1305        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "container_ipdu");
1306        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "secured_ipdu");
1307        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "multiplexed_ipdu");
1308        assert!(pdus_iter.next().is_none());
1309    }
1310
1311    #[test]
1312    fn test_pdu_triggering() {
1313        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1314        let package = model.get_or_create_package("/pkg").unwrap();
1315        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1316
1317        // create an ISignalIPdu with a signal
1318        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
1319        let syssignal = package.create_system_signal("syssignal").unwrap();
1320        let isignal = system.create_isignal("isignal", &package, 1, &syssignal, None).unwrap();
1321        isignal_ipdu
1322            .map_signal(
1323                &isignal,
1324                0,
1325                ByteOrder::MostSignificantByteLast,
1326                None,
1327                TransferProperty::Triggered,
1328            )
1329            .unwrap();
1330        // create an ISignalGroup with a second signal
1331        let syssignal_group = package.create_system_signal_group("syssignal_group").unwrap();
1332        let isignal_group = system
1333            .create_isignal_group("isignal_group", &package, &syssignal_group)
1334            .unwrap();
1335        let syssignal2 = package.create_system_signal("syssignal2").unwrap();
1336        let isignal2 = system
1337            .create_isignal("isignal2", &package, 1, &syssignal2, None)
1338            .unwrap();
1339        isignal_ipdu.map_signal_group(&isignal_group).unwrap();
1340        isignal_ipdu
1341            .map_signal(
1342                &isignal2,
1343                1,
1344                ByteOrder::MostSignificantByteLast,
1345                None,
1346                TransferProperty::Triggered,
1347            )
1348            .unwrap();
1349
1350        // create a frame and map the ISignalIPdu to it
1351        let can_cluster = system.create_can_cluster("Cluster", &package, None).unwrap();
1352        let channel = can_cluster.create_physical_channel("Channel").unwrap();
1353        let frame = system.create_can_frame("frame", &package, 8).unwrap();
1354        let frame_triggering = channel
1355            .trigger_frame(&frame, 0x123, CanAddressingMode::Standard, CanFrameType::Can20)
1356            .unwrap();
1357        let _mapping = frame
1358            .map_pdu(&isignal_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
1359            .unwrap();
1360
1361        // create an EcuInstance, and connect it to the channel. The frame is reeived by the ECU
1362        let ecu = system.create_ecu_instance("ecu", &package).unwrap();
1363        let controller = ecu.create_can_communication_controller("controller").unwrap();
1364        controller.connect_physical_channel("connection", &channel).unwrap();
1365        frame_triggering
1366            .connect_to_ecu(&ecu, CommunicationDirection::In)
1367            .unwrap();
1368
1369        let pdu_triggering = frame_triggering.pdu_triggerings().next().unwrap();
1370        assert_eq!(pdu_triggering.pdu_ports().count(), 1);
1371        assert_eq!(pdu_triggering.signal_triggerings().count(), 3); // one for each signal, and another for the signal group
1372
1373        let pdu_port = pdu_triggering.pdu_ports().next().unwrap();
1374        assert_eq!(pdu_port.ecu().unwrap().name().unwrap(), "ecu");
1375        assert_eq!(pdu_port.communication_direction().unwrap(), CommunicationDirection::In);
1376        pdu_port
1377            .set_communication_direction(CommunicationDirection::Out)
1378            .unwrap();
1379        assert_eq!(pdu_port.communication_direction().unwrap(), CommunicationDirection::Out);
1380        pdu_port.set_name("new_name").unwrap();
1381        assert_eq!(pdu_port.name().unwrap(), "new_name");
1382    }
1383
1384    #[test]
1385    fn nm_pdu() {
1386        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00052);
1387        let package = model.get_or_create_package("/pkg").unwrap();
1388        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1389
1390        let nm_pdu = system.create_nm_pdu("nm_pdu", &package, 1).unwrap();
1391        assert_eq!(nm_pdu.length().unwrap(), 1);
1392
1393        nm_pdu.set_length(8).unwrap();
1394        assert_eq!(nm_pdu.length().unwrap(), 8);
1395
1396        nm_pdu.set_unused_bit_pattern(0xff).unwrap();
1397        assert_eq!(nm_pdu.unused_bit_pattern().unwrap(), 0xff);
1398
1399        // create a signal and map it to the PDU
1400        let syssignal = package.create_system_signal("sys_userdata").unwrap();
1401        let isignal = system
1402            .create_isignal("userdata", &package, 16, &syssignal, None)
1403            .unwrap();
1404        let mapping = nm_pdu
1405            .map_signal(
1406                &isignal,
1407                0,
1408                ByteOrder::MostSignificantByteFirst,
1409                Some(16),
1410                TransferProperty::Triggered,
1411            )
1412            .unwrap();
1413        assert_eq!(mapping.signal().unwrap(), isignal);
1414    }
1415
1416    #[test]
1417    fn general_purpose_pdu() {
1418        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1419        let package = model.get_or_create_package("/pkg").unwrap();
1420        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1421
1422        let gp_pdu1 = system
1423            .create_general_purpose_pdu("gp_pdu1", &package, 1, GeneralPurposePduCategory::Sd)
1424            .unwrap();
1425        assert_eq!(gp_pdu1.category().unwrap(), GeneralPurposePduCategory::Sd);
1426
1427        let gp_pdu2 = system
1428            .create_general_purpose_pdu("gp_pdu2", &package, 1, GeneralPurposePduCategory::GlobalTime)
1429            .unwrap();
1430        assert_eq!(gp_pdu2.category().unwrap(), GeneralPurposePduCategory::GlobalTime);
1431
1432        let gp_pdu3 = system
1433            .create_general_purpose_pdu("gp_pdu3", &package, 1, GeneralPurposePduCategory::DoIp)
1434            .unwrap();
1435        assert_eq!(gp_pdu3.category().unwrap(), GeneralPurposePduCategory::DoIp);
1436
1437        // conversion of category to string and back
1438        assert_eq!(
1439            GeneralPurposePduCategory::from_str("SD").unwrap(),
1440            GeneralPurposePduCategory::Sd
1441        );
1442        assert_eq!(
1443            GeneralPurposePduCategory::from_str("GLOBAL_TIME").unwrap(),
1444            GeneralPurposePduCategory::GlobalTime
1445        );
1446        assert_eq!(
1447            GeneralPurposePduCategory::from_str("DOIP").unwrap(),
1448            GeneralPurposePduCategory::DoIp
1449        );
1450        assert!(GeneralPurposePduCategory::from_str("invalid").is_err());
1451    }
1452
1453    #[test]
1454    fn general_purpose_ipdu() {
1455        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1456        let package = model.get_or_create_package("/pkg").unwrap();
1457        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1458
1459        let gp_ipdu1 = system
1460            .create_general_purpose_ipdu("gp_ipdu1", &package, 1, GeneralPurposeIPduCategory::Xcp)
1461            .unwrap();
1462        assert_eq!(gp_ipdu1.category().unwrap(), GeneralPurposeIPduCategory::Xcp);
1463
1464        let gp_ipdu2 = system
1465            .create_general_purpose_ipdu("gp_ipdu2", &package, 1, GeneralPurposeIPduCategory::SomeipSegmentedIpdu)
1466            .unwrap();
1467        assert_eq!(
1468            gp_ipdu2.category().unwrap(),
1469            GeneralPurposeIPduCategory::SomeipSegmentedIpdu
1470        );
1471
1472        let gp_ipdu3 = system
1473            .create_general_purpose_ipdu("gp_ipdu3", &package, 1, GeneralPurposeIPduCategory::Dlt)
1474            .unwrap();
1475        assert_eq!(gp_ipdu3.category().unwrap(), GeneralPurposeIPduCategory::Dlt);
1476
1477        // conversion of category to string and back
1478        assert_eq!(
1479            GeneralPurposeIPduCategory::from_str("XCP").unwrap(),
1480            GeneralPurposeIPduCategory::Xcp
1481        );
1482        assert_eq!(
1483            GeneralPurposeIPduCategory::from_str("SOMEIP_SEGMENTED_IPDU").unwrap(),
1484            GeneralPurposeIPduCategory::SomeipSegmentedIpdu
1485        );
1486        assert_eq!(
1487            GeneralPurposeIPduCategory::from_str("DLT").unwrap(),
1488            GeneralPurposeIPduCategory::Dlt
1489        );
1490        assert!(GeneralPurposeIPduCategory::from_str("invalid").is_err());
1491    }
1492
1493    #[test]
1494    fn multiplexed_ipdu() {
1495        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1496        let package = model.get_or_create_package("/pkg").unwrap();
1497        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1498
1499        let multiplexed_ipdu = system.create_multiplexed_ipdu("multiplexed_ipdu", &package, 1).unwrap();
1500        assert_eq!(multiplexed_ipdu.length().unwrap(), 1);
1501
1502        multiplexed_ipdu.set_length(16).unwrap();
1503        assert_eq!(multiplexed_ipdu.length().unwrap(), 16);
1504
1505        let static_ipdu = system.create_isignal_ipdu("static_ipdu", &package, 8).unwrap();
1506        multiplexed_ipdu.set_static_part(&static_ipdu).unwrap();
1507        assert_eq!(multiplexed_ipdu.static_part().unwrap(), static_ipdu);
1508
1509        let dynamic_ipdu = system.create_isignal_ipdu("dynamic_ipdu", &package, 8).unwrap();
1510        let dp_alt = multiplexed_ipdu.add_dynamic_part(&dynamic_ipdu, 0, true).unwrap();
1511        assert_eq!(dp_alt.ipdu().unwrap(), dynamic_ipdu);
1512        assert_eq!(dp_alt.selector_field_code().unwrap(), 0);
1513        assert_eq!(dp_alt.is_initial_dynamic_part().unwrap(), true);
1514        assert_eq!(dp_alt.multiplexed_ipdu().unwrap(), multiplexed_ipdu);
1515        assert_eq!(multiplexed_ipdu.dynamic_part_alternatives().count(), 1);
1516
1517        multiplexed_ipdu
1518            .set_selector_field(8, 16, ByteOrder::MostSignificantByteFirst)
1519            .unwrap();
1520        let (start_bit, length, byte_order) = multiplexed_ipdu.selector_field().unwrap();
1521        assert_eq!(start_bit, 8);
1522        assert_eq!(length, 16);
1523        assert_eq!(byte_order, ByteOrder::MostSignificantByteFirst);
1524
1525        let cluster = system.create_can_cluster("cluster", &package, None).unwrap();
1526        let channel = cluster.create_physical_channel("channel").unwrap();
1527        let frame = system.create_can_frame("frame", &package, 64).unwrap();
1528        channel
1529            .trigger_frame(&frame, 0x33, CanAddressingMode::Standard, CanFrameType::CanFd)
1530            .unwrap();
1531        let _mapping = frame
1532            .map_pdu(&multiplexed_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
1533            .unwrap();
1534
1535        assert_eq!(channel.frame_triggerings().count(), 1);
1536        assert_eq!(channel.pdu_triggerings().count(), 3); // multiplex pdu + static part + dynamic part
1537    }
1538
1539    #[test]
1540    fn ipdu() {
1541        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1542        let package = model.get_or_create_package("/pkg").unwrap();
1543        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1544
1545        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
1546        let n_pdu = system.create_n_pdu("n_pdu", &package, 1).unwrap();
1547        let dcm_ipdu = system
1548            .create_dcm_ipdu("dcm_ipdu", &package, 1, DiagPduType::DiagResponse)
1549            .unwrap();
1550        let gp_ipdu = system
1551            .create_general_purpose_ipdu("gp_ipdu", &package, 1, GeneralPurposeIPduCategory::Xcp)
1552            .unwrap();
1553        let container_ipdu = system
1554            .create_container_ipdu(
1555                "container_ipdu",
1556                &package,
1557                1,
1558                ContainerIPduHeaderType::LongHeader,
1559                RxAcceptContainedIPdu::AcceptConfigured,
1560            )
1561            .unwrap();
1562        let secured_ipdu = system
1563            .create_secured_ipdu("secured_ipdu", &package, 1, &SecureCommunicationProps::default())
1564            .unwrap();
1565        let multiplexed_ipdu = system.create_multiplexed_ipdu("multiplexed_ipdu", &package, 1).unwrap();
1566
1567        let ipdu: IPdu = isignal_ipdu.clone().into();
1568        assert_eq!(ipdu.element(), isignal_ipdu.element());
1569        assert!(matches!(ipdu, IPdu::ISignalIPdu(_)));
1570
1571        // NmPdu is not an ipdu
1572
1573        let ipdu: IPdu = n_pdu.clone().into();
1574        assert_eq!(ipdu.element(), n_pdu.element());
1575        assert!(matches!(ipdu, IPdu::NPdu(_)));
1576
1577        let ipdu: IPdu = dcm_ipdu.clone().into();
1578        assert_eq!(ipdu.element(), dcm_ipdu.element());
1579        assert!(matches!(ipdu, IPdu::DcmIPdu(_)));
1580
1581        let ipdu: IPdu = n_pdu.clone().into();
1582        assert_eq!(ipdu.element(), n_pdu.element());
1583        assert!(matches!(ipdu, IPdu::NPdu(_)));
1584
1585        // GeneralPurposePdu is not an ipdu
1586
1587        let ipdu: IPdu = gp_ipdu.clone().into();
1588        assert_eq!(ipdu.element(), gp_ipdu.element());
1589        assert!(matches!(ipdu, IPdu::GeneralPurposeIPdu(_)));
1590
1591        let ipdu: IPdu = container_ipdu.clone().into();
1592        assert_eq!(ipdu.element(), container_ipdu.element());
1593        assert!(matches!(ipdu, IPdu::ContainerIPdu(_)));
1594
1595        let ipdu: IPdu = secured_ipdu.clone().into();
1596        assert_eq!(ipdu.element(), secured_ipdu.element());
1597        assert!(matches!(ipdu, IPdu::SecuredIPdu(_)));
1598
1599        let ipdu: IPdu = multiplexed_ipdu.clone().into();
1600        assert_eq!(ipdu.element(), multiplexed_ipdu.element());
1601        assert!(matches!(ipdu, IPdu::MultiplexedIPdu(_)));
1602
1603        // any Ipdu can be converted to a Pdu
1604        let pdu: Pdu = ipdu.clone().into();
1605        assert_eq!(pdu.element(), ipdu.element());
1606    }
1607}