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
570impl AbstractPdu for MultiplexedIPdu {}
571
572impl AbstractIpdu for MultiplexedIPdu {}
573
574impl From<MultiplexedIPdu> for Pdu {
575    fn from(value: MultiplexedIPdu) -> Self {
576        Pdu::MultiplexedIPdu(value)
577    }
578}
579
580impl From<MultiplexedIPdu> for IPdu {
581    fn from(value: MultiplexedIPdu) -> Self {
582        IPdu::MultiplexedIPdu(value)
583    }
584}
585
586//##################################################################
587
588/// Wrapper for all Pdu types. It is used as a return value for functions that can return any Pdu type
589#[derive(Debug, Clone, PartialEq, Eq, Hash)]
590pub enum Pdu {
591    /// The Pdu is an `ISignalIPdu`
592    ISignalIPdu(ISignalIPdu),
593    /// The Pdu is a Network Management Pdu
594    NmPdu(NmPdu),
595    /// The Pdu is a Transport Layer Pdu
596    NPdu(NPdu),
597    /// The Pdu is a Diagnostic Communication Management Pdu
598    DcmIPdu(DcmIPdu),
599    /// The Pdu is a General Purpose Pdu
600    GeneralPurposePdu(GeneralPurposePdu),
601    /// The Pdu is a General Purpose `IPdu`
602    GeneralPurposeIPdu(GeneralPurposeIPdu),
603    /// The Pdu is a Container `IPdu`
604    ContainerIPdu(ContainerIPdu),
605    /// The Pdu is a Secured `IPdu`
606    SecuredIPdu(SecuredIPdu),
607    /// The Pdu is a Multiplexed `IPdu`
608    MultiplexedIPdu(MultiplexedIPdu),
609}
610
611impl AbstractionElement for Pdu {
612    fn element(&self) -> &Element {
613        match self {
614            Pdu::ISignalIPdu(pdu) => pdu.element(),
615            Pdu::NmPdu(pdu) => pdu.element(),
616            Pdu::NPdu(pdu) => pdu.element(),
617            Pdu::DcmIPdu(pdu) => pdu.element(),
618            Pdu::GeneralPurposePdu(pdu) => pdu.element(),
619            Pdu::GeneralPurposeIPdu(pdu) => pdu.element(),
620            Pdu::ContainerIPdu(pdu) => pdu.element(),
621            Pdu::SecuredIPdu(pdu) => pdu.element(),
622            Pdu::MultiplexedIPdu(pdu) => pdu.element(),
623        }
624    }
625}
626
627impl TryFrom<Element> for Pdu {
628    type Error = AutosarAbstractionError;
629
630    fn try_from(element: Element) -> Result<Self, Self::Error> {
631        match element.element_name() {
632            ElementName::ISignalIPdu => Ok(ISignalIPdu::try_from(element)?.into()),
633            ElementName::NmPdu => Ok(NmPdu::try_from(element)?.into()),
634            ElementName::NPdu => Ok(NPdu::try_from(element)?.into()),
635            ElementName::DcmIPdu => Ok(DcmIPdu::try_from(element)?.into()),
636            ElementName::GeneralPurposePdu => Ok(GeneralPurposePdu::try_from(element)?.into()),
637            ElementName::GeneralPurposeIPdu => Ok(GeneralPurposeIPdu::try_from(element)?.into()),
638            ElementName::ContainerIPdu => Ok(ContainerIPdu::try_from(element)?.into()),
639            ElementName::SecuredIPdu => Ok(SecuredIPdu::try_from(element)?.into()),
640            ElementName::MultiplexedIPdu => Ok(MultiplexedIPdu::try_from(element)?.into()),
641            _ => Err(AutosarAbstractionError::ConversionError {
642                element,
643                dest: "Pdu".to_string(),
644            }),
645        }
646    }
647}
648
649impl IdentifiableAbstractionElement for Pdu {}
650impl AbstractPdu for Pdu {}
651
652//##################################################################
653
654/// Wrapper for all Pdu types. It is used as a return value for functions that can return any Pdu type
655#[derive(Debug, Clone, PartialEq, Eq, Hash)]
656pub enum IPdu {
657    /// The `IPdu` is an `ISignalIPdu`
658    ISignalIPdu(ISignalIPdu),
659    /// The Pdu is a Transport Layer Pdu
660    NPdu(NPdu),
661    /// The `IPdu` is a Diagnostic Communication Management Pdu
662    DcmIPdu(DcmIPdu),
663    /// The `IPdu` is a general purpose Pdu
664    GeneralPurposeIPdu(GeneralPurposeIPdu),
665    /// The `IPdu` is a Container `IPdu`
666    ContainerIPdu(ContainerIPdu),
667    /// The `IPdu` is a secured `IPdu`
668    SecuredIPdu(SecuredIPdu),
669    /// The `IPdu` is a multiplexed `IPdu`
670    MultiplexedIPdu(MultiplexedIPdu),
671}
672
673impl AbstractionElement for IPdu {
674    fn element(&self) -> &Element {
675        match self {
676            IPdu::ISignalIPdu(pdu) => pdu.element(),
677            IPdu::NPdu(pdu) => pdu.element(),
678            IPdu::DcmIPdu(pdu) => pdu.element(),
679            IPdu::GeneralPurposeIPdu(pdu) => pdu.element(),
680            IPdu::ContainerIPdu(pdu) => pdu.element(),
681            IPdu::SecuredIPdu(pdu) => pdu.element(),
682            IPdu::MultiplexedIPdu(pdu) => pdu.element(),
683        }
684    }
685}
686
687impl TryFrom<Element> for IPdu {
688    type Error = AutosarAbstractionError;
689
690    fn try_from(element: Element) -> Result<Self, Self::Error> {
691        match element.element_name() {
692            ElementName::ISignalIPdu => Ok(IPdu::ISignalIPdu(ISignalIPdu::try_from(element)?)),
693            ElementName::NPdu => Ok(IPdu::NPdu(NPdu::try_from(element)?)),
694            ElementName::DcmIPdu => Ok(IPdu::DcmIPdu(DcmIPdu::try_from(element)?)),
695            ElementName::GeneralPurposeIPdu => Ok(IPdu::GeneralPurposeIPdu(GeneralPurposeIPdu::try_from(element)?)),
696            ElementName::ContainerIPdu => Ok(IPdu::ContainerIPdu(ContainerIPdu::try_from(element)?)),
697            ElementName::SecuredIPdu => Ok(IPdu::SecuredIPdu(SecuredIPdu::try_from(element)?)),
698            ElementName::MultiplexedIPdu => Ok(IPdu::MultiplexedIPdu(MultiplexedIPdu::try_from(element)?)),
699            _ => Err(AutosarAbstractionError::ConversionError {
700                element,
701                dest: "IPdu".to_string(),
702            }),
703        }
704    }
705}
706
707impl From<IPdu> for Pdu {
708    fn from(value: IPdu) -> Self {
709        match value {
710            IPdu::ISignalIPdu(pdu) => Pdu::ISignalIPdu(pdu),
711            IPdu::NPdu(pdu) => Pdu::NPdu(pdu),
712            IPdu::DcmIPdu(pdu) => Pdu::DcmIPdu(pdu),
713            IPdu::GeneralPurposeIPdu(pdu) => Pdu::GeneralPurposeIPdu(pdu),
714            IPdu::ContainerIPdu(pdu) => Pdu::ContainerIPdu(pdu),
715            IPdu::SecuredIPdu(pdu) => Pdu::SecuredIPdu(pdu),
716            IPdu::MultiplexedIPdu(pdu) => Pdu::MultiplexedIPdu(pdu),
717        }
718    }
719}
720
721impl IdentifiableAbstractionElement for IPdu {}
722impl AbstractPdu for IPdu {}
723impl AbstractIpdu for IPdu {}
724
725//##################################################################
726
727/// a `PduTriggering` triggers a PDU in a frame or ethernet connection
728#[derive(Debug, Clone, PartialEq, Eq, Hash)]
729pub struct PduTriggering(Element);
730abstraction_element!(PduTriggering, PduTriggering);
731impl IdentifiableAbstractionElement for PduTriggering {}
732
733impl PduTriggering {
734    pub(crate) fn new(pdu: &Pdu, channel: &PhysicalChannel) -> Result<Self, AutosarAbstractionError> {
735        let model = channel.element().model()?;
736        let base_path = channel.element().path()?;
737        let pdu_name = pdu
738            .name()
739            .ok_or(AutosarAbstractionError::InvalidParameter("invalid pdu".to_string()))?;
740        let pt_name = format!("PT_{pdu_name}");
741        let pt_name = make_unique_name(&model, &base_path, &pt_name);
742
743        let triggerings = channel
744            .element()
745            .get_or_create_sub_element(ElementName::PduTriggerings)?;
746        let pt_elem = triggerings.create_named_sub_element(ElementName::PduTriggering, &pt_name)?;
747        pt_elem
748            .create_sub_element(ElementName::IPduRef)?
749            .set_reference_target(pdu.element())?;
750
751        let pt = Self(pt_elem);
752
753        if let Pdu::ISignalIPdu(isignal_ipdu) = pdu {
754            for signal_mapping in isignal_ipdu.mapped_signals() {
755                if let Some(signal) = signal_mapping.signal() {
756                    pt.create_signal_triggering(&signal)?;
757                } else if let Some(signal_group) = signal_mapping.signal_group() {
758                    pt.create_signal_group_triggering(&signal_group)?;
759                }
760            }
761        }
762
763        Ok(pt)
764    }
765
766    /// get the Pdu that is triggered by this pdu triggering
767    #[must_use]
768    pub fn pdu(&self) -> Option<Pdu> {
769        let pdu_elem = self
770            .element()
771            .get_sub_element(ElementName::IPduRef)?
772            .get_reference_target()
773            .ok()?;
774        Pdu::try_from(pdu_elem).ok()
775    }
776
777    /// get the physical channel that contains this pdu triggering
778    pub fn physical_channel(&self) -> Result<PhysicalChannel, AutosarAbstractionError> {
779        let channel_elem = self.element().named_parent()?.ok_or(AutosarDataError::ItemDeleted)?;
780        PhysicalChannel::try_from(channel_elem)
781    }
782
783    /// create an `IPduPort` to connect a `PduTriggering` to an `EcuInstance`
784    pub fn create_pdu_port(
785        &self,
786        ecu: &EcuInstance,
787        direction: CommunicationDirection,
788    ) -> Result<IPduPort, AutosarAbstractionError> {
789        for pdu_port in self.pdu_ports() {
790            if let (Ok(existing_ecu), Some(existing_direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
791                if existing_ecu == *ecu && existing_direction == direction {
792                    return Ok(pdu_port);
793                }
794            }
795        }
796
797        let channel = self.physical_channel()?;
798        let connector = channel
799            .ecu_connector(ecu)
800            .ok_or(AutosarAbstractionError::InvalidParameter(
801                "The ECU is not connected to the channel".to_string(),
802            ))?;
803
804        let name = self.name().ok_or(AutosarDataError::ItemDeleted)?;
805        let suffix = match direction {
806            CommunicationDirection::In => "Rx",
807            CommunicationDirection::Out => "Tx",
808        };
809        let port_name = format!("{name}_{suffix}",);
810        let pp_elem = connector
811            .element()
812            .get_or_create_sub_element(ElementName::EcuCommPortInstances)?
813            .create_named_sub_element(ElementName::IPduPort, &port_name)?;
814        pp_elem
815            .create_sub_element(ElementName::CommunicationDirection)?
816            .set_character_data::<EnumItem>(direction.into())?;
817
818        self.element()
819            .get_or_create_sub_element(ElementName::IPduPortRefs)?
820            .create_sub_element(ElementName::IPduPortRef)?
821            .set_reference_target(&pp_elem)?;
822
823        for st in self.signal_triggerings() {
824            st.connect_to_ecu(ecu, direction)?;
825        }
826
827        Ok(IPduPort(pp_elem))
828    }
829
830    /// create an iterator over the `IPduPorts` that are connected to this `PduTriggering`
831    pub fn pdu_ports(&self) -> impl Iterator<Item = IPduPort> + Send + use<> {
832        self.element()
833            .get_sub_element(ElementName::IPduPortRefs)
834            .into_iter()
835            .flat_map(|ipprefs| ipprefs.sub_elements())
836            .filter_map(|ippref| {
837                ippref
838                    .get_reference_target()
839                    .ok()
840                    .and_then(|elem| IPduPort::try_from(elem).ok())
841            })
842    }
843
844    /// create an iterator over the `ISignalTriggerings` that are triggered by this `PduTriggering`
845    pub fn signal_triggerings(&self) -> impl Iterator<Item = ISignalTriggering> + Send + use<> {
846        self.element()
847            .get_sub_element(ElementName::ISignalTriggerings)
848            .into_iter()
849            .flat_map(|ists| ists.sub_elements())
850            .filter_map(|ist| {
851                ist.get_sub_element(ElementName::ISignalTriggeringRef)
852                    .and_then(|str| str.get_reference_target().ok())
853                    .and_then(|elem| ISignalTriggering::try_from(elem).ok())
854            })
855    }
856
857    /// create a signal triggering for a signal and connect it to this `PduTriggering`
858    pub(crate) fn create_signal_triggering(
859        &self,
860        signal: &ISignal,
861    ) -> Result<ISignalTriggering, AutosarAbstractionError> {
862        let channel = self.physical_channel()?;
863        let st = ISignalTriggering::new(signal, &channel)?;
864        let triggerings = self
865            .element()
866            .get_or_create_sub_element(ElementName::ISignalTriggerings)?;
867        triggerings
868            .create_sub_element(ElementName::ISignalTriggeringRefConditional)?
869            .create_sub_element(ElementName::ISignalTriggeringRef)?
870            .set_reference_target(st.element())?;
871
872        for pdu_port in self.pdu_ports() {
873            if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
874                st.connect_to_ecu(&ecu, direction)?;
875            }
876        }
877
878        Ok(st)
879    }
880
881    /// create a signal triggering for a signal group and connect it to this `PduTriggering`
882    pub(crate) fn create_signal_group_triggering(
883        &self,
884        signal_group: &ISignalGroup,
885    ) -> Result<ISignalTriggering, AutosarAbstractionError> {
886        let channel = self.physical_channel()?;
887        let st = ISignalTriggering::new_group(signal_group, &channel)?;
888        let triggerings = self
889            .element()
890            .get_or_create_sub_element(ElementName::ISignalTriggerings)?;
891        triggerings
892            .create_sub_element(ElementName::ISignalTriggeringRefConditional)?
893            .create_sub_element(ElementName::ISignalTriggeringRef)?
894            .set_reference_target(st.element())?;
895
896        for pdu_port in self.pdu_ports() {
897            if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
898                st.connect_to_ecu(&ecu, direction)?;
899            }
900        }
901
902        Ok(st)
903    }
904}
905
906//##################################################################
907
908/// The `IPduPort` allows an ECU to send or receive a PDU
909#[derive(Debug, Clone, PartialEq, Eq, Hash)]
910pub struct IPduPort(Element);
911abstraction_element!(IPduPort, IPduPort);
912impl IdentifiableAbstractionElement for IPduPort {}
913
914impl IPduPort {
915    /// get the ECU instance that contains this `IPduPort`
916    pub fn ecu(&self) -> Result<EcuInstance, AutosarAbstractionError> {
917        let comm_connector_elem = self.element().named_parent()?.unwrap();
918        let ecu_elem = comm_connector_elem.named_parent()?.unwrap();
919        EcuInstance::try_from(ecu_elem)
920    }
921
922    /// set the communication direction of this `IPduPort`
923    pub fn set_communication_direction(
924        &self,
925        direction: CommunicationDirection,
926    ) -> Result<(), AutosarAbstractionError> {
927        self.element()
928            .get_or_create_sub_element(ElementName::CommunicationDirection)?
929            .set_character_data::<EnumItem>(direction.into())?;
930        Ok(())
931    }
932
933    /// get the communication direction of this `IPduPort`
934    #[must_use]
935    pub fn communication_direction(&self) -> Option<CommunicationDirection> {
936        self.element()
937            .get_sub_element(ElementName::CommunicationDirection)?
938            .character_data()?
939            .enum_value()?
940            .try_into()
941            .ok()
942    }
943}
944
945//##################################################################
946
947/// The collction trigger defines whether a Pdu contributes to the triggering
948/// of the data transmission if Pdu collection is enabled
949#[derive(Debug, Clone, Copy, PartialEq, Eq)]
950pub enum PduCollectionTrigger {
951    /// Pdu will trigger the transmission of the data.
952    Always,
953    /// Pdu will be buffered and will not trigger the transmission of the data
954    Never,
955}
956
957impl From<PduCollectionTrigger> for EnumItem {
958    fn from(value: PduCollectionTrigger) -> Self {
959        match value {
960            PduCollectionTrigger::Always => EnumItem::Always,
961            PduCollectionTrigger::Never => EnumItem::Never,
962        }
963    }
964}
965
966impl TryFrom<EnumItem> for PduCollectionTrigger {
967    type Error = AutosarAbstractionError;
968
969    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
970        match value {
971            EnumItem::Always => Ok(PduCollectionTrigger::Always),
972            EnumItem::Never => Ok(PduCollectionTrigger::Never),
973            _ => Err(AutosarAbstractionError::ValueConversionError {
974                value: value.to_string(),
975                dest: "PduCollectionTrigger".to_string(),
976            }),
977        }
978    }
979}
980
981//##################################################################
982
983#[cfg(test)]
984mod test {
985    use super::*;
986    use crate::{
987        AutosarModelAbstraction, ByteOrder, SystemCategory,
988        communication::{AbstractFrame, AbstractFrameTriggering, CanAddressingMode, CanFrameType, TransferProperty},
989    };
990    use autosar_data::AutosarVersion;
991
992    #[test]
993    fn test_pdus() {
994        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
995        let package = model.get_or_create_package("/pkg").unwrap();
996        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
997
998        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
999        let nm_pdu = system.create_nm_pdu("nm_pdu", &package, 1).unwrap();
1000        let n_pdu = system.create_n_pdu("n_pdu", &package, 1).unwrap();
1001        let dcm_ipdu = system
1002            .create_dcm_ipdu("dcm_ipdu", &package, 1, DiagPduType::DiagRequest)
1003            .unwrap();
1004        let gp_pdu = system
1005            .create_general_purpose_pdu("gp_pdu", &package, 1, GeneralPurposePduCategory::Sd)
1006            .unwrap();
1007        let gp_ipdu = system
1008            .create_general_purpose_ipdu("gp_ipdu", &package, 1, GeneralPurposeIPduCategory::Xcp)
1009            .unwrap();
1010        let container_ipdu = system
1011            .create_container_ipdu(
1012                "container_ipdu",
1013                &package,
1014                1,
1015                ContainerIPduHeaderType::ShortHeader,
1016                RxAcceptContainedIPdu::AcceptAll,
1017            )
1018            .unwrap();
1019        let secured_ipdu = system
1020            .create_secured_ipdu("secured_ipdu", &package, 1, &SecureCommunicationProps::default())
1021            .unwrap();
1022        let multiplexed_ipdu = system.create_multiplexed_ipdu("multiplexed_ipdu", &package, 1).unwrap();
1023
1024        assert_eq!(isignal_ipdu.length().unwrap(), 1);
1025        assert_eq!(nm_pdu.length().unwrap(), 1);
1026        assert_eq!(n_pdu.length().unwrap(), 1);
1027        assert_eq!(dcm_ipdu.length().unwrap(), 1);
1028        assert_eq!(gp_pdu.length().unwrap(), 1);
1029        assert_eq!(gp_ipdu.length().unwrap(), 1);
1030        assert_eq!(container_ipdu.length().unwrap(), 1);
1031        assert_eq!(secured_ipdu.length().unwrap(), 1);
1032        assert_eq!(multiplexed_ipdu.length().unwrap(), 1);
1033
1034        isignal_ipdu.set_length(2).unwrap();
1035        assert_eq!(isignal_ipdu.length().unwrap(), 2);
1036
1037        let frame = system.create_flexray_frame("frame1", &package, 64).unwrap();
1038        frame
1039            .map_pdu(&isignal_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
1040            .unwrap();
1041        frame
1042            .map_pdu(&nm_pdu, 8, ByteOrder::MostSignificantByteLast, None)
1043            .unwrap();
1044        frame
1045            .map_pdu(&n_pdu, 16, ByteOrder::MostSignificantByteLast, None)
1046            .unwrap();
1047        frame
1048            .map_pdu(&dcm_ipdu, 24, ByteOrder::MostSignificantByteLast, None)
1049            .unwrap();
1050        frame
1051            .map_pdu(&gp_pdu, 32, ByteOrder::MostSignificantByteLast, None)
1052            .unwrap();
1053        frame
1054            .map_pdu(&gp_ipdu, 40, ByteOrder::MostSignificantByteLast, None)
1055            .unwrap();
1056        frame
1057            .map_pdu(&container_ipdu, 48, ByteOrder::MostSignificantByteLast, None)
1058            .unwrap();
1059        frame
1060            .map_pdu(&secured_ipdu, 56, ByteOrder::MostSignificantByteLast, None)
1061            .unwrap();
1062        frame
1063            .map_pdu(&multiplexed_ipdu, 64, ByteOrder::MostSignificantByteLast, None)
1064            .unwrap();
1065
1066        let mut pdus_iter = frame.mapped_pdus();
1067        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "isignal_ipdu");
1068        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "nm_pdu");
1069        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "n_pdu");
1070        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "dcm_ipdu");
1071        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "gp_pdu");
1072        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "gp_ipdu");
1073        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "container_ipdu");
1074        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "secured_ipdu");
1075        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "multiplexed_ipdu");
1076        assert!(pdus_iter.next().is_none());
1077    }
1078
1079    #[test]
1080    fn test_pdu_triggering() {
1081        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1082        let package = model.get_or_create_package("/pkg").unwrap();
1083        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1084
1085        // create an ISignalIPdu with a signal
1086        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
1087        let syssignal = package.create_system_signal("syssignal").unwrap();
1088        let isignal = system.create_isignal("isignal", &package, 1, &syssignal, None).unwrap();
1089        isignal_ipdu
1090            .map_signal(
1091                &isignal,
1092                0,
1093                ByteOrder::MostSignificantByteLast,
1094                None,
1095                TransferProperty::Triggered,
1096            )
1097            .unwrap();
1098        // create an ISignalGroup with a second signal
1099        let syssignal_group = package.create_system_signal_group("syssignal_group").unwrap();
1100        let isignal_group = system
1101            .create_isignal_group("isignal_group", &package, &syssignal_group)
1102            .unwrap();
1103        let syssignal2 = package.create_system_signal("syssignal2").unwrap();
1104        let isignal2 = system
1105            .create_isignal("isignal2", &package, 1, &syssignal2, None)
1106            .unwrap();
1107        isignal_ipdu.map_signal_group(&isignal_group).unwrap();
1108        isignal_ipdu
1109            .map_signal(
1110                &isignal2,
1111                1,
1112                ByteOrder::MostSignificantByteLast,
1113                None,
1114                TransferProperty::Triggered,
1115            )
1116            .unwrap();
1117
1118        // create a frame and map the ISignalIPdu to it
1119        let can_cluster = system.create_can_cluster("Cluster", &package, None).unwrap();
1120        let channel = can_cluster.create_physical_channel("Channel").unwrap();
1121        let frame = system.create_can_frame("frame", &package, 8).unwrap();
1122        let frame_triggering = channel
1123            .trigger_frame(&frame, 0x123, CanAddressingMode::Standard, CanFrameType::Can20)
1124            .unwrap();
1125        let _mapping = frame
1126            .map_pdu(&isignal_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
1127            .unwrap();
1128
1129        // create an EcuInstance, and connect it to the channel. The frame is reeived by the ECU
1130        let ecu = system.create_ecu_instance("ecu", &package).unwrap();
1131        let controller = ecu.create_can_communication_controller("controller").unwrap();
1132        controller.connect_physical_channel("connection", &channel).unwrap();
1133        frame_triggering
1134            .connect_to_ecu(&ecu, CommunicationDirection::In)
1135            .unwrap();
1136
1137        let pdu_triggering = frame_triggering.pdu_triggerings().next().unwrap();
1138        assert_eq!(pdu_triggering.pdu_ports().count(), 1);
1139        assert_eq!(pdu_triggering.signal_triggerings().count(), 3); // one for each signal, and another for the signal group
1140
1141        let pdu_port = pdu_triggering.pdu_ports().next().unwrap();
1142        assert_eq!(pdu_port.ecu().unwrap().name().unwrap(), "ecu");
1143        assert_eq!(pdu_port.communication_direction().unwrap(), CommunicationDirection::In);
1144        pdu_port
1145            .set_communication_direction(CommunicationDirection::Out)
1146            .unwrap();
1147        assert_eq!(pdu_port.communication_direction().unwrap(), CommunicationDirection::Out);
1148        pdu_port.set_name("new_name").unwrap();
1149        assert_eq!(pdu_port.name().unwrap(), "new_name");
1150    }
1151
1152    #[test]
1153    fn nm_pdu() {
1154        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00052);
1155        let package = model.get_or_create_package("/pkg").unwrap();
1156        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1157
1158        let nm_pdu = system.create_nm_pdu("nm_pdu", &package, 1).unwrap();
1159        assert_eq!(nm_pdu.length().unwrap(), 1);
1160
1161        nm_pdu.set_length(8).unwrap();
1162        assert_eq!(nm_pdu.length().unwrap(), 8);
1163
1164        nm_pdu.set_unused_bit_pattern(0xff).unwrap();
1165        assert_eq!(nm_pdu.unused_bit_pattern().unwrap(), 0xff);
1166
1167        // create a signal and map it to the PDU
1168        let syssignal = package.create_system_signal("sys_userdata").unwrap();
1169        let isignal = system
1170            .create_isignal("userdata", &package, 16, &syssignal, None)
1171            .unwrap();
1172        let mapping = nm_pdu
1173            .map_signal(
1174                &isignal,
1175                0,
1176                ByteOrder::MostSignificantByteFirst,
1177                Some(16),
1178                TransferProperty::Triggered,
1179            )
1180            .unwrap();
1181        assert_eq!(mapping.signal().unwrap(), isignal);
1182    }
1183
1184    #[test]
1185    fn general_purpose_pdu() {
1186        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1187        let package = model.get_or_create_package("/pkg").unwrap();
1188        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1189
1190        let gp_pdu1 = system
1191            .create_general_purpose_pdu("gp_pdu1", &package, 1, GeneralPurposePduCategory::Sd)
1192            .unwrap();
1193        assert_eq!(gp_pdu1.category().unwrap(), GeneralPurposePduCategory::Sd);
1194
1195        let gp_pdu2 = system
1196            .create_general_purpose_pdu("gp_pdu2", &package, 1, GeneralPurposePduCategory::GlobalTime)
1197            .unwrap();
1198        assert_eq!(gp_pdu2.category().unwrap(), GeneralPurposePduCategory::GlobalTime);
1199
1200        let gp_pdu3 = system
1201            .create_general_purpose_pdu("gp_pdu3", &package, 1, GeneralPurposePduCategory::DoIp)
1202            .unwrap();
1203        assert_eq!(gp_pdu3.category().unwrap(), GeneralPurposePduCategory::DoIp);
1204
1205        // conversion of category to string and back
1206        assert_eq!(
1207            GeneralPurposePduCategory::from_str("SD").unwrap(),
1208            GeneralPurposePduCategory::Sd
1209        );
1210        assert_eq!(
1211            GeneralPurposePduCategory::from_str("GLOBAL_TIME").unwrap(),
1212            GeneralPurposePduCategory::GlobalTime
1213        );
1214        assert_eq!(
1215            GeneralPurposePduCategory::from_str("DOIP").unwrap(),
1216            GeneralPurposePduCategory::DoIp
1217        );
1218        assert!(GeneralPurposePduCategory::from_str("invalid").is_err());
1219    }
1220
1221    #[test]
1222    fn create_general_purpose_ipdu() {
1223        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1224        let package = model.get_or_create_package("/pkg").unwrap();
1225        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1226
1227        let gp_ipdu1 = system
1228            .create_general_purpose_ipdu("gp_ipdu1", &package, 1, GeneralPurposeIPduCategory::Xcp)
1229            .unwrap();
1230        assert_eq!(gp_ipdu1.category().unwrap(), GeneralPurposeIPduCategory::Xcp);
1231
1232        let gp_ipdu2 = system
1233            .create_general_purpose_ipdu("gp_ipdu2", &package, 1, GeneralPurposeIPduCategory::SomeipSegmentedIpdu)
1234            .unwrap();
1235        assert_eq!(
1236            gp_ipdu2.category().unwrap(),
1237            GeneralPurposeIPduCategory::SomeipSegmentedIpdu
1238        );
1239
1240        let gp_ipdu3 = system
1241            .create_general_purpose_ipdu("gp_ipdu3", &package, 1, GeneralPurposeIPduCategory::Dlt)
1242            .unwrap();
1243        assert_eq!(gp_ipdu3.category().unwrap(), GeneralPurposeIPduCategory::Dlt);
1244
1245        // conversion of category to string and back
1246        assert_eq!(
1247            GeneralPurposeIPduCategory::from_str("XCP").unwrap(),
1248            GeneralPurposeIPduCategory::Xcp
1249        );
1250        assert_eq!(
1251            GeneralPurposeIPduCategory::from_str("SOMEIP_SEGMENTED_IPDU").unwrap(),
1252            GeneralPurposeIPduCategory::SomeipSegmentedIpdu
1253        );
1254        assert_eq!(
1255            GeneralPurposeIPduCategory::from_str("DLT").unwrap(),
1256            GeneralPurposeIPduCategory::Dlt
1257        );
1258        assert!(GeneralPurposeIPduCategory::from_str("invalid").is_err());
1259    }
1260
1261    #[test]
1262    fn ipdu() {
1263        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1264        let package = model.get_or_create_package("/pkg").unwrap();
1265        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1266
1267        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
1268        let n_pdu = system.create_n_pdu("n_pdu", &package, 1).unwrap();
1269        let dcm_ipdu = system
1270            .create_dcm_ipdu("dcm_ipdu", &package, 1, DiagPduType::DiagResponse)
1271            .unwrap();
1272        let gp_ipdu = system
1273            .create_general_purpose_ipdu("gp_ipdu", &package, 1, GeneralPurposeIPduCategory::Xcp)
1274            .unwrap();
1275        let container_ipdu = system
1276            .create_container_ipdu(
1277                "container_ipdu",
1278                &package,
1279                1,
1280                ContainerIPduHeaderType::LongHeader,
1281                RxAcceptContainedIPdu::AcceptConfigured,
1282            )
1283            .unwrap();
1284        let secured_ipdu = system
1285            .create_secured_ipdu("secured_ipdu", &package, 1, &SecureCommunicationProps::default())
1286            .unwrap();
1287        let multiplexed_ipdu = system.create_multiplexed_ipdu("multiplexed_ipdu", &package, 1).unwrap();
1288
1289        let ipdu: IPdu = isignal_ipdu.clone().into();
1290        assert_eq!(ipdu.element(), isignal_ipdu.element());
1291        assert!(matches!(ipdu, IPdu::ISignalIPdu(_)));
1292
1293        // NmPdu is not an ipdu
1294
1295        let ipdu: IPdu = n_pdu.clone().into();
1296        assert_eq!(ipdu.element(), n_pdu.element());
1297        assert!(matches!(ipdu, IPdu::NPdu(_)));
1298
1299        let ipdu: IPdu = dcm_ipdu.clone().into();
1300        assert_eq!(ipdu.element(), dcm_ipdu.element());
1301        assert!(matches!(ipdu, IPdu::DcmIPdu(_)));
1302
1303        let ipdu: IPdu = n_pdu.clone().into();
1304        assert_eq!(ipdu.element(), n_pdu.element());
1305        assert!(matches!(ipdu, IPdu::NPdu(_)));
1306
1307        // GeneralPurposePdu is not an ipdu
1308
1309        let ipdu: IPdu = gp_ipdu.clone().into();
1310        assert_eq!(ipdu.element(), gp_ipdu.element());
1311        assert!(matches!(ipdu, IPdu::GeneralPurposeIPdu(_)));
1312
1313        let ipdu: IPdu = container_ipdu.clone().into();
1314        assert_eq!(ipdu.element(), container_ipdu.element());
1315        assert!(matches!(ipdu, IPdu::ContainerIPdu(_)));
1316
1317        let ipdu: IPdu = secured_ipdu.clone().into();
1318        assert_eq!(ipdu.element(), secured_ipdu.element());
1319        assert!(matches!(ipdu, IPdu::SecuredIPdu(_)));
1320
1321        let ipdu: IPdu = multiplexed_ipdu.clone().into();
1322        assert_eq!(ipdu.element(), multiplexed_ipdu.element());
1323        assert!(matches!(ipdu, IPdu::MultiplexedIPdu(_)));
1324
1325        // any Ipdu can be converted to a Pdu
1326        let pdu: Pdu = ipdu.clone().into();
1327        assert_eq!(pdu.element(), ipdu.element());
1328    }
1329}