autosar_data_abstraction/communication/pdu/
mod.rs

1use crate::communication::{
2    AbstractPhysicalChannel, CommunicationDirection, ISignal, ISignalGroup, ISignalTriggering, PduToFrameMapping,
3    PhysicalChannel, SoConIPduIdentifier, SomeipTpConnection, TransferProperty,
4};
5use crate::{
6    AbstractionElement, ArPackage, AutosarAbstractionError, ByteOrder, EcuInstance, IdentifiableAbstractionElement,
7    abstraction_element, get_reference_parents, is_used_system_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    /// remove this `NmPdu` from the model
98    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
99        // remove all triggerings of this PDU
100        for pdu_triggering in self.pdu_triggerings() {
101            let _ = pdu_triggering.element().remove_sub_element_kind(ElementName::IPduRef);
102            let _ = pdu_triggering.remove(deep);
103        }
104
105        for signal_mapping in self.mapped_signals() {
106            let _ = signal_mapping.remove(deep);
107        }
108
109        let ref_parents = get_reference_parents(self.element())?;
110
111        AbstractionElement::remove(self, deep)?;
112
113        for (named_parent, _parent) in ref_parents {
114            if named_parent.element_name() == ElementName::PduToFrameMapping
115                && let Ok(pdu_to_frame_mapping) = PduToFrameMapping::try_from(named_parent)
116            {
117                pdu_to_frame_mapping.remove(deep)?;
118            }
119        }
120
121        Ok(())
122    }
123
124    /// set the unused bit pattern for this NmPdu
125    pub fn set_unused_bit_pattern(&self, pattern: u8) -> Result<(), AutosarAbstractionError> {
126        self.element()
127            .get_or_create_sub_element(ElementName::UnusedBitPattern)?
128            .set_character_data(pattern.to_string())?;
129        Ok(())
130    }
131
132    /// get the unused bit pattern for this NmPdu
133    #[must_use]
134    pub fn unused_bit_pattern(&self) -> Option<u8> {
135        self.element()
136            .get_sub_element(ElementName::UnusedBitPattern)?
137            .character_data()?
138            .parse_integer()
139    }
140
141    /// map a signal to the `ISignalIPdu`
142    ///
143    /// If this signal is part of a signal group, then the group must be mapped first
144    pub fn map_signal(
145        &self,
146        signal: &ISignal,
147        start_position: u32,
148        byte_order: ByteOrder,
149        update_bit: Option<u32>,
150        transfer_property: TransferProperty,
151    ) -> Result<ISignalToIPduMapping, AutosarAbstractionError> {
152        let signal_name = signal
153            .name()
154            .ok_or(AutosarAbstractionError::InvalidParameter("invalid signal".to_string()))?;
155
156        verify_signal_mapping(self, signal, start_position, byte_order, update_bit, &signal_name)?;
157
158        // add a pdu triggering for the newly mapped PDU to each frame triggering of this frame
159        for pt in self.pdu_triggerings() {
160            let st = pt.create_signal_triggering(signal)?;
161            for pdu_port in pt.pdu_ports() {
162                if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
163                    st.connect_to_ecu(&ecu, direction)?;
164                }
165            }
166        }
167
168        // create and return the new mapping
169        let model = self.element().model()?;
170        let base_path = self.element().path()?;
171        let name = make_unique_name(&model, &base_path, &signal_name);
172
173        // the crucial difference between NmPdu and ISignalIPdu is here
174        // NmPdu uses ISignalToIPduMapping, while ISignalIPdu uses ISignalToPduMapping
175        let mappings = self
176            .element()
177            .get_or_create_sub_element(ElementName::ISignalToIPduMappings)?;
178
179        ISignalToIPduMapping::new_with_signal(
180            &name,
181            &mappings,
182            signal,
183            start_position,
184            byte_order,
185            update_bit,
186            transfer_property,
187        )
188    }
189
190    /// map a signal group to the PDU
191    pub fn map_signal_group(
192        &self,
193        signal_group: &ISignalGroup,
194    ) -> Result<ISignalToIPduMapping, AutosarAbstractionError> {
195        let signal_group_name = signal_group.name().ok_or(AutosarAbstractionError::InvalidParameter(
196            "invalid signal group".to_string(),
197        ))?;
198
199        // add a pdu triggering for the newly mapped PDU to each frame triggering of this frame
200        for pt in self.pdu_triggerings() {
201            let st = pt.create_signal_group_triggering(signal_group)?;
202            for pdu_port in pt.pdu_ports() {
203                if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
204                    st.connect_to_ecu(&ecu, direction)?;
205                }
206            }
207        }
208
209        // create and return the new mapping
210        let model = self.element().model()?;
211        let base_path = self.element().path()?;
212        let name = make_unique_name(&model, &base_path, &signal_group_name);
213
214        // the crucial difference between NmPdu and ISignalIPdu is here
215        // NmPdu uses ISignalToIPduMapping, while ISignalIPdu uses ISignalToPduMapping
216        let mappings = self
217            .element()
218            .get_or_create_sub_element(ElementName::ISignalToIPduMappings)?;
219
220        ISignalToIPduMapping::new_with_group(&name, &mappings, signal_group)
221    }
222}
223
224impl AbstractPdu for NmPdu {}
225
226impl SignalPdu for NmPdu {
227    /// returns an iterator over all signals and signal groups mapped to the PDU
228    fn mapped_signals(&self) -> impl Iterator<Item = ISignalToIPduMapping> + Send + use<> {
229        self.element()
230            .get_sub_element(ElementName::ISignalToIPduMappings)
231            .into_iter()
232            .flat_map(|mappings| mappings.sub_elements())
233            .filter_map(|elem| ISignalToIPduMapping::try_from(elem).ok())
234    }
235
236    fn map_signal(
237        &self,
238        signal: &ISignal,
239        start_position: u32,
240        byte_order: ByteOrder,
241        update_bit: Option<u32>,
242        transfer_property: TransferProperty,
243    ) -> Result<ISignalToIPduMapping, AutosarAbstractionError> {
244        NmPdu::map_signal(self, signal, start_position, byte_order, update_bit, transfer_property)
245    }
246
247    fn map_signal_group(&self, signal_group: &ISignalGroup) -> Result<ISignalToIPduMapping, AutosarAbstractionError> {
248        NmPdu::map_signal_group(self, signal_group)
249    }
250}
251
252impl From<NmPdu> for Pdu {
253    fn from(value: NmPdu) -> Self {
254        Pdu::NmPdu(value)
255    }
256}
257
258//##################################################################
259
260/// This is a Pdu of the transport layer. The main purpose of the TP layer is to segment and reassemble `IPdus`.
261#[derive(Debug, Clone, PartialEq, Eq, Hash)]
262pub struct NPdu(Element);
263abstraction_element!(NPdu, NPdu);
264impl IdentifiableAbstractionElement for NPdu {}
265
266impl NPdu {
267    pub(crate) fn new(name: &str, package: &ArPackage, length: u32) -> Result<Self, AutosarAbstractionError> {
268        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
269        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::NPdu, name)?;
270        elem_pdu
271            .create_sub_element(ElementName::Length)?
272            .set_character_data(length.to_string())?;
273
274        Ok(Self(elem_pdu))
275    }
276
277    /// remove this `NPdu` from the model
278    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
279        // remove all triggerings of this PDU
280        for pdu_triggering in self.pdu_triggerings() {
281            let _ = pdu_triggering.element().remove_sub_element_kind(ElementName::IPduRef);
282            let _ = pdu_triggering.remove(deep);
283        }
284
285        let ref_parents = get_reference_parents(self.element())?;
286
287        AbstractionElement::remove(self, deep)?;
288
289        for (named_parent, _parent) in ref_parents {
290            if named_parent.element_name() == ElementName::PduToFrameMapping
291                && let Ok(pdu_to_frame_mapping) = PduToFrameMapping::try_from(named_parent)
292            {
293                pdu_to_frame_mapping.remove(deep)?;
294            }
295        }
296
297        Ok(())
298    }
299}
300
301impl AbstractPdu for NPdu {}
302
303impl AbstractIpdu for NPdu {}
304
305impl From<NPdu> for Pdu {
306    fn from(value: NPdu) -> Self {
307        Pdu::NPdu(value)
308    }
309}
310
311impl From<NPdu> for IPdu {
312    fn from(value: NPdu) -> Self {
313        IPdu::NPdu(value)
314    }
315}
316
317//##################################################################
318
319/// Represents the `IPdus` handled by Dcm
320#[derive(Debug, Clone, PartialEq, Eq, Hash)]
321pub struct DcmIPdu(Element);
322abstraction_element!(DcmIPdu, DcmIPdu);
323impl IdentifiableAbstractionElement for DcmIPdu {}
324
325impl DcmIPdu {
326    pub(crate) fn new(
327        name: &str,
328        package: &ArPackage,
329        length: u32,
330        diag_pdu_type: DiagPduType,
331    ) -> Result<Self, AutosarAbstractionError> {
332        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
333        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::DcmIPdu, name)?;
334        elem_pdu
335            .create_sub_element(ElementName::Length)?
336            .set_character_data(length.to_string())?;
337        let dcm_ipdu = Self(elem_pdu);
338        dcm_ipdu.set_diag_pdu_type(diag_pdu_type)?;
339
340        Ok(dcm_ipdu)
341    }
342
343    /// remove this `DcmIPdu` from the model
344    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
345        // remove all triggerings of this PDU
346        for pdu_triggering in self.pdu_triggerings() {
347            let _ = pdu_triggering.element().remove_sub_element_kind(ElementName::IPduRef);
348            let _ = pdu_triggering.remove(deep);
349        }
350
351        let ref_parents = get_reference_parents(self.element())?;
352
353        AbstractionElement::remove(self, deep)?;
354
355        for (named_parent, _parent) in ref_parents {
356            if named_parent.element_name() == ElementName::PduToFrameMapping
357                && let Ok(pdu_to_frame_mapping) = PduToFrameMapping::try_from(named_parent)
358            {
359                pdu_to_frame_mapping.remove(deep)?;
360            }
361        }
362
363        Ok(())
364    }
365
366    /// set the type of this DcmIPdu
367    pub fn set_diag_pdu_type(&self, pdu_type: DiagPduType) -> Result<(), AutosarAbstractionError> {
368        self.element()
369            .get_or_create_sub_element(ElementName::DiagPduType)?
370            .set_character_data::<EnumItem>(pdu_type.into())?;
371        Ok(())
372    }
373
374    /// get the type of this DcmIPdu
375    #[must_use]
376    pub fn diag_pdu_type(&self) -> Option<DiagPduType> {
377        let enum_item = self
378            .element()
379            .get_sub_element(ElementName::DiagPduType)?
380            .character_data()?
381            .enum_value()?;
382        DiagPduType::try_from(enum_item).ok()
383    }
384}
385
386impl AbstractPdu for DcmIPdu {}
387
388impl AbstractIpdu for DcmIPdu {}
389
390impl From<DcmIPdu> for Pdu {
391    fn from(value: DcmIPdu) -> Self {
392        Pdu::DcmIPdu(value)
393    }
394}
395
396impl From<DcmIPdu> for IPdu {
397    fn from(value: DcmIPdu) -> Self {
398        IPdu::DcmIPdu(value)
399    }
400}
401
402//##################################################################
403
404/// The type of a DcmIPdu
405#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
406pub enum DiagPduType {
407    /// Diagnostic Request
408    DiagRequest,
409    /// Diagnostic Response
410    DiagResponse,
411}
412
413impl TryFrom<EnumItem> for DiagPduType {
414    type Error = AutosarAbstractionError;
415
416    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
417        match value {
418            EnumItem::DiagRequest => Ok(DiagPduType::DiagRequest),
419            EnumItem::DiagResponse => Ok(DiagPduType::DiagResponse),
420            _ => Err(AutosarAbstractionError::ValueConversionError {
421                value: value.to_string(),
422                dest: "DiagPduType".to_string(),
423            }),
424        }
425    }
426}
427
428impl From<DiagPduType> for EnumItem {
429    fn from(value: DiagPduType) -> Self {
430        match value {
431            DiagPduType::DiagRequest => EnumItem::DiagRequest,
432            DiagPduType::DiagResponse => EnumItem::DiagResponse,
433        }
434    }
435}
436
437//##################################################################
438
439/// This element is used for AUTOSAR Pdus without additional attributes that are routed by a bus interface
440#[derive(Debug, Clone, PartialEq, Eq, Hash)]
441pub struct GeneralPurposePdu(Element);
442abstraction_element!(GeneralPurposePdu, GeneralPurposePdu);
443impl IdentifiableAbstractionElement for GeneralPurposePdu {}
444
445impl GeneralPurposePdu {
446    pub(crate) fn new(
447        name: &str,
448        package: &ArPackage,
449        length: u32,
450        category: GeneralPurposePduCategory,
451    ) -> Result<Self, AutosarAbstractionError> {
452        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
453        let pdu_elem = pkg_elements.create_named_sub_element(ElementName::GeneralPurposePdu, name)?;
454        let pdu = Self(pdu_elem);
455
456        pdu.set_length(length)?;
457        pdu.set_category(category)?;
458
459        Ok(pdu)
460    }
461
462    /// remove this `GeneralPurposePdu` from the model
463    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
464        // remove all triggerings of this PDU
465        for pdu_triggering in self.pdu_triggerings() {
466            let _ = pdu_triggering.element().remove_sub_element_kind(ElementName::IPduRef);
467            let _ = pdu_triggering.remove(deep);
468        }
469
470        let ref_parents = get_reference_parents(self.element())?;
471
472        AbstractionElement::remove(self, deep)?;
473
474        for (named_parent, _parent) in ref_parents {
475            if named_parent.element_name() == ElementName::PduToFrameMapping
476                && let Ok(pdu_to_frame_mapping) = PduToFrameMapping::try_from(named_parent)
477            {
478                pdu_to_frame_mapping.remove(deep)?;
479            }
480        }
481
482        Ok(())
483    }
484
485    /// set the category of this PDU
486    pub fn set_category(&self, category: GeneralPurposePduCategory) -> Result<(), AutosarAbstractionError> {
487        self.element()
488            .get_or_create_sub_element(ElementName::Category)?
489            .set_character_data(category.to_string())?;
490        Ok(())
491    }
492
493    /// get the category of this PDU
494    #[must_use]
495    pub fn category(&self) -> Option<GeneralPurposePduCategory> {
496        let category_string = self
497            .element()
498            .get_sub_element(ElementName::Category)?
499            .character_data()?
500            .string_value()?;
501        GeneralPurposePduCategory::from_str(&category_string).ok()
502    }
503}
504
505impl AbstractPdu for GeneralPurposePdu {}
506
507impl From<GeneralPurposePdu> for Pdu {
508    fn from(value: GeneralPurposePdu) -> Self {
509        Pdu::GeneralPurposePdu(value)
510    }
511}
512
513//##################################################################
514
515/// The category of a `GeneralPurposePdu`
516///
517/// The Autosar standard defines the following categories:
518/// - `SD`
519/// - `GLOBAL_TIME`
520/// - `DOIP`
521#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
522pub enum GeneralPurposePduCategory {
523    /// Service Discovery
524    Sd,
525    /// Global Time Synchronization
526    GlobalTime,
527    /// Diagnostic over IP
528    DoIp,
529}
530
531impl std::fmt::Display for GeneralPurposePduCategory {
532    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
533        match self {
534            GeneralPurposePduCategory::Sd => write!(f, "SD"),
535            GeneralPurposePduCategory::GlobalTime => write!(f, "GLOBAL_TIME"),
536            GeneralPurposePduCategory::DoIp => write!(f, "DOIP"),
537        }
538    }
539}
540
541impl std::str::FromStr for GeneralPurposePduCategory {
542    type Err = AutosarAbstractionError;
543
544    fn from_str(s: &str) -> Result<Self, Self::Err> {
545        match s {
546            "SD" => Ok(GeneralPurposePduCategory::Sd),
547            "GLOBAL_TIME" => Ok(GeneralPurposePduCategory::GlobalTime),
548            "DOIP" => Ok(GeneralPurposePduCategory::DoIp),
549            _ => Err(AutosarAbstractionError::InvalidParameter(s.to_string())),
550        }
551    }
552}
553
554//##################################################################
555
556/// This element is used for AUTOSAR Pdus without attributes that are routed by the `PduR`
557#[derive(Debug, Clone, PartialEq, Eq, Hash)]
558pub struct GeneralPurposeIPdu(Element);
559abstraction_element!(GeneralPurposeIPdu, GeneralPurposeIPdu);
560impl IdentifiableAbstractionElement for GeneralPurposeIPdu {}
561
562impl GeneralPurposeIPdu {
563    pub(crate) fn new(
564        name: &str,
565        package: &ArPackage,
566        length: u32,
567        category: GeneralPurposeIPduCategory,
568    ) -> Result<Self, AutosarAbstractionError> {
569        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
570        let pdu_elem = pkg_elements.create_named_sub_element(ElementName::GeneralPurposeIPdu, name)?;
571        let pdu = Self(pdu_elem);
572
573        pdu.set_length(length)?;
574        pdu.set_category(category)?;
575
576        Ok(pdu)
577    }
578
579    /// remove this `GeneralPurposeIPdu` from the model
580    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
581        // remove all triggerings of this PDU
582        for pdu_triggering in self.pdu_triggerings() {
583            let _ = pdu_triggering.element().remove_sub_element_kind(ElementName::IPduRef);
584            let _ = pdu_triggering.remove(deep);
585        }
586
587        let ref_parents = get_reference_parents(self.element())?;
588
589        AbstractionElement::remove(self, deep)?;
590
591        for (named_parent, _parent) in ref_parents {
592            if named_parent.element_name() == ElementName::PduToFrameMapping
593                && let Ok(pdu_to_frame_mapping) = PduToFrameMapping::try_from(named_parent)
594            {
595                pdu_to_frame_mapping.remove(deep)?;
596            }
597        }
598
599        Ok(())
600    }
601
602    /// set the category of this PDU
603    pub fn set_category(&self, category: GeneralPurposeIPduCategory) -> Result<(), AutosarAbstractionError> {
604        self.element()
605            .get_or_create_sub_element(ElementName::Category)?
606            .set_character_data(category.to_string())?;
607        Ok(())
608    }
609
610    /// get the category of this PDU
611    #[must_use]
612    pub fn category(&self) -> Option<GeneralPurposeIPduCategory> {
613        let category_string = self
614            .element()
615            .get_sub_element(ElementName::Category)?
616            .character_data()?
617            .string_value()?;
618        GeneralPurposeIPduCategory::from_str(&category_string).ok()
619    }
620}
621
622impl AbstractPdu for GeneralPurposeIPdu {}
623
624impl AbstractIpdu for GeneralPurposeIPdu {}
625
626impl From<GeneralPurposeIPdu> for Pdu {
627    fn from(value: GeneralPurposeIPdu) -> Self {
628        Pdu::GeneralPurposeIPdu(value)
629    }
630}
631
632impl From<GeneralPurposeIPdu> for IPdu {
633    fn from(value: GeneralPurposeIPdu) -> Self {
634        IPdu::GeneralPurposeIPdu(value)
635    }
636}
637
638//##################################################################
639
640/// The category of a `GeneralPurposeIPdu`
641///
642/// The Autosar standard defines the following categories:
643/// - XCP
644/// - `SOMEIP_SEGMENTED_IPDU`
645/// - DLT
646#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
647pub enum GeneralPurposeIPduCategory {
648    /// XCP
649    Xcp,
650    /// SOME/IP Segmented `IPdu`
651    SomeipSegmentedIpdu,
652    /// Diagnostic Log and Trace
653    Dlt,
654}
655
656impl std::fmt::Display for GeneralPurposeIPduCategory {
657    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
658        match self {
659            GeneralPurposeIPduCategory::Xcp => write!(f, "XCP"),
660            GeneralPurposeIPduCategory::SomeipSegmentedIpdu => write!(f, "SOMEIP_SEGMENTED_IPDU"),
661            GeneralPurposeIPduCategory::Dlt => write!(f, "DLT"),
662        }
663    }
664}
665
666impl std::str::FromStr for GeneralPurposeIPduCategory {
667    type Err = AutosarAbstractionError;
668
669    fn from_str(s: &str) -> Result<Self, Self::Err> {
670        match s {
671            "XCP" => Ok(GeneralPurposeIPduCategory::Xcp),
672            "SOMEIP_SEGMENTED_IPDU" => Ok(GeneralPurposeIPduCategory::SomeipSegmentedIpdu),
673            "DLT" => Ok(GeneralPurposeIPduCategory::Dlt),
674            _ => Err(AutosarAbstractionError::InvalidParameter(s.to_string())),
675        }
676    }
677}
678
679//##################################################################
680
681/// The multiplexed pdu contains one of serveral signal pdus
682#[derive(Debug, Clone, PartialEq, Eq, Hash)]
683pub struct MultiplexedIPdu(Element);
684abstraction_element!(MultiplexedIPdu, MultiplexedIPdu);
685impl IdentifiableAbstractionElement for MultiplexedIPdu {}
686
687impl MultiplexedIPdu {
688    pub(crate) fn new(name: &str, package: &ArPackage, length: u32) -> Result<Self, AutosarAbstractionError> {
689        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
690        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::MultiplexedIPdu, name)?;
691        elem_pdu
692            .create_sub_element(ElementName::Length)?
693            .set_character_data(length.to_string())?;
694
695        Ok(Self(elem_pdu))
696    }
697
698    /// remove this `GeneralPurposeIPdu` from the model
699    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
700        // remove all triggerings of this PDU
701        for pdu_triggering in self.pdu_triggerings() {
702            let _ = pdu_triggering.element().remove_sub_element_kind(ElementName::IPduRef);
703            let _ = pdu_triggering.remove(deep);
704        }
705
706        let opt_static_pdu = self.static_part();
707
708        for dynamic_part in self.dynamic_part_alternatives() {
709            dynamic_part.remove(deep)?;
710        }
711
712        let ref_parents = get_reference_parents(self.element())?;
713
714        AbstractionElement::remove(self, deep)?;
715
716        for (named_parent, _parent) in ref_parents {
717            if named_parent.element_name() == ElementName::PduToFrameMapping
718                && let Ok(pdu_to_frame_mapping) = PduToFrameMapping::try_from(named_parent)
719            {
720                pdu_to_frame_mapping.remove(deep)?;
721            }
722        }
723
724        if let Some(ipdu) = opt_static_pdu {
725            let mut triggerings = ipdu.pdu_triggerings();
726            // because the multiplexed ipdu does not keep a reference to the pdu triggering of it's parts,
727            // we don't know which PDU triggering to remove if there is more than one
728            if triggerings.len() == 1
729                && let Some(pdu_triggering) = triggerings.pop()
730            {
731                pdu_triggering.remove(deep)?;
732            }
733        }
734
735        Ok(())
736    }
737
738    /// set the ISignalIPdu containing the static part of this multiplexed ipdu
739    pub fn set_static_part(&self, static_ipdu: &ISignalIPdu) -> Result<(), AutosarAbstractionError> {
740        let prev_static_part = self.static_part();
741
742        self.element()
743            .get_or_create_sub_element(ElementName::StaticParts)?
744            .get_or_create_sub_element(ElementName::StaticPart)?
745            .get_or_create_sub_element(ElementName::IPduRef)?
746            .set_reference_target(static_ipdu.element())?;
747
748        self.update_pdu_triggerings(prev_static_part.as_ref(), static_ipdu)?;
749
750        Ok(())
751    }
752
753    /// get the ISignalIPdu containing the static part of this multiplexed ipdu
754    #[must_use]
755    pub fn static_part(&self) -> Option<ISignalIPdu> {
756        let ipdu_elem = self
757            .element()
758            .get_sub_element(ElementName::StaticParts)?
759            .get_sub_element(ElementName::StaticPart)?
760            .get_sub_element(ElementName::IPduRef)?
761            .get_reference_target()
762            .ok()?;
763        ISignalIPdu::try_from(ipdu_elem).ok()
764    }
765
766    /// add a dynamic part alternative to this multiplexed ipdu
767    /// `selector_code` is the value of the multiplexor that selects this dynamic part
768    /// `initial_dynamic_part` indicates whether this is the initial dynamic part; only one dynamic part can be initial
769    ///
770    /// All dynamic parts must have the same length
771    pub fn add_dynamic_part(
772        &self,
773        dynamic_ipdu: &ISignalIPdu,
774        selector_code: u16,
775        initial_dynamic_part: bool,
776    ) -> Result<DynamicPartAlternative, AutosarAbstractionError> {
777        let dp_alternatives = self
778            .element()
779            .get_or_create_sub_element(ElementName::DynamicParts)?
780            .get_or_create_sub_element(ElementName::DynamicPart)?
781            .get_or_create_sub_element(ElementName::DynamicPartAlternatives)?;
782
783        DynamicPartAlternative::new(&dp_alternatives, dynamic_ipdu, selector_code, initial_dynamic_part)
784    }
785
786    /// list all dynamic part alternatives of this multiplexed ipdu
787    pub fn dynamic_part_alternatives(&self) -> impl Iterator<Item = DynamicPartAlternative> + Send + use<> {
788        let dp_alternatives_elem = self
789            .element()
790            .get_sub_element(ElementName::DynamicParts)
791            .and_then(|e| e.get_sub_element(ElementName::DynamicPart))
792            .and_then(|e| e.get_sub_element(ElementName::DynamicPartAlternatives));
793
794        dp_alternatives_elem
795            .into_iter()
796            .flat_map(|e| e.sub_elements())
797            .filter_map(|elem| DynamicPartAlternative::try_from(elem).ok())
798    }
799
800    /// set the selector field of this multiplexed ipdu
801    ///
802    /// The selector field should exist as a signal in each dynamic part
803    pub fn set_selector_field(
804        &self,
805        length: u8,
806        start_position: u32,
807        byte_order: ByteOrder,
808    ) -> Result<(), AutosarAbstractionError> {
809        if length == 0 || length > 16 {
810            return Err(AutosarAbstractionError::InvalidParameter(
811                "selector field length must be between 1 and 16".to_string(),
812            ));
813        }
814        self.element()
815            .get_or_create_sub_element(ElementName::SelectorFieldLength)?
816            .set_character_data(length as u64)?;
817        self.element()
818            .get_or_create_sub_element(ElementName::SelectorFieldStartPosition)?
819            .set_character_data(start_position as u64)?;
820        self.element()
821            .get_or_create_sub_element(ElementName::SelectorFieldByteOrder)?
822            .set_character_data::<EnumItem>(byte_order.into())?;
823        Ok(())
824    }
825
826    /// get the selector field of this multiplexed ipdu
827    pub fn selector_field(&self) -> Option<(u8, u32, ByteOrder)> {
828        let length = self
829            .element()
830            .get_sub_element(ElementName::SelectorFieldLength)?
831            .character_data()?
832            .parse_integer()?;
833        let start_position = self
834            .element()
835            .get_sub_element(ElementName::SelectorFieldStartPosition)?
836            .character_data()?
837            .parse_integer()?;
838        let byte_order_enum = self
839            .element()
840            .get_sub_element(ElementName::SelectorFieldByteOrder)?
841            .character_data()?
842            .enum_value()?;
843        let byte_order = ByteOrder::try_from(byte_order_enum).ok()?;
844        Some((length, start_position, byte_order))
845    }
846
847    // Update the pdu triggerings when the static part or a dynamic part is changed
848    // Also used when the multiplexed ipdu is newly mapped to a frame
849    pub(crate) fn update_pdu_triggerings(
850        &self,
851        old_ipdu: Option<&ISignalIPdu>,
852        new_ipdu: &ISignalIPdu,
853    ) -> Result<(), AutosarAbstractionError> {
854        // note: usually, the MultiplexedIPdu is triggered exactly once, and thus the pdu_triggerings list has exactly one entry
855        // remove the pdu triggering(s) of the previous ipdu
856        if let Some(old_ipdu) = old_ipdu {
857            for pt in old_ipdu.pdu_triggerings() {
858                pt.remove(false)?;
859            }
860        }
861
862        // create pdu triggerings for the new ipdu part in channels where this multiplexed ipdu is triggered
863        for multiplex_pt in self.pdu_triggerings() {
864            if let Ok(channel) = multiplex_pt.physical_channel() {
865                let new_pt = PduTriggering::new(&Pdu::ISignalIPdu(new_ipdu.clone()), &channel)?;
866                for pp in multiplex_pt.pdu_ports() {
867                    if let (Ok(ecu), Some(direction)) = (pp.ecu(), pp.communication_direction()) {
868                        let _ = new_pt.create_pdu_port(&ecu, direction);
869                    }
870                }
871            }
872        }
873
874        Ok(())
875    }
876}
877
878impl AbstractPdu for MultiplexedIPdu {}
879
880impl AbstractIpdu for MultiplexedIPdu {}
881
882impl From<MultiplexedIPdu> for Pdu {
883    fn from(value: MultiplexedIPdu) -> Self {
884        Pdu::MultiplexedIPdu(value)
885    }
886}
887
888impl From<MultiplexedIPdu> for IPdu {
889    fn from(value: MultiplexedIPdu) -> Self {
890        IPdu::MultiplexedIPdu(value)
891    }
892}
893
894//##################################################################
895
896/// A dynamic part alternative of a multiplexed PDU
897#[derive(Debug, Clone, PartialEq, Eq, Hash)]
898pub struct DynamicPartAlternative(Element);
899abstraction_element!(DynamicPartAlternative, DynamicPartAlternative);
900
901impl DynamicPartAlternative {
902    fn new(
903        parent: &Element,
904        dynamic_ipdu: &ISignalIPdu,
905        selector_code: u16,
906        initial_dynamic_part: bool,
907    ) -> Result<Self, AutosarAbstractionError> {
908        let dp_alt_elem = parent.create_sub_element(ElementName::DynamicPartAlternative)?;
909        let dp_alt = Self(dp_alt_elem);
910        dp_alt.set_ipdu(dynamic_ipdu)?;
911        dp_alt.set_selector_field_code(selector_code)?;
912        dp_alt.set_initial_dynamic_part(initial_dynamic_part)?;
913
914        Ok(dp_alt)
915    }
916
917    /// remove this dynamic part alternative from the model
918    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
919        // remove all triggerings of this PDU
920        let opt_ipdu = self.ipdu();
921
922        AbstractionElement::remove(self, deep)?;
923
924        if let Some(ipdu) = opt_ipdu {
925            let mut triggerings = ipdu.pdu_triggerings();
926            // because the multiplexed ipdu does not keep a reference to the pdu triggering of its parts,
927            // we don't know which PDU triggering to remove if there is more than one
928            if triggerings.len() == 1
929                && let Some(pdu_triggering) = triggerings.pop()
930            {
931                pdu_triggering.remove(deep)?;
932            }
933        }
934
935        Ok(())
936    }
937
938    /// set the `ISignalIPdu` referenced by this dynamic part alternative
939    pub fn set_ipdu(&self, ipdu: &ISignalIPdu) -> Result<(), AutosarAbstractionError> {
940        let old_ipdu = self.ipdu();
941        self.element()
942            .get_or_create_sub_element(ElementName::IPduRef)?
943            .set_reference_target(ipdu.element())?;
944
945        self.multiplexed_ipdu()?
946            .update_pdu_triggerings(old_ipdu.as_ref(), ipdu)?;
947
948        Ok(())
949    }
950
951    /// get the `ISignalIPdu` referenced by this dynamic part alternative
952    #[must_use]
953    pub fn ipdu(&self) -> Option<ISignalIPdu> {
954        let ipdu_elem = self
955            .element()
956            .get_sub_element(ElementName::IPduRef)?
957            .get_reference_target()
958            .ok()?;
959        ISignalIPdu::try_from(ipdu_elem).ok()
960    }
961
962    /// set the selector field code of this dynamic part alternative
963    pub fn set_selector_field_code(&self, code: u16) -> Result<(), AutosarAbstractionError> {
964        self.element()
965            .get_or_create_sub_element(ElementName::SelectorFieldCode)?
966            .set_character_data(code as u64)?;
967        Ok(())
968    }
969
970    /// get the selector field code of this dynamic part alternative
971    #[must_use]
972    pub fn selector_field_code(&self) -> Option<u16> {
973        self.element()
974            .get_sub_element(ElementName::SelectorFieldCode)?
975            .character_data()?
976            .parse_integer()
977    }
978
979    /// set whether this is the initial dynamic part
980    pub fn set_initial_dynamic_part(&self, initial: bool) -> Result<(), AutosarAbstractionError> {
981        self.element()
982            .get_or_create_sub_element(ElementName::InitialDynamicPart)?
983            .set_character_data(initial)?;
984        Ok(())
985    }
986
987    /// check whether this is the initial dynamic part
988    #[must_use]
989    pub fn is_initial_dynamic_part(&self) -> Option<bool> {
990        self.element()
991            .get_sub_element(ElementName::InitialDynamicPart)?
992            .character_data()?
993            .parse_bool()
994    }
995
996    /// get the multiplexed ipdu containing this dynamic part alternative
997    pub fn multiplexed_ipdu(&self) -> Result<MultiplexedIPdu, AutosarAbstractionError> {
998        let parent_elem = self.element().named_parent()?;
999        let parent_elem = parent_elem.unwrap(); // unwrap is safe here because the parent must exist
1000        MultiplexedIPdu::try_from(parent_elem)
1001    }
1002}
1003
1004//##################################################################
1005
1006/// This element is used for user defined AUTOSAR Pdus
1007///
1008/// This PDU type is occasionally used for time-sync messages
1009#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1010pub struct UserDefinedPdu(Element);
1011abstraction_element!(UserDefinedPdu, UserDefinedPdu);
1012impl IdentifiableAbstractionElement for UserDefinedPdu {}
1013
1014impl UserDefinedPdu {
1015    pub(crate) fn new(name: &str, package: &ArPackage, length: u32) -> Result<Self, AutosarAbstractionError> {
1016        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
1017        let pdu_elem = pkg_elements.create_named_sub_element(ElementName::UserDefinedPdu, name)?;
1018        let pdu = Self(pdu_elem);
1019
1020        pdu.set_length(length)?;
1021
1022        Ok(pdu)
1023    }
1024
1025    /// remove this `UserDefinedPdu` from the model
1026    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
1027        // remove all triggerings of this PDU
1028        for pdu_triggering in self.pdu_triggerings() {
1029            let _ = pdu_triggering.element().remove_sub_element_kind(ElementName::IPduRef);
1030            let _ = pdu_triggering.remove(deep);
1031        }
1032
1033        let ref_parents = get_reference_parents(self.element())?;
1034
1035        AbstractionElement::remove(self, deep)?;
1036
1037        for (named_parent, _parent) in ref_parents {
1038            if named_parent.element_name() == ElementName::PduToFrameMapping
1039                && let Ok(pdu_to_frame_mapping) = PduToFrameMapping::try_from(named_parent)
1040            {
1041                pdu_to_frame_mapping.remove(deep)?;
1042            }
1043        }
1044
1045        Ok(())
1046    }
1047}
1048
1049impl AbstractPdu for UserDefinedPdu {}
1050
1051impl From<UserDefinedPdu> for Pdu {
1052    fn from(value: UserDefinedPdu) -> Self {
1053        Pdu::UserDefinedPdu(value)
1054    }
1055}
1056
1057//##################################################################
1058
1059/// Wrapper for all Pdu types. It is used as a return value for functions that can return any Pdu type
1060#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1061pub enum Pdu {
1062    /// The Pdu is an `ISignalIPdu`
1063    ISignalIPdu(ISignalIPdu),
1064    /// The Pdu is a Network Management Pdu
1065    NmPdu(NmPdu),
1066    /// The Pdu is a Transport Layer Pdu
1067    NPdu(NPdu),
1068    /// The Pdu is a Diagnostic Communication Management Pdu
1069    DcmIPdu(DcmIPdu),
1070    /// The Pdu is a General Purpose Pdu
1071    GeneralPurposePdu(GeneralPurposePdu),
1072    /// The Pdu is a General Purpose `IPdu`
1073    GeneralPurposeIPdu(GeneralPurposeIPdu),
1074    /// The Pdu is a Container `IPdu`
1075    ContainerIPdu(ContainerIPdu),
1076    /// The Pdu is a Secured `IPdu`
1077    SecuredIPdu(SecuredIPdu),
1078    /// The Pdu is a Multiplexed `IPdu`
1079    MultiplexedIPdu(MultiplexedIPdu),
1080    /// The Pdu is a User Defined Pdu
1081    UserDefinedPdu(UserDefinedPdu),
1082}
1083
1084impl AbstractionElement for Pdu {
1085    fn element(&self) -> &Element {
1086        match self {
1087            Pdu::ISignalIPdu(pdu) => pdu.element(),
1088            Pdu::NmPdu(pdu) => pdu.element(),
1089            Pdu::NPdu(pdu) => pdu.element(),
1090            Pdu::DcmIPdu(pdu) => pdu.element(),
1091            Pdu::GeneralPurposePdu(pdu) => pdu.element(),
1092            Pdu::GeneralPurposeIPdu(pdu) => pdu.element(),
1093            Pdu::ContainerIPdu(pdu) => pdu.element(),
1094            Pdu::SecuredIPdu(pdu) => pdu.element(),
1095            Pdu::MultiplexedIPdu(pdu) => pdu.element(),
1096            Pdu::UserDefinedPdu(pdu) => pdu.element(),
1097        }
1098    }
1099}
1100
1101impl TryFrom<Element> for Pdu {
1102    type Error = AutosarAbstractionError;
1103
1104    fn try_from(element: Element) -> Result<Self, Self::Error> {
1105        match element.element_name() {
1106            ElementName::ISignalIPdu => Ok(ISignalIPdu::try_from(element)?.into()),
1107            ElementName::NmPdu => Ok(NmPdu::try_from(element)?.into()),
1108            ElementName::NPdu => Ok(NPdu::try_from(element)?.into()),
1109            ElementName::DcmIPdu => Ok(DcmIPdu::try_from(element)?.into()),
1110            ElementName::GeneralPurposePdu => Ok(GeneralPurposePdu::try_from(element)?.into()),
1111            ElementName::GeneralPurposeIPdu => Ok(GeneralPurposeIPdu::try_from(element)?.into()),
1112            ElementName::ContainerIPdu => Ok(ContainerIPdu::try_from(element)?.into()),
1113            ElementName::SecuredIPdu => Ok(SecuredIPdu::try_from(element)?.into()),
1114            ElementName::MultiplexedIPdu => Ok(MultiplexedIPdu::try_from(element)?.into()),
1115            ElementName::UserDefinedPdu => Ok(UserDefinedPdu::try_from(element)?.into()),
1116            _ => Err(AutosarAbstractionError::ConversionError {
1117                element,
1118                dest: "Pdu".to_string(),
1119            }),
1120        }
1121    }
1122}
1123
1124impl IdentifiableAbstractionElement for Pdu {}
1125impl AbstractPdu for Pdu {}
1126
1127impl Pdu {
1128    /// remove this `Pdu` from the model
1129    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
1130        match self {
1131            Pdu::ISignalIPdu(pdu) => pdu.remove(deep),
1132            Pdu::NmPdu(pdu) => pdu.remove(deep),
1133            Pdu::NPdu(pdu) => pdu.remove(deep),
1134            Pdu::DcmIPdu(pdu) => pdu.remove(deep),
1135            Pdu::GeneralPurposePdu(pdu) => pdu.remove(deep),
1136            Pdu::GeneralPurposeIPdu(pdu) => pdu.remove(deep),
1137            Pdu::ContainerIPdu(pdu) => pdu.remove(deep),
1138            Pdu::SecuredIPdu(pdu) => pdu.remove(deep),
1139            Pdu::MultiplexedIPdu(pdu) => pdu.remove(deep),
1140            Pdu::UserDefinedPdu(pdu) => pdu.remove(deep),
1141        }
1142    }
1143}
1144
1145//##################################################################
1146
1147/// Wrapper for all Pdu types. It is used as a return value for functions that can return any Pdu type
1148#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1149pub enum IPdu {
1150    /// The `IPdu` is an `ISignalIPdu`
1151    ISignalIPdu(ISignalIPdu),
1152    /// The Pdu is a Transport Layer Pdu
1153    NPdu(NPdu),
1154    /// The `IPdu` is a Diagnostic Communication Management Pdu
1155    DcmIPdu(DcmIPdu),
1156    /// The `IPdu` is a general purpose Pdu
1157    GeneralPurposeIPdu(GeneralPurposeIPdu),
1158    /// The `IPdu` is a Container `IPdu`
1159    ContainerIPdu(ContainerIPdu),
1160    /// The `IPdu` is a secured `IPdu`
1161    SecuredIPdu(SecuredIPdu),
1162    /// The `IPdu` is a multiplexed `IPdu`
1163    MultiplexedIPdu(MultiplexedIPdu),
1164}
1165
1166impl AbstractionElement for IPdu {
1167    fn element(&self) -> &Element {
1168        match self {
1169            IPdu::ISignalIPdu(pdu) => pdu.element(),
1170            IPdu::NPdu(pdu) => pdu.element(),
1171            IPdu::DcmIPdu(pdu) => pdu.element(),
1172            IPdu::GeneralPurposeIPdu(pdu) => pdu.element(),
1173            IPdu::ContainerIPdu(pdu) => pdu.element(),
1174            IPdu::SecuredIPdu(pdu) => pdu.element(),
1175            IPdu::MultiplexedIPdu(pdu) => pdu.element(),
1176        }
1177    }
1178}
1179
1180impl TryFrom<Element> for IPdu {
1181    type Error = AutosarAbstractionError;
1182
1183    fn try_from(element: Element) -> Result<Self, Self::Error> {
1184        match element.element_name() {
1185            ElementName::ISignalIPdu => Ok(IPdu::ISignalIPdu(ISignalIPdu::try_from(element)?)),
1186            ElementName::NPdu => Ok(IPdu::NPdu(NPdu::try_from(element)?)),
1187            ElementName::DcmIPdu => Ok(IPdu::DcmIPdu(DcmIPdu::try_from(element)?)),
1188            ElementName::GeneralPurposeIPdu => Ok(IPdu::GeneralPurposeIPdu(GeneralPurposeIPdu::try_from(element)?)),
1189            ElementName::ContainerIPdu => Ok(IPdu::ContainerIPdu(ContainerIPdu::try_from(element)?)),
1190            ElementName::SecuredIPdu => Ok(IPdu::SecuredIPdu(SecuredIPdu::try_from(element)?)),
1191            ElementName::MultiplexedIPdu => Ok(IPdu::MultiplexedIPdu(MultiplexedIPdu::try_from(element)?)),
1192            _ => Err(AutosarAbstractionError::ConversionError {
1193                element,
1194                dest: "IPdu".to_string(),
1195            }),
1196        }
1197    }
1198}
1199
1200impl From<IPdu> for Pdu {
1201    fn from(value: IPdu) -> Self {
1202        match value {
1203            IPdu::ISignalIPdu(pdu) => Pdu::ISignalIPdu(pdu),
1204            IPdu::NPdu(pdu) => Pdu::NPdu(pdu),
1205            IPdu::DcmIPdu(pdu) => Pdu::DcmIPdu(pdu),
1206            IPdu::GeneralPurposeIPdu(pdu) => Pdu::GeneralPurposeIPdu(pdu),
1207            IPdu::ContainerIPdu(pdu) => Pdu::ContainerIPdu(pdu),
1208            IPdu::SecuredIPdu(pdu) => Pdu::SecuredIPdu(pdu),
1209            IPdu::MultiplexedIPdu(pdu) => Pdu::MultiplexedIPdu(pdu),
1210        }
1211    }
1212}
1213
1214impl IdentifiableAbstractionElement for IPdu {}
1215impl AbstractPdu for IPdu {}
1216impl AbstractIpdu for IPdu {}
1217
1218impl IPdu {
1219    /// remove this `IPdu` from the model
1220    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
1221        match self {
1222            IPdu::ISignalIPdu(pdu) => pdu.remove(deep),
1223            IPdu::NPdu(pdu) => pdu.remove(deep),
1224            IPdu::DcmIPdu(pdu) => pdu.remove(deep),
1225            IPdu::GeneralPurposeIPdu(pdu) => pdu.remove(deep),
1226            IPdu::ContainerIPdu(pdu) => pdu.remove(deep),
1227            IPdu::SecuredIPdu(pdu) => pdu.remove(deep),
1228            IPdu::MultiplexedIPdu(pdu) => pdu.remove(deep),
1229        }
1230    }
1231}
1232
1233//##################################################################
1234
1235/// a `PduTriggering` triggers a PDU in a frame or ethernet connection
1236#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1237pub struct PduTriggering(Element);
1238abstraction_element!(PduTriggering, PduTriggering);
1239impl IdentifiableAbstractionElement for PduTriggering {}
1240
1241impl PduTriggering {
1242    pub(crate) fn new(pdu: &Pdu, channel: &PhysicalChannel) -> Result<Self, AutosarAbstractionError> {
1243        let model = channel.element().model()?;
1244        let base_path = channel.element().path()?;
1245        let pdu_name = pdu
1246            .name()
1247            .ok_or(AutosarAbstractionError::InvalidParameter("invalid pdu".to_string()))?;
1248        let pt_name = format!("PT_{pdu_name}");
1249        let pt_name = make_unique_name(&model, &base_path, &pt_name);
1250
1251        let triggerings = channel
1252            .element()
1253            .get_or_create_sub_element(ElementName::PduTriggerings)?;
1254        let pt_elem = triggerings.create_named_sub_element(ElementName::PduTriggering, &pt_name)?;
1255        pt_elem
1256            .create_sub_element(ElementName::IPduRef)?
1257            .set_reference_target(pdu.element())?;
1258
1259        let pt = Self(pt_elem);
1260
1261        if let Pdu::ISignalIPdu(isignal_ipdu) = pdu {
1262            for signal_mapping in isignal_ipdu.mapped_signals() {
1263                if let Some(signal) = signal_mapping.signal() {
1264                    pt.create_signal_triggering(&signal)?;
1265                } else if let Some(signal_group) = signal_mapping.signal_group() {
1266                    pt.create_signal_group_triggering(&signal_group)?;
1267                }
1268            }
1269        }
1270
1271        Ok(pt)
1272    }
1273
1274    /// remove this `PduTriggering` from the model
1275    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
1276        let opt_pdu = self.pdu();
1277
1278        for signal_triggering in self.signal_triggerings() {
1279            signal_triggering.remove(deep)?;
1280        }
1281
1282        for pdu_port in self.pdu_ports() {
1283            pdu_port.remove(deep)?;
1284        }
1285
1286        // removal of PayloadRefs from SecuredIPdus is handled by AbstractionElement::remove
1287        // removal of ContainedPduTriggeringRefs from ContainerIPdus is handled by AbstractionElement::remove
1288
1289        let ref_parents = get_reference_parents(self.element())?;
1290
1291        AbstractionElement::remove(self, deep)?;
1292
1293        for (named_parent, parent) in ref_parents {
1294            if named_parent.element_name() == ElementName::SoConIPduIdentifier
1295                && let Ok(socon_ipdu_identifier) = SoConIPduIdentifier::try_from(named_parent)
1296            {
1297                socon_ipdu_identifier.remove(deep)?;
1298            } else if parent.element_name() == ElementName::SomeipTpConnection {
1299                if let Ok(someip_tp_connection) = SomeipTpConnection::try_from(parent) {
1300                    someip_tp_connection.remove(deep)?;
1301                }
1302            } else if parent.element_name() == ElementName::PduTriggeringRefConditional
1303                && let Ok(Some(parent_parent)) = parent.parent()
1304            {
1305                parent_parent.remove_sub_element(parent)?;
1306            }
1307        }
1308
1309        if deep && let Some(pdu) = opt_pdu {
1310            // check if the PDU became unused; if so remove it too
1311            if !is_used_system_element(pdu.element()) {
1312                pdu.remove(deep)?;
1313            }
1314        }
1315
1316        Ok(())
1317    }
1318
1319    /// get the Pdu that is triggered by this pdu triggering
1320    #[must_use]
1321    pub fn pdu(&self) -> Option<Pdu> {
1322        let pdu_elem = self
1323            .element()
1324            .get_sub_element(ElementName::IPduRef)?
1325            .get_reference_target()
1326            .ok()?;
1327        Pdu::try_from(pdu_elem).ok()
1328    }
1329
1330    /// get the physical channel that contains this pdu triggering
1331    pub fn physical_channel(&self) -> Result<PhysicalChannel, AutosarAbstractionError> {
1332        let channel_elem = self.element().named_parent()?.ok_or(AutosarDataError::ItemDeleted)?;
1333        PhysicalChannel::try_from(channel_elem)
1334    }
1335
1336    /// create an `IPduPort` to connect a `PduTriggering` to an `EcuInstance`
1337    pub fn create_pdu_port(
1338        &self,
1339        ecu: &EcuInstance,
1340        direction: CommunicationDirection,
1341    ) -> Result<IPduPort, AutosarAbstractionError> {
1342        for pdu_port in self.pdu_ports() {
1343            if let (Ok(existing_ecu), Some(existing_direction)) = (pdu_port.ecu(), pdu_port.communication_direction())
1344                && existing_ecu == *ecu
1345                && existing_direction == direction
1346            {
1347                return Ok(pdu_port);
1348            }
1349        }
1350
1351        let channel = self.physical_channel()?;
1352        let connector = channel
1353            .ecu_connector(ecu)
1354            .ok_or(AutosarAbstractionError::InvalidParameter(
1355                "The ECU is not connected to the channel".to_string(),
1356            ))?;
1357
1358        let name = self.name().ok_or(AutosarDataError::ItemDeleted)?;
1359        let suffix = match direction {
1360            CommunicationDirection::In => "Rx",
1361            CommunicationDirection::Out => "Tx",
1362        };
1363        let port_name = format!("{name}_{suffix}",);
1364        let pp_elem = connector
1365            .element()
1366            .get_or_create_sub_element(ElementName::EcuCommPortInstances)?
1367            .create_named_sub_element(ElementName::IPduPort, &port_name)?;
1368        pp_elem
1369            .create_sub_element(ElementName::CommunicationDirection)?
1370            .set_character_data::<EnumItem>(direction.into())?;
1371
1372        self.element()
1373            .get_or_create_sub_element(ElementName::IPduPortRefs)?
1374            .create_sub_element(ElementName::IPduPortRef)?
1375            .set_reference_target(&pp_elem)?;
1376
1377        for st in self.signal_triggerings() {
1378            st.connect_to_ecu(ecu, direction)?;
1379        }
1380
1381        Ok(IPduPort(pp_elem))
1382    }
1383
1384    /// create an iterator over the `IPduPorts` that are connected to this `PduTriggering`
1385    pub fn pdu_ports(&self) -> impl Iterator<Item = IPduPort> + Send + use<> {
1386        self.element()
1387            .get_sub_element(ElementName::IPduPortRefs)
1388            .into_iter()
1389            .flat_map(|ipprefs| ipprefs.sub_elements())
1390            .filter_map(|ippref| {
1391                ippref
1392                    .get_reference_target()
1393                    .ok()
1394                    .and_then(|elem| IPduPort::try_from(elem).ok())
1395            })
1396    }
1397
1398    /// create an iterator over the `ISignalTriggerings` that are triggered by this `PduTriggering`
1399    pub fn signal_triggerings(&self) -> impl Iterator<Item = ISignalTriggering> + Send + use<> {
1400        self.element()
1401            .get_sub_element(ElementName::ISignalTriggerings)
1402            .into_iter()
1403            .flat_map(|ists| ists.sub_elements())
1404            .filter_map(|ist| {
1405                ist.get_sub_element(ElementName::ISignalTriggeringRef)
1406                    .and_then(|str| str.get_reference_target().ok())
1407                    .and_then(|elem| ISignalTriggering::try_from(elem).ok())
1408            })
1409    }
1410
1411    /// create a signal triggering for a signal and connect it to this `PduTriggering`
1412    pub(crate) fn create_signal_triggering(
1413        &self,
1414        signal: &ISignal,
1415    ) -> Result<ISignalTriggering, AutosarAbstractionError> {
1416        let channel = self.physical_channel()?;
1417        let st = ISignalTriggering::new(signal, &channel)?;
1418        let triggerings = self
1419            .element()
1420            .get_or_create_sub_element(ElementName::ISignalTriggerings)?;
1421        triggerings
1422            .create_sub_element(ElementName::ISignalTriggeringRefConditional)?
1423            .create_sub_element(ElementName::ISignalTriggeringRef)?
1424            .set_reference_target(st.element())?;
1425
1426        for pdu_port in self.pdu_ports() {
1427            if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
1428                st.connect_to_ecu(&ecu, direction)?;
1429            }
1430        }
1431
1432        Ok(st)
1433    }
1434
1435    /// create a signal triggering for a signal group and connect it to this `PduTriggering`
1436    pub(crate) fn create_signal_group_triggering(
1437        &self,
1438        signal_group: &ISignalGroup,
1439    ) -> Result<ISignalTriggering, AutosarAbstractionError> {
1440        let channel = self.physical_channel()?;
1441        let st = ISignalTriggering::new_group(signal_group, &channel)?;
1442        let triggerings = self
1443            .element()
1444            .get_or_create_sub_element(ElementName::ISignalTriggerings)?;
1445        triggerings
1446            .create_sub_element(ElementName::ISignalTriggeringRefConditional)?
1447            .create_sub_element(ElementName::ISignalTriggeringRef)?
1448            .set_reference_target(st.element())?;
1449
1450        for pdu_port in self.pdu_ports() {
1451            if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
1452                st.connect_to_ecu(&ecu, direction)?;
1453            }
1454        }
1455
1456        Ok(st)
1457    }
1458}
1459
1460//##################################################################
1461
1462/// The `IPduPort` allows an ECU to send or receive a PDU
1463#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1464pub struct IPduPort(Element);
1465abstraction_element!(IPduPort, IPduPort);
1466impl IdentifiableAbstractionElement for IPduPort {}
1467
1468impl IPduPort {
1469    /// get the ECU instance that contains this `IPduPort`
1470    pub fn ecu(&self) -> Result<EcuInstance, AutosarAbstractionError> {
1471        let comm_connector_elem = self.element().named_parent()?.unwrap();
1472        let ecu_elem = comm_connector_elem.named_parent()?.unwrap();
1473        EcuInstance::try_from(ecu_elem)
1474    }
1475
1476    /// set the communication direction of this `IPduPort`
1477    pub fn set_communication_direction(
1478        &self,
1479        direction: CommunicationDirection,
1480    ) -> Result<(), AutosarAbstractionError> {
1481        self.element()
1482            .get_or_create_sub_element(ElementName::CommunicationDirection)?
1483            .set_character_data::<EnumItem>(direction.into())?;
1484        Ok(())
1485    }
1486
1487    /// get the communication direction of this `IPduPort`
1488    #[must_use]
1489    pub fn communication_direction(&self) -> Option<CommunicationDirection> {
1490        self.element()
1491            .get_sub_element(ElementName::CommunicationDirection)?
1492            .character_data()?
1493            .enum_value()?
1494            .try_into()
1495            .ok()
1496    }
1497}
1498
1499//##################################################################
1500
1501/// The collction trigger defines whether a Pdu contributes to the triggering
1502/// of the data transmission if Pdu collection is enabled
1503#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1504pub enum PduCollectionTrigger {
1505    /// Pdu will trigger the transmission of the data.
1506    Always,
1507    /// Pdu will be buffered and will not trigger the transmission of the data
1508    Never,
1509}
1510
1511impl From<PduCollectionTrigger> for EnumItem {
1512    fn from(value: PduCollectionTrigger) -> Self {
1513        match value {
1514            PduCollectionTrigger::Always => EnumItem::Always,
1515            PduCollectionTrigger::Never => EnumItem::Never,
1516        }
1517    }
1518}
1519
1520impl TryFrom<EnumItem> for PduCollectionTrigger {
1521    type Error = AutosarAbstractionError;
1522
1523    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
1524        match value {
1525            EnumItem::Always => Ok(PduCollectionTrigger::Always),
1526            EnumItem::Never => Ok(PduCollectionTrigger::Never),
1527            _ => Err(AutosarAbstractionError::ValueConversionError {
1528                value: value.to_string(),
1529                dest: "PduCollectionTrigger".to_string(),
1530            }),
1531        }
1532    }
1533}
1534
1535//##################################################################
1536
1537#[cfg(test)]
1538mod test {
1539    use super::*;
1540    use crate::{
1541        AutosarModelAbstraction, ByteOrder, SystemCategory,
1542        communication::{AbstractFrame, AbstractFrameTriggering, CanAddressingMode, CanFrameType, TransferProperty},
1543    };
1544    use autosar_data::AutosarVersion;
1545
1546    #[test]
1547    fn test_pdus() {
1548        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1549        let package = model.get_or_create_package("/pkg").unwrap();
1550        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1551
1552        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
1553        let nm_pdu = system.create_nm_pdu("nm_pdu", &package, 1).unwrap();
1554        let n_pdu = system.create_n_pdu("n_pdu", &package, 1).unwrap();
1555        let dcm_ipdu = system
1556            .create_dcm_ipdu("dcm_ipdu", &package, 1, DiagPduType::DiagRequest)
1557            .unwrap();
1558        let gp_pdu = system
1559            .create_general_purpose_pdu("gp_pdu", &package, 1, GeneralPurposePduCategory::Sd)
1560            .unwrap();
1561        let gp_ipdu = system
1562            .create_general_purpose_ipdu("gp_ipdu", &package, 1, GeneralPurposeIPduCategory::Xcp)
1563            .unwrap();
1564        let container_ipdu = system
1565            .create_container_ipdu(
1566                "container_ipdu",
1567                &package,
1568                1,
1569                ContainerIPduHeaderType::ShortHeader,
1570                RxAcceptContainedIPdu::AcceptAll,
1571            )
1572            .unwrap();
1573        let secured_ipdu = system
1574            .create_secured_ipdu("secured_ipdu", &package, 1, &SecureCommunicationProps::default())
1575            .unwrap();
1576        let multiplexed_ipdu = system.create_multiplexed_ipdu("multiplexed_ipdu", &package, 1).unwrap();
1577
1578        assert_eq!(isignal_ipdu.length().unwrap(), 1);
1579        assert_eq!(nm_pdu.length().unwrap(), 1);
1580        assert_eq!(n_pdu.length().unwrap(), 1);
1581        assert_eq!(dcm_ipdu.length().unwrap(), 1);
1582        assert_eq!(gp_pdu.length().unwrap(), 1);
1583        assert_eq!(gp_ipdu.length().unwrap(), 1);
1584        assert_eq!(container_ipdu.length().unwrap(), 1);
1585        assert_eq!(secured_ipdu.length().unwrap(), 1);
1586        assert_eq!(multiplexed_ipdu.length().unwrap(), 1);
1587
1588        isignal_ipdu.set_length(2).unwrap();
1589        assert_eq!(isignal_ipdu.length().unwrap(), 2);
1590
1591        let frame = system.create_flexray_frame("frame1", &package, 64).unwrap();
1592        frame
1593            .map_pdu(&isignal_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
1594            .unwrap();
1595        frame
1596            .map_pdu(&nm_pdu, 8, ByteOrder::MostSignificantByteLast, None)
1597            .unwrap();
1598        frame
1599            .map_pdu(&n_pdu, 16, ByteOrder::MostSignificantByteLast, None)
1600            .unwrap();
1601        frame
1602            .map_pdu(&dcm_ipdu, 24, ByteOrder::MostSignificantByteLast, None)
1603            .unwrap();
1604        frame
1605            .map_pdu(&gp_pdu, 32, ByteOrder::MostSignificantByteLast, None)
1606            .unwrap();
1607        frame
1608            .map_pdu(&gp_ipdu, 40, ByteOrder::MostSignificantByteLast, None)
1609            .unwrap();
1610        frame
1611            .map_pdu(&container_ipdu, 48, ByteOrder::MostSignificantByteLast, None)
1612            .unwrap();
1613        frame
1614            .map_pdu(&secured_ipdu, 56, ByteOrder::MostSignificantByteLast, None)
1615            .unwrap();
1616        frame
1617            .map_pdu(&multiplexed_ipdu, 64, ByteOrder::MostSignificantByteLast, None)
1618            .unwrap();
1619
1620        let mut pdus_iter = frame.mapped_pdus();
1621        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "isignal_ipdu");
1622        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "nm_pdu");
1623        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "n_pdu");
1624        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "dcm_ipdu");
1625        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "gp_pdu");
1626        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "gp_ipdu");
1627        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "container_ipdu");
1628        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "secured_ipdu");
1629        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "multiplexed_ipdu");
1630        assert!(pdus_iter.next().is_none());
1631    }
1632
1633    #[test]
1634    fn test_pdu_triggering() {
1635        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1636        let package = model.get_or_create_package("/pkg").unwrap();
1637        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1638
1639        // create an ISignalIPdu with a signal
1640        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
1641        let syssignal = package.create_system_signal("syssignal").unwrap();
1642        let isignal = system.create_isignal("isignal", &package, 1, &syssignal, None).unwrap();
1643        isignal_ipdu
1644            .map_signal(
1645                &isignal,
1646                0,
1647                ByteOrder::MostSignificantByteLast,
1648                None,
1649                TransferProperty::Triggered,
1650            )
1651            .unwrap();
1652        // create an ISignalGroup with a second signal
1653        let syssignal_group = package.create_system_signal_group("syssignal_group").unwrap();
1654        let isignal_group = system
1655            .create_isignal_group("isignal_group", &package, &syssignal_group)
1656            .unwrap();
1657        let syssignal2 = package.create_system_signal("syssignal2").unwrap();
1658        let isignal2 = system
1659            .create_isignal("isignal2", &package, 1, &syssignal2, None)
1660            .unwrap();
1661        isignal_ipdu.map_signal_group(&isignal_group).unwrap();
1662        isignal_ipdu
1663            .map_signal(
1664                &isignal2,
1665                1,
1666                ByteOrder::MostSignificantByteLast,
1667                None,
1668                TransferProperty::Triggered,
1669            )
1670            .unwrap();
1671
1672        // create a frame and map the ISignalIPdu to it
1673        let can_cluster = system.create_can_cluster("Cluster", &package, None).unwrap();
1674        let channel = can_cluster.create_physical_channel("Channel").unwrap();
1675        let frame = system.create_can_frame("frame", &package, 8).unwrap();
1676        let frame_triggering = channel
1677            .trigger_frame(&frame, 0x123, CanAddressingMode::Standard, CanFrameType::Can20)
1678            .unwrap();
1679        let _mapping = frame
1680            .map_pdu(&isignal_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
1681            .unwrap();
1682
1683        // create an EcuInstance, and connect it to the channel. The frame is reeived by the ECU
1684        let ecu = system.create_ecu_instance("ecu", &package).unwrap();
1685        let controller = ecu.create_can_communication_controller("controller").unwrap();
1686        controller.connect_physical_channel("connection", &channel).unwrap();
1687        frame_triggering
1688            .connect_to_ecu(&ecu, CommunicationDirection::In)
1689            .unwrap();
1690
1691        let pdu_triggering = frame_triggering.pdu_triggerings().next().unwrap();
1692        assert_eq!(pdu_triggering.pdu_ports().count(), 1);
1693        assert_eq!(pdu_triggering.signal_triggerings().count(), 3); // one for each signal, and another for the signal group
1694
1695        let pdu_port = pdu_triggering.pdu_ports().next().unwrap();
1696        assert_eq!(pdu_port.ecu().unwrap().name().unwrap(), "ecu");
1697        assert_eq!(pdu_port.communication_direction().unwrap(), CommunicationDirection::In);
1698        pdu_port
1699            .set_communication_direction(CommunicationDirection::Out)
1700            .unwrap();
1701        assert_eq!(pdu_port.communication_direction().unwrap(), CommunicationDirection::Out);
1702        pdu_port.set_name("new_name").unwrap();
1703        assert_eq!(pdu_port.name().unwrap(), "new_name");
1704    }
1705
1706    #[test]
1707    fn nm_pdu() {
1708        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00052);
1709        let package = model.get_or_create_package("/pkg").unwrap();
1710        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1711
1712        let nm_pdu = system.create_nm_pdu("nm_pdu", &package, 1).unwrap();
1713        assert_eq!(nm_pdu.length().unwrap(), 1);
1714
1715        nm_pdu.set_length(8).unwrap();
1716        assert_eq!(nm_pdu.length().unwrap(), 8);
1717
1718        nm_pdu.set_unused_bit_pattern(0xff).unwrap();
1719        assert_eq!(nm_pdu.unused_bit_pattern().unwrap(), 0xff);
1720
1721        // create a signal and map it to the PDU
1722        let syssignal = package.create_system_signal("sys_userdata").unwrap();
1723        let isignal = system
1724            .create_isignal("userdata", &package, 16, &syssignal, None)
1725            .unwrap();
1726        let mapping = nm_pdu
1727            .map_signal(
1728                &isignal,
1729                0,
1730                ByteOrder::MostSignificantByteFirst,
1731                Some(16),
1732                TransferProperty::Triggered,
1733            )
1734            .unwrap();
1735        assert_eq!(mapping.signal().unwrap(), isignal);
1736    }
1737
1738    #[test]
1739    fn general_purpose_pdu() {
1740        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1741        let package = model.get_or_create_package("/pkg").unwrap();
1742        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1743
1744        let gp_pdu1 = system
1745            .create_general_purpose_pdu("gp_pdu1", &package, 1, GeneralPurposePduCategory::Sd)
1746            .unwrap();
1747        assert_eq!(gp_pdu1.category().unwrap(), GeneralPurposePduCategory::Sd);
1748
1749        let gp_pdu2 = system
1750            .create_general_purpose_pdu("gp_pdu2", &package, 1, GeneralPurposePduCategory::GlobalTime)
1751            .unwrap();
1752        assert_eq!(gp_pdu2.category().unwrap(), GeneralPurposePduCategory::GlobalTime);
1753
1754        let gp_pdu3 = system
1755            .create_general_purpose_pdu("gp_pdu3", &package, 1, GeneralPurposePduCategory::DoIp)
1756            .unwrap();
1757        assert_eq!(gp_pdu3.category().unwrap(), GeneralPurposePduCategory::DoIp);
1758
1759        // conversion of category to string and back
1760        assert_eq!(
1761            GeneralPurposePduCategory::from_str("SD").unwrap(),
1762            GeneralPurposePduCategory::Sd
1763        );
1764        assert_eq!(
1765            GeneralPurposePduCategory::from_str("GLOBAL_TIME").unwrap(),
1766            GeneralPurposePduCategory::GlobalTime
1767        );
1768        assert_eq!(
1769            GeneralPurposePduCategory::from_str("DOIP").unwrap(),
1770            GeneralPurposePduCategory::DoIp
1771        );
1772        assert!(GeneralPurposePduCategory::from_str("invalid").is_err());
1773    }
1774
1775    #[test]
1776    fn general_purpose_ipdu() {
1777        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1778        let package = model.get_or_create_package("/pkg").unwrap();
1779        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1780
1781        let gp_ipdu1 = system
1782            .create_general_purpose_ipdu("gp_ipdu1", &package, 1, GeneralPurposeIPduCategory::Xcp)
1783            .unwrap();
1784        assert_eq!(gp_ipdu1.category().unwrap(), GeneralPurposeIPduCategory::Xcp);
1785
1786        let gp_ipdu2 = system
1787            .create_general_purpose_ipdu("gp_ipdu2", &package, 1, GeneralPurposeIPduCategory::SomeipSegmentedIpdu)
1788            .unwrap();
1789        assert_eq!(
1790            gp_ipdu2.category().unwrap(),
1791            GeneralPurposeIPduCategory::SomeipSegmentedIpdu
1792        );
1793
1794        let gp_ipdu3 = system
1795            .create_general_purpose_ipdu("gp_ipdu3", &package, 1, GeneralPurposeIPduCategory::Dlt)
1796            .unwrap();
1797        assert_eq!(gp_ipdu3.category().unwrap(), GeneralPurposeIPduCategory::Dlt);
1798
1799        // conversion of category to string and back
1800        assert_eq!(
1801            GeneralPurposeIPduCategory::from_str("XCP").unwrap(),
1802            GeneralPurposeIPduCategory::Xcp
1803        );
1804        assert_eq!(
1805            GeneralPurposeIPduCategory::from_str("SOMEIP_SEGMENTED_IPDU").unwrap(),
1806            GeneralPurposeIPduCategory::SomeipSegmentedIpdu
1807        );
1808        assert_eq!(
1809            GeneralPurposeIPduCategory::from_str("DLT").unwrap(),
1810            GeneralPurposeIPduCategory::Dlt
1811        );
1812        assert!(GeneralPurposeIPduCategory::from_str("invalid").is_err());
1813    }
1814
1815    #[test]
1816    fn multiplexed_ipdu() {
1817        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1818        let package = model.get_or_create_package("/pkg").unwrap();
1819        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1820
1821        let multiplexed_ipdu = system.create_multiplexed_ipdu("multiplexed_ipdu", &package, 1).unwrap();
1822        assert_eq!(multiplexed_ipdu.length().unwrap(), 1);
1823
1824        multiplexed_ipdu.set_length(16).unwrap();
1825        assert_eq!(multiplexed_ipdu.length().unwrap(), 16);
1826
1827        let static_ipdu = system.create_isignal_ipdu("static_ipdu", &package, 8).unwrap();
1828        multiplexed_ipdu.set_static_part(&static_ipdu).unwrap();
1829        assert_eq!(multiplexed_ipdu.static_part().unwrap(), static_ipdu);
1830
1831        let dynamic_ipdu = system.create_isignal_ipdu("dynamic_ipdu", &package, 8).unwrap();
1832        let dp_alt = multiplexed_ipdu.add_dynamic_part(&dynamic_ipdu, 0, true).unwrap();
1833        assert_eq!(dp_alt.ipdu().unwrap(), dynamic_ipdu);
1834        assert_eq!(dp_alt.selector_field_code().unwrap(), 0);
1835        assert_eq!(dp_alt.is_initial_dynamic_part().unwrap(), true);
1836        assert_eq!(dp_alt.multiplexed_ipdu().unwrap(), multiplexed_ipdu);
1837        assert_eq!(multiplexed_ipdu.dynamic_part_alternatives().count(), 1);
1838
1839        multiplexed_ipdu
1840            .set_selector_field(8, 16, ByteOrder::MostSignificantByteFirst)
1841            .unwrap();
1842        let (start_bit, length, byte_order) = multiplexed_ipdu.selector_field().unwrap();
1843        assert_eq!(start_bit, 8);
1844        assert_eq!(length, 16);
1845        assert_eq!(byte_order, ByteOrder::MostSignificantByteFirst);
1846
1847        let cluster = system.create_can_cluster("cluster", &package, None).unwrap();
1848        let channel = cluster.create_physical_channel("channel").unwrap();
1849        let frame = system.create_can_frame("frame", &package, 64).unwrap();
1850        channel
1851            .trigger_frame(&frame, 0x33, CanAddressingMode::Standard, CanFrameType::CanFd)
1852            .unwrap();
1853        let _mapping = frame
1854            .map_pdu(&multiplexed_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
1855            .unwrap();
1856
1857        assert_eq!(channel.frame_triggerings().count(), 1);
1858        assert_eq!(channel.pdu_triggerings().count(), 3); // multiplex pdu + static part + dynamic part
1859    }
1860
1861    #[test]
1862    fn ipdu() {
1863        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1864        let package = model.get_or_create_package("/pkg").unwrap();
1865        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1866
1867        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
1868        let n_pdu = system.create_n_pdu("n_pdu", &package, 1).unwrap();
1869        let dcm_ipdu = system
1870            .create_dcm_ipdu("dcm_ipdu", &package, 1, DiagPduType::DiagResponse)
1871            .unwrap();
1872        let gp_ipdu = system
1873            .create_general_purpose_ipdu("gp_ipdu", &package, 1, GeneralPurposeIPduCategory::Xcp)
1874            .unwrap();
1875        let container_ipdu = system
1876            .create_container_ipdu(
1877                "container_ipdu",
1878                &package,
1879                1,
1880                ContainerIPduHeaderType::LongHeader,
1881                RxAcceptContainedIPdu::AcceptConfigured,
1882            )
1883            .unwrap();
1884        let secured_ipdu = system
1885            .create_secured_ipdu("secured_ipdu", &package, 1, &SecureCommunicationProps::default())
1886            .unwrap();
1887        let multiplexed_ipdu = system.create_multiplexed_ipdu("multiplexed_ipdu", &package, 1).unwrap();
1888
1889        let ipdu: IPdu = isignal_ipdu.clone().into();
1890        assert_eq!(ipdu.element(), isignal_ipdu.element());
1891        assert!(matches!(ipdu, IPdu::ISignalIPdu(_)));
1892
1893        // NmPdu is not an ipdu
1894
1895        let ipdu: IPdu = n_pdu.clone().into();
1896        assert_eq!(ipdu.element(), n_pdu.element());
1897        assert!(matches!(ipdu, IPdu::NPdu(_)));
1898
1899        let ipdu: IPdu = dcm_ipdu.clone().into();
1900        assert_eq!(ipdu.element(), dcm_ipdu.element());
1901        assert!(matches!(ipdu, IPdu::DcmIPdu(_)));
1902
1903        let ipdu: IPdu = n_pdu.clone().into();
1904        assert_eq!(ipdu.element(), n_pdu.element());
1905        assert!(matches!(ipdu, IPdu::NPdu(_)));
1906
1907        // GeneralPurposePdu is not an ipdu
1908
1909        let ipdu: IPdu = gp_ipdu.clone().into();
1910        assert_eq!(ipdu.element(), gp_ipdu.element());
1911        assert!(matches!(ipdu, IPdu::GeneralPurposeIPdu(_)));
1912
1913        let ipdu: IPdu = container_ipdu.clone().into();
1914        assert_eq!(ipdu.element(), container_ipdu.element());
1915        assert!(matches!(ipdu, IPdu::ContainerIPdu(_)));
1916
1917        let ipdu: IPdu = secured_ipdu.clone().into();
1918        assert_eq!(ipdu.element(), secured_ipdu.element());
1919        assert!(matches!(ipdu, IPdu::SecuredIPdu(_)));
1920
1921        let ipdu: IPdu = multiplexed_ipdu.clone().into();
1922        assert_eq!(ipdu.element(), multiplexed_ipdu.element());
1923        assert!(matches!(ipdu, IPdu::MultiplexedIPdu(_)));
1924
1925        // any Ipdu can be converted to a Pdu
1926        let pdu: Pdu = ipdu.clone().into();
1927        assert_eq!(pdu.element(), ipdu.element());
1928    }
1929
1930    #[test]
1931    fn remove() {
1932        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1933        let package = model.get_or_create_package("/pkg").unwrap();
1934        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1935
1936        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
1937        let n_pdu = system.create_n_pdu("n_pdu", &package, 1).unwrap();
1938        let dcm_ipdu = system
1939            .create_dcm_ipdu("dcm_ipdu", &package, 1, DiagPduType::DiagResponse)
1940            .unwrap();
1941        let gp_ipdu = system
1942            .create_general_purpose_ipdu("gp_ipdu", &package, 1, GeneralPurposeIPduCategory::Xcp)
1943            .unwrap();
1944        let container_ipdu = system
1945            .create_container_ipdu(
1946                "container_ipdu",
1947                &package,
1948                1,
1949                ContainerIPduHeaderType::LongHeader,
1950                RxAcceptContainedIPdu::AcceptConfigured,
1951            )
1952            .unwrap();
1953        let secured_ipdu = system
1954            .create_secured_ipdu("secured_ipdu", &package, 1, &SecureCommunicationProps::default())
1955            .unwrap();
1956        let multiplexed_ipdu = system.create_multiplexed_ipdu("multiplexed_ipdu", &package, 1).unwrap();
1957
1958        assert!(!is_used_system_element(isignal_ipdu.element()));
1959        assert!(!is_used_system_element(n_pdu.element()));
1960        assert!(!is_used_system_element(dcm_ipdu.element()));
1961        assert!(!is_used_system_element(gp_ipdu.element()));
1962        assert!(!is_used_system_element(container_ipdu.element()));
1963        assert!(!is_used_system_element(secured_ipdu.element()));
1964        assert!(!is_used_system_element(multiplexed_ipdu.element()));
1965
1966        let can_cluster = system.create_can_cluster("cluster", &package, None).unwrap();
1967        let channel = can_cluster.create_physical_channel("channel").unwrap();
1968        let frame1 = system.create_can_frame("frame", &package, 64).unwrap();
1969        channel
1970            .trigger_frame(&frame1, 0x123, CanAddressingMode::Standard, CanFrameType::Can20)
1971            .unwrap();
1972        let _mapping = frame1
1973            .map_pdu(&isignal_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
1974            .unwrap();
1975        let frame2 = system.create_can_frame("frame2", &package, 64).unwrap();
1976        channel
1977            .trigger_frame(&frame2, 0x124, CanAddressingMode::Standard, CanFrameType::Can20)
1978            .unwrap();
1979        let _mapping = frame2
1980            .map_pdu(&n_pdu, 0, ByteOrder::MostSignificantByteLast, None)
1981            .unwrap();
1982        let frame3 = system.create_can_frame("frame3", &package, 64).unwrap();
1983        channel
1984            .trigger_frame(&frame3, 0x125, CanAddressingMode::Standard, CanFrameType::Can20)
1985            .unwrap();
1986        let _mapping = frame3
1987            .map_pdu(&dcm_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
1988            .unwrap();
1989        let frame4 = system.create_can_frame("frame4", &package, 64).unwrap();
1990        channel
1991            .trigger_frame(&frame4, 0x126, CanAddressingMode::Standard, CanFrameType::Can20)
1992            .unwrap();
1993        let _mapping = frame4
1994            .map_pdu(&gp_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
1995            .unwrap();
1996        let frame5 = system.create_can_frame("frame5", &package, 64).unwrap();
1997        channel
1998            .trigger_frame(&frame5, 0x127, CanAddressingMode::Standard, CanFrameType::Can20)
1999            .unwrap();
2000        let _mapping = frame5
2001            .map_pdu(&container_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
2002            .unwrap();
2003        let frame6 = system.create_can_frame("frame6", &package, 64).unwrap();
2004        channel
2005            .trigger_frame(&frame6, 0x128, CanAddressingMode::Standard, CanFrameType::Can20)
2006            .unwrap();
2007        let _mapping = frame6
2008            .map_pdu(&secured_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
2009            .unwrap();
2010        let frame7 = system.create_can_frame("frame7", &package, 64).unwrap();
2011        channel
2012            .trigger_frame(&frame7, 0x129, CanAddressingMode::Standard, CanFrameType::Can20)
2013            .unwrap();
2014        let _mapping = frame7
2015            .map_pdu(&multiplexed_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
2016            .unwrap();
2017
2018        assert_eq!(channel.pdu_triggerings().count(), 7);
2019
2020        // add static and dynamic parts to the multiplexed ipdu
2021        let static_part_pdu = system.create_isignal_ipdu("static_part_pdu", &package, 8).unwrap();
2022        multiplexed_ipdu.set_static_part(&static_part_pdu).unwrap();
2023        let dynamic_part_pdu = system.create_isignal_ipdu("dynamic_part_pdu", &package, 8).unwrap();
2024        multiplexed_ipdu.add_dynamic_part(&dynamic_part_pdu, 0, true).unwrap();
2025
2026        assert_eq!(channel.pdu_triggerings().count(), 9); // static + dynamic part added
2027
2028        // add a contained IPdu to the container ipdu
2029        let contained_ipdu = system.create_isignal_ipdu("contained_ipdu", &package, 8).unwrap();
2030        container_ipdu.map_ipdu(&contained_ipdu, &channel).unwrap();
2031
2032        // add a payload pdu to the secured ipdu
2033        let payload_ipdu = system.create_isignal_ipdu("payload_ipdu", &package, 8).unwrap();
2034        secured_ipdu.set_payload_ipdu(&payload_ipdu, &channel).unwrap();
2035
2036        assert_eq!(channel.pdu_triggerings().count(), 11); // contained + payload added
2037
2038        // remove all pdus deeply
2039        isignal_ipdu.remove(true).unwrap();
2040        n_pdu.remove(true).unwrap();
2041        dcm_ipdu.remove(true).unwrap();
2042        gp_ipdu.remove(true).unwrap();
2043        container_ipdu.remove(true).unwrap();
2044        secured_ipdu.remove(true).unwrap();
2045        multiplexed_ipdu.remove(true).unwrap();
2046
2047        // all PDU triggerings, including for contained and payload pdus, should be removed
2048        assert_eq!(channel.pdu_triggerings().count(), 0);
2049    }
2050}