autosar_data_abstraction/communication/pdu/
mod.rs

1use crate::communication::{
2    AbstractPhysicalChannel, CommunicationDirection, ISignal, ISignalGroup, ISignalTriggering, PhysicalChannel,
3};
4use crate::{
5    AbstractionElement, ArPackage, AutosarAbstractionError, EcuInstance, IdentifiableAbstractionElement,
6    abstraction_element, make_unique_name,
7};
8use autosar_data::{AutosarDataError, Element, ElementName, EnumItem};
9use std::str::FromStr;
10
11mod container_ipdu;
12mod isignal_ipdu;
13mod secured_ipdu;
14
15pub use container_ipdu::*;
16pub use isignal_ipdu::*;
17pub use secured_ipdu::*;
18
19//##################################################################
20
21/// This trait is implemented by all Pdus
22pub trait AbstractPdu: AbstractionElement + Into<Pdu> {
23    /// set the length of the PDU
24    fn set_length(&self, length: u32) -> Result<(), AutosarAbstractionError> {
25        self.element()
26            .get_or_create_sub_element(ElementName::Length)?
27            .set_character_data(length as u64)?;
28        Ok(())
29    }
30
31    /// get the length of the PDU
32    fn length(&self) -> Option<u32> {
33        self.element()
34            .get_sub_element(ElementName::Length)?
35            .character_data()?
36            .parse_integer()
37    }
38
39    /// list all `PduTriggerings` that trigger this PDU
40    fn pdu_triggerings(&self) -> Vec<PduTriggering> {
41        let model_result = self.element().model();
42        let path_result = self.element().path();
43        if let (Ok(model), Ok(path)) = (model_result, path_result) {
44            model
45                .get_references_to(&path)
46                .iter()
47                .filter_map(|e| {
48                    e.upgrade()
49                        .and_then(|ref_elem| ref_elem.named_parent().ok().flatten())
50                        .and_then(|elem| PduTriggering::try_from(elem).ok())
51                })
52                .collect()
53        } else {
54            vec![]
55        }
56    }
57}
58
59//##################################################################
60
61/// for now this is a marker trait to identify `IPdus`
62pub trait AbstractIpdu: AbstractPdu + Into<IPdu> {
63    /// set the `ContainedIPduProps` for this `IPdu`
64    ///
65    /// This is only relevant for `IPdus` that will be transmitted in `ContainerIPdus`
66    fn set_contained_ipdu_props(&self, props: Option<&ContainedIPduProps>) -> Result<(), AutosarAbstractionError> {
67        ContainedIPduProps::set_props(self.element(), props)
68    }
69
70    /// get the `ContainedIPduProps` for this `IPdu`
71    #[must_use]
72    fn contained_ipdu_props(&self) -> Option<ContainedIPduProps> {
73        ContainedIPduProps::get_props(self.element())
74    }
75}
76
77//##################################################################
78
79/// Network Management Pdu
80#[derive(Debug, Clone, PartialEq, Eq, Hash)]
81pub struct NmPdu(Element);
82abstraction_element!(NmPdu, NmPdu);
83impl IdentifiableAbstractionElement for NmPdu {}
84
85impl NmPdu {
86    pub(crate) fn new(name: &str, package: &ArPackage, length: u32) -> Result<Self, AutosarAbstractionError> {
87        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
88        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::NmPdu, name)?;
89        elem_pdu
90            .create_sub_element(ElementName::Length)?
91            .set_character_data(length.to_string())?;
92
93        Ok(Self(elem_pdu))
94    }
95}
96
97impl AbstractPdu for NmPdu {}
98
99impl From<NmPdu> for Pdu {
100    fn from(value: NmPdu) -> Self {
101        Pdu::NmPdu(value)
102    }
103}
104
105//##################################################################
106
107/// This is a Pdu of the transport layer. The main purpose of the TP layer is to segment and reassemble `IPdus`.
108#[derive(Debug, Clone, PartialEq, Eq, Hash)]
109pub struct NPdu(Element);
110abstraction_element!(NPdu, NPdu);
111impl IdentifiableAbstractionElement for NPdu {}
112
113impl NPdu {
114    pub(crate) fn new(name: &str, package: &ArPackage, length: u32) -> Result<Self, AutosarAbstractionError> {
115        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
116        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::NPdu, name)?;
117        elem_pdu
118            .create_sub_element(ElementName::Length)?
119            .set_character_data(length.to_string())?;
120
121        Ok(Self(elem_pdu))
122    }
123}
124
125impl AbstractPdu for NPdu {}
126
127impl AbstractIpdu for NPdu {}
128
129impl From<NPdu> for Pdu {
130    fn from(value: NPdu) -> Self {
131        Pdu::NPdu(value)
132    }
133}
134
135impl From<NPdu> for IPdu {
136    fn from(value: NPdu) -> Self {
137        IPdu::NPdu(value)
138    }
139}
140
141//##################################################################
142
143/// Represents the `IPdus` handled by Dcm
144#[derive(Debug, Clone, PartialEq, Eq, Hash)]
145pub struct DcmIPdu(Element);
146abstraction_element!(DcmIPdu, DcmIPdu);
147impl IdentifiableAbstractionElement for DcmIPdu {}
148
149impl DcmIPdu {
150    pub(crate) fn new(name: &str, package: &ArPackage, length: u32) -> Result<Self, AutosarAbstractionError> {
151        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
152        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::DcmIPdu, name)?;
153        elem_pdu
154            .create_sub_element(ElementName::Length)?
155            .set_character_data(length.to_string())?;
156
157        Ok(Self(elem_pdu))
158    }
159}
160
161impl AbstractPdu for DcmIPdu {}
162
163impl AbstractIpdu for DcmIPdu {}
164
165impl From<DcmIPdu> for Pdu {
166    fn from(value: DcmIPdu) -> Self {
167        Pdu::DcmIPdu(value)
168    }
169}
170
171impl From<DcmIPdu> for IPdu {
172    fn from(value: DcmIPdu) -> Self {
173        IPdu::DcmIPdu(value)
174    }
175}
176
177//##################################################################
178
179/// This element is used for AUTOSAR Pdus without additional attributes that are routed by a bus interface
180#[derive(Debug, Clone, PartialEq, Eq, Hash)]
181pub struct GeneralPurposePdu(Element);
182abstraction_element!(GeneralPurposePdu, GeneralPurposePdu);
183impl IdentifiableAbstractionElement for GeneralPurposePdu {}
184
185impl GeneralPurposePdu {
186    pub(crate) fn new(
187        name: &str,
188        package: &ArPackage,
189        length: u32,
190        category: GeneralPurposePduCategory,
191    ) -> Result<Self, AutosarAbstractionError> {
192        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
193        let pdu_elem = pkg_elements.create_named_sub_element(ElementName::GeneralPurposePdu, name)?;
194        let pdu = Self(pdu_elem);
195
196        pdu.set_length(length)?;
197        pdu.set_category(category)?;
198
199        Ok(pdu)
200    }
201
202    /// set the category of this PDU
203    pub fn set_category(&self, category: GeneralPurposePduCategory) -> Result<(), AutosarAbstractionError> {
204        self.element()
205            .get_or_create_sub_element(ElementName::Category)?
206            .set_character_data(category.to_string())?;
207        Ok(())
208    }
209
210    /// get the category of this PDU
211    #[must_use]
212    pub fn category(&self) -> Option<GeneralPurposePduCategory> {
213        let category_string = self
214            .element()
215            .get_sub_element(ElementName::Category)?
216            .character_data()?
217            .string_value()?;
218        GeneralPurposePduCategory::from_str(&category_string).ok()
219    }
220}
221
222impl AbstractPdu for GeneralPurposePdu {}
223
224impl From<GeneralPurposePdu> for Pdu {
225    fn from(value: GeneralPurposePdu) -> Self {
226        Pdu::GeneralPurposePdu(value)
227    }
228}
229
230//##################################################################
231
232/// The category of a `GeneralPurposePdu`
233///
234/// The Autosar standard defines the following categories:
235/// - `SD`
236/// - `GLOBAL_TIME`
237/// - `DOIP`
238#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
239pub enum GeneralPurposePduCategory {
240    /// Service Discovery
241    Sd,
242    /// Global Time Synchronization
243    GlobalTime,
244    /// Diagnostic over IP
245    DoIp,
246}
247
248impl std::fmt::Display for GeneralPurposePduCategory {
249    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250        match self {
251            GeneralPurposePduCategory::Sd => write!(f, "SD"),
252            GeneralPurposePduCategory::GlobalTime => write!(f, "GLOBAL_TIME"),
253            GeneralPurposePduCategory::DoIp => write!(f, "DOIP"),
254        }
255    }
256}
257
258impl std::str::FromStr for GeneralPurposePduCategory {
259    type Err = AutosarAbstractionError;
260
261    fn from_str(s: &str) -> Result<Self, Self::Err> {
262        match s {
263            "SD" => Ok(GeneralPurposePduCategory::Sd),
264            "GLOBAL_TIME" => Ok(GeneralPurposePduCategory::GlobalTime),
265            "DOIP" => Ok(GeneralPurposePduCategory::DoIp),
266            _ => Err(AutosarAbstractionError::InvalidParameter(s.to_string())),
267        }
268    }
269}
270
271//##################################################################
272
273/// This element is used for AUTOSAR Pdus without attributes that are routed by the `PduR`
274#[derive(Debug, Clone, PartialEq, Eq, Hash)]
275pub struct GeneralPurposeIPdu(Element);
276abstraction_element!(GeneralPurposeIPdu, GeneralPurposeIPdu);
277impl IdentifiableAbstractionElement for GeneralPurposeIPdu {}
278
279impl GeneralPurposeIPdu {
280    pub(crate) fn new(
281        name: &str,
282        package: &ArPackage,
283        length: u32,
284        category: GeneralPurposeIPduCategory,
285    ) -> Result<Self, AutosarAbstractionError> {
286        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
287        let pdu_elem = pkg_elements.create_named_sub_element(ElementName::GeneralPurposeIPdu, name)?;
288        let pdu = Self(pdu_elem);
289
290        pdu.set_length(length)?;
291        pdu.set_category(category)?;
292
293        Ok(pdu)
294    }
295
296    /// set the category of this PDU
297    pub fn set_category(&self, category: GeneralPurposeIPduCategory) -> Result<(), AutosarAbstractionError> {
298        self.element()
299            .get_or_create_sub_element(ElementName::Category)?
300            .set_character_data(category.to_string())?;
301        Ok(())
302    }
303
304    /// get the category of this PDU
305    #[must_use]
306    pub fn category(&self) -> Option<GeneralPurposeIPduCategory> {
307        let category_string = self
308            .element()
309            .get_sub_element(ElementName::Category)?
310            .character_data()?
311            .string_value()?;
312        GeneralPurposeIPduCategory::from_str(&category_string).ok()
313    }
314}
315
316impl AbstractPdu for GeneralPurposeIPdu {}
317
318impl AbstractIpdu for GeneralPurposeIPdu {}
319
320impl From<GeneralPurposeIPdu> for Pdu {
321    fn from(value: GeneralPurposeIPdu) -> Self {
322        Pdu::GeneralPurposeIPdu(value)
323    }
324}
325
326impl From<GeneralPurposeIPdu> for IPdu {
327    fn from(value: GeneralPurposeIPdu) -> Self {
328        IPdu::GeneralPurposeIPdu(value)
329    }
330}
331
332//##################################################################
333
334/// The category of a `GeneralPurposeIPdu`
335///
336/// The Autosar standard defines the following categories:
337/// - XCP
338/// - `SOMEIP_SEGMENTED_IPDU`
339/// - DLT
340#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
341pub enum GeneralPurposeIPduCategory {
342    /// XCP
343    Xcp,
344    /// SOME/IP Segmented `IPdu`
345    SomeipSegmentedIpdu,
346    /// Diagnostic Log and Trace
347    Dlt,
348}
349
350impl std::fmt::Display for GeneralPurposeIPduCategory {
351    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
352        match self {
353            GeneralPurposeIPduCategory::Xcp => write!(f, "XCP"),
354            GeneralPurposeIPduCategory::SomeipSegmentedIpdu => write!(f, "SOMEIP_SEGMENTED_IPDU"),
355            GeneralPurposeIPduCategory::Dlt => write!(f, "DLT"),
356        }
357    }
358}
359
360impl std::str::FromStr for GeneralPurposeIPduCategory {
361    type Err = AutosarAbstractionError;
362
363    fn from_str(s: &str) -> Result<Self, Self::Err> {
364        match s {
365            "XCP" => Ok(GeneralPurposeIPduCategory::Xcp),
366            "SOMEIP_SEGMENTED_IPDU" => Ok(GeneralPurposeIPduCategory::SomeipSegmentedIpdu),
367            "DLT" => Ok(GeneralPurposeIPduCategory::Dlt),
368            _ => Err(AutosarAbstractionError::InvalidParameter(s.to_string())),
369        }
370    }
371}
372
373//##################################################################
374
375/// The multiplexed pdu contains one of serveral signal pdus
376#[derive(Debug, Clone, PartialEq, Eq, Hash)]
377pub struct MultiplexedIPdu(Element);
378abstraction_element!(MultiplexedIPdu, MultiplexedIPdu);
379impl IdentifiableAbstractionElement for MultiplexedIPdu {}
380
381impl MultiplexedIPdu {
382    pub(crate) fn new(name: &str, package: &ArPackage, length: u32) -> Result<Self, AutosarAbstractionError> {
383        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
384        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::MultiplexedIPdu, name)?;
385        elem_pdu
386            .create_sub_element(ElementName::Length)?
387            .set_character_data(length.to_string())?;
388
389        Ok(Self(elem_pdu))
390    }
391}
392
393impl AbstractPdu for MultiplexedIPdu {}
394
395impl AbstractIpdu for MultiplexedIPdu {}
396
397impl From<MultiplexedIPdu> for Pdu {
398    fn from(value: MultiplexedIPdu) -> Self {
399        Pdu::MultiplexedIPdu(value)
400    }
401}
402
403impl From<MultiplexedIPdu> for IPdu {
404    fn from(value: MultiplexedIPdu) -> Self {
405        IPdu::MultiplexedIPdu(value)
406    }
407}
408
409//##################################################################
410
411/// Wrapper for all Pdu types. It is used as a return value for functions that can return any Pdu type
412#[derive(Debug, Clone, PartialEq, Eq, Hash)]
413pub enum Pdu {
414    /// The Pdu is an `ISignalIPdu`
415    ISignalIPdu(ISignalIPdu),
416    /// The Pdu is a Network Management Pdu
417    NmPdu(NmPdu),
418    /// The Pdu is a Transport Layer Pdu
419    NPdu(NPdu),
420    /// The Pdu is a Diagnostic Communication Management Pdu
421    DcmIPdu(DcmIPdu),
422    /// The Pdu is a General Purpose Pdu
423    GeneralPurposePdu(GeneralPurposePdu),
424    /// The Pdu is a General Purpose `IPdu`
425    GeneralPurposeIPdu(GeneralPurposeIPdu),
426    /// The Pdu is a Container `IPdu`
427    ContainerIPdu(ContainerIPdu),
428    /// The Pdu is a Secured `IPdu`
429    SecuredIPdu(SecuredIPdu),
430    /// The Pdu is a Multiplexed `IPdu`
431    MultiplexedIPdu(MultiplexedIPdu),
432}
433
434impl AbstractionElement for Pdu {
435    fn element(&self) -> &Element {
436        match self {
437            Pdu::ISignalIPdu(pdu) => pdu.element(),
438            Pdu::NmPdu(pdu) => pdu.element(),
439            Pdu::NPdu(pdu) => pdu.element(),
440            Pdu::DcmIPdu(pdu) => pdu.element(),
441            Pdu::GeneralPurposePdu(pdu) => pdu.element(),
442            Pdu::GeneralPurposeIPdu(pdu) => pdu.element(),
443            Pdu::ContainerIPdu(pdu) => pdu.element(),
444            Pdu::SecuredIPdu(pdu) => pdu.element(),
445            Pdu::MultiplexedIPdu(pdu) => pdu.element(),
446        }
447    }
448}
449
450impl TryFrom<Element> for Pdu {
451    type Error = AutosarAbstractionError;
452
453    fn try_from(element: Element) -> Result<Self, Self::Error> {
454        match element.element_name() {
455            ElementName::ISignalIPdu => Ok(ISignalIPdu::try_from(element)?.into()),
456            ElementName::NmPdu => Ok(NmPdu::try_from(element)?.into()),
457            ElementName::NPdu => Ok(NPdu::try_from(element)?.into()),
458            ElementName::DcmIPdu => Ok(DcmIPdu::try_from(element)?.into()),
459            ElementName::GeneralPurposePdu => Ok(GeneralPurposePdu::try_from(element)?.into()),
460            ElementName::GeneralPurposeIPdu => Ok(GeneralPurposeIPdu::try_from(element)?.into()),
461            ElementName::ContainerIPdu => Ok(ContainerIPdu::try_from(element)?.into()),
462            ElementName::SecuredIPdu => Ok(SecuredIPdu::try_from(element)?.into()),
463            ElementName::MultiplexedIPdu => Ok(MultiplexedIPdu::try_from(element)?.into()),
464            _ => Err(AutosarAbstractionError::ConversionError {
465                element,
466                dest: "Pdu".to_string(),
467            }),
468        }
469    }
470}
471
472impl IdentifiableAbstractionElement for Pdu {}
473impl AbstractPdu for Pdu {}
474
475//##################################################################
476
477/// Wrapper for all Pdu types. It is used as a return value for functions that can return any Pdu type
478#[derive(Debug, Clone, PartialEq, Eq, Hash)]
479pub enum IPdu {
480    /// The `IPdu` is an `ISignalIPdu`
481    ISignalIPdu(ISignalIPdu),
482    /// The Pdu is a Transport Layer Pdu
483    NPdu(NPdu),
484    /// The `IPdu` is a Diagnostic Communication Management Pdu
485    DcmIPdu(DcmIPdu),
486    /// The `IPdu` is a general purpose Pdu
487    GeneralPurposeIPdu(GeneralPurposeIPdu),
488    /// The `IPdu` is a Container `IPdu`
489    ContainerIPdu(ContainerIPdu),
490    /// The `IPdu` is a secured `IPdu`
491    SecuredIPdu(SecuredIPdu),
492    /// The `IPdu` is a multiplexed `IPdu`
493    MultiplexedIPdu(MultiplexedIPdu),
494}
495
496impl AbstractionElement for IPdu {
497    fn element(&self) -> &Element {
498        match self {
499            IPdu::ISignalIPdu(pdu) => pdu.element(),
500            IPdu::NPdu(pdu) => pdu.element(),
501            IPdu::DcmIPdu(pdu) => pdu.element(),
502            IPdu::GeneralPurposeIPdu(pdu) => pdu.element(),
503            IPdu::ContainerIPdu(pdu) => pdu.element(),
504            IPdu::SecuredIPdu(pdu) => pdu.element(),
505            IPdu::MultiplexedIPdu(pdu) => pdu.element(),
506        }
507    }
508}
509
510impl TryFrom<Element> for IPdu {
511    type Error = AutosarAbstractionError;
512
513    fn try_from(element: Element) -> Result<Self, Self::Error> {
514        match element.element_name() {
515            ElementName::ISignalIPdu => Ok(IPdu::ISignalIPdu(ISignalIPdu::try_from(element)?)),
516            ElementName::NPdu => Ok(IPdu::NPdu(NPdu::try_from(element)?)),
517            ElementName::DcmIPdu => Ok(IPdu::DcmIPdu(DcmIPdu::try_from(element)?)),
518            ElementName::GeneralPurposeIPdu => Ok(IPdu::GeneralPurposeIPdu(GeneralPurposeIPdu::try_from(element)?)),
519            ElementName::ContainerIPdu => Ok(IPdu::ContainerIPdu(ContainerIPdu::try_from(element)?)),
520            ElementName::SecuredIPdu => Ok(IPdu::SecuredIPdu(SecuredIPdu::try_from(element)?)),
521            ElementName::MultiplexedIPdu => Ok(IPdu::MultiplexedIPdu(MultiplexedIPdu::try_from(element)?)),
522            _ => Err(AutosarAbstractionError::ConversionError {
523                element,
524                dest: "IPdu".to_string(),
525            }),
526        }
527    }
528}
529
530impl From<IPdu> for Pdu {
531    fn from(value: IPdu) -> Self {
532        match value {
533            IPdu::ISignalIPdu(pdu) => Pdu::ISignalIPdu(pdu),
534            IPdu::NPdu(pdu) => Pdu::NPdu(pdu),
535            IPdu::DcmIPdu(pdu) => Pdu::DcmIPdu(pdu),
536            IPdu::GeneralPurposeIPdu(pdu) => Pdu::GeneralPurposeIPdu(pdu),
537            IPdu::ContainerIPdu(pdu) => Pdu::ContainerIPdu(pdu),
538            IPdu::SecuredIPdu(pdu) => Pdu::SecuredIPdu(pdu),
539            IPdu::MultiplexedIPdu(pdu) => Pdu::MultiplexedIPdu(pdu),
540        }
541    }
542}
543
544impl IdentifiableAbstractionElement for IPdu {}
545impl AbstractPdu for IPdu {}
546impl AbstractIpdu for IPdu {}
547
548//##################################################################
549
550/// a `PduTriggering` triggers a PDU in a frame or ethernet connection
551#[derive(Debug, Clone, PartialEq, Eq, Hash)]
552pub struct PduTriggering(Element);
553abstraction_element!(PduTriggering, PduTriggering);
554impl IdentifiableAbstractionElement for PduTriggering {}
555
556impl PduTriggering {
557    pub(crate) fn new(pdu: &Pdu, channel: &PhysicalChannel) -> Result<Self, AutosarAbstractionError> {
558        let model = channel.element().model()?;
559        let base_path = channel.element().path()?;
560        let pdu_name = pdu
561            .name()
562            .ok_or(AutosarAbstractionError::InvalidParameter("invalid pdu".to_string()))?;
563        let pt_name = format!("PT_{pdu_name}");
564        let pt_name = make_unique_name(&model, &base_path, &pt_name);
565
566        let triggerings = channel
567            .element()
568            .get_or_create_sub_element(ElementName::PduTriggerings)?;
569        let pt_elem = triggerings.create_named_sub_element(ElementName::PduTriggering, &pt_name)?;
570        pt_elem
571            .create_sub_element(ElementName::IPduRef)?
572            .set_reference_target(pdu.element())?;
573
574        let pt = Self(pt_elem);
575
576        if let Pdu::ISignalIPdu(isignal_ipdu) = pdu {
577            for signal_mapping in isignal_ipdu.mapped_signals() {
578                if let Some(signal) = signal_mapping.signal() {
579                    pt.create_signal_triggering(&signal)?;
580                } else if let Some(signal_group) = signal_mapping.signal_group() {
581                    pt.create_signal_group_triggering(&signal_group)?;
582                }
583            }
584        }
585
586        Ok(pt)
587    }
588
589    /// get the Pdu that is triggered by this pdu triggering
590    #[must_use]
591    pub fn pdu(&self) -> Option<Pdu> {
592        let pdu_elem = self
593            .element()
594            .get_sub_element(ElementName::IPduRef)?
595            .get_reference_target()
596            .ok()?;
597        Pdu::try_from(pdu_elem).ok()
598    }
599
600    /// get the physical channel that contains this pdu triggering
601    pub fn physical_channel(&self) -> Result<PhysicalChannel, AutosarAbstractionError> {
602        let channel_elem = self.element().named_parent()?.ok_or(AutosarDataError::ItemDeleted)?;
603        PhysicalChannel::try_from(channel_elem)
604    }
605
606    /// create an `IPduPort` to connect a `PduTriggering` to an `EcuInstance`
607    pub fn create_pdu_port(
608        &self,
609        ecu: &EcuInstance,
610        direction: CommunicationDirection,
611    ) -> Result<IPduPort, AutosarAbstractionError> {
612        for pdu_port in self.pdu_ports() {
613            if let (Ok(existing_ecu), Some(existing_direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
614                if existing_ecu == *ecu && existing_direction == direction {
615                    return Ok(pdu_port);
616                }
617            }
618        }
619
620        let channel = self.physical_channel()?;
621        let connector = channel
622            .ecu_connector(ecu)
623            .ok_or(AutosarAbstractionError::InvalidParameter(
624                "The ECU is not connected to the channel".to_string(),
625            ))?;
626
627        let name = self.name().ok_or(AutosarDataError::ItemDeleted)?;
628        let suffix = match direction {
629            CommunicationDirection::In => "Rx",
630            CommunicationDirection::Out => "Tx",
631        };
632        let port_name = format!("{name}_{suffix}",);
633        let pp_elem = connector
634            .element()
635            .get_or_create_sub_element(ElementName::EcuCommPortInstances)?
636            .create_named_sub_element(ElementName::IPduPort, &port_name)?;
637        pp_elem
638            .create_sub_element(ElementName::CommunicationDirection)?
639            .set_character_data::<EnumItem>(direction.into())?;
640
641        self.element()
642            .get_or_create_sub_element(ElementName::IPduPortRefs)?
643            .create_sub_element(ElementName::IPduPortRef)?
644            .set_reference_target(&pp_elem)?;
645
646        for st in self.signal_triggerings() {
647            st.connect_to_ecu(ecu, direction)?;
648        }
649
650        Ok(IPduPort(pp_elem))
651    }
652
653    /// create an iterator over the `IPduPorts` that are connected to this `PduTriggering`
654    pub fn pdu_ports(&self) -> impl Iterator<Item = IPduPort> + Send + use<> {
655        self.element()
656            .get_sub_element(ElementName::IPduPortRefs)
657            .into_iter()
658            .flat_map(|ipprefs| ipprefs.sub_elements())
659            .filter_map(|ippref| {
660                ippref
661                    .get_reference_target()
662                    .ok()
663                    .and_then(|elem| IPduPort::try_from(elem).ok())
664            })
665    }
666
667    /// create an iterator over the `ISignalTriggerings` that are triggered by this `PduTriggering`
668    pub fn signal_triggerings(&self) -> impl Iterator<Item = ISignalTriggering> + Send + use<> {
669        self.element()
670            .get_sub_element(ElementName::ISignalTriggerings)
671            .into_iter()
672            .flat_map(|ists| ists.sub_elements())
673            .filter_map(|ist| {
674                ist.get_sub_element(ElementName::ISignalTriggeringRef)
675                    .and_then(|str| str.get_reference_target().ok())
676                    .and_then(|elem| ISignalTriggering::try_from(elem).ok())
677            })
678    }
679
680    /// create a signal triggering for a signal and connect it to this `PduTriggering`
681    pub(crate) fn create_signal_triggering(
682        &self,
683        signal: &ISignal,
684    ) -> Result<ISignalTriggering, AutosarAbstractionError> {
685        let channel = self.physical_channel()?;
686        let st = ISignalTriggering::new(signal, &channel)?;
687        let triggerings = self
688            .element()
689            .get_or_create_sub_element(ElementName::ISignalTriggerings)?;
690        triggerings
691            .create_sub_element(ElementName::ISignalTriggeringRefConditional)?
692            .create_sub_element(ElementName::ISignalTriggeringRef)?
693            .set_reference_target(st.element())?;
694
695        for pdu_port in self.pdu_ports() {
696            if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
697                st.connect_to_ecu(&ecu, direction)?;
698            }
699        }
700
701        Ok(st)
702    }
703
704    /// create a signal triggering for a signal group and connect it to this `PduTriggering`
705    pub(crate) fn create_signal_group_triggering(
706        &self,
707        signal_group: &ISignalGroup,
708    ) -> Result<ISignalTriggering, AutosarAbstractionError> {
709        let channel = self.physical_channel()?;
710        let st = ISignalTriggering::new_group(signal_group, &channel)?;
711        let triggerings = self
712            .element()
713            .get_or_create_sub_element(ElementName::ISignalTriggerings)?;
714        triggerings
715            .create_sub_element(ElementName::ISignalTriggeringRefConditional)?
716            .create_sub_element(ElementName::ISignalTriggeringRef)?
717            .set_reference_target(st.element())?;
718
719        for pdu_port in self.pdu_ports() {
720            if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
721                st.connect_to_ecu(&ecu, direction)?;
722            }
723        }
724
725        Ok(st)
726    }
727}
728
729//##################################################################
730
731/// The `IPduPort` allows an ECU to send or receive a PDU
732#[derive(Debug, Clone, PartialEq, Eq, Hash)]
733pub struct IPduPort(Element);
734abstraction_element!(IPduPort, IPduPort);
735impl IdentifiableAbstractionElement for IPduPort {}
736
737impl IPduPort {
738    /// get the ECU instance that contains this `IPduPort`
739    pub fn ecu(&self) -> Result<EcuInstance, AutosarAbstractionError> {
740        let comm_connector_elem = self.element().named_parent()?.unwrap();
741        let ecu_elem = comm_connector_elem.named_parent()?.unwrap();
742        EcuInstance::try_from(ecu_elem)
743    }
744
745    /// set the communication direction of this `IPduPort`
746    pub fn set_communication_direction(
747        &self,
748        direction: CommunicationDirection,
749    ) -> Result<(), AutosarAbstractionError> {
750        self.element()
751            .get_or_create_sub_element(ElementName::CommunicationDirection)?
752            .set_character_data::<EnumItem>(direction.into())?;
753        Ok(())
754    }
755
756    /// get the communication direction of this `IPduPort`
757    #[must_use]
758    pub fn communication_direction(&self) -> Option<CommunicationDirection> {
759        self.element()
760            .get_sub_element(ElementName::CommunicationDirection)?
761            .character_data()?
762            .enum_value()?
763            .try_into()
764            .ok()
765    }
766}
767
768//##################################################################
769
770/// The collction trigger defines whether a Pdu contributes to the triggering
771/// of the data transmission if Pdu collection is enabled
772#[derive(Debug, Clone, Copy, PartialEq, Eq)]
773pub enum PduCollectionTrigger {
774    /// Pdu will trigger the transmission of the data.
775    Always,
776    /// Pdu will be buffered and will not trigger the transmission of the data
777    Never,
778}
779
780impl From<PduCollectionTrigger> for EnumItem {
781    fn from(value: PduCollectionTrigger) -> Self {
782        match value {
783            PduCollectionTrigger::Always => EnumItem::Always,
784            PduCollectionTrigger::Never => EnumItem::Never,
785        }
786    }
787}
788
789impl TryFrom<EnumItem> for PduCollectionTrigger {
790    type Error = AutosarAbstractionError;
791
792    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
793        match value {
794            EnumItem::Always => Ok(PduCollectionTrigger::Always),
795            EnumItem::Never => Ok(PduCollectionTrigger::Never),
796            _ => Err(AutosarAbstractionError::ValueConversionError {
797                value: value.to_string(),
798                dest: "PduCollectionTrigger".to_string(),
799            }),
800        }
801    }
802}
803
804//##################################################################
805
806#[cfg(test)]
807mod test {
808    use super::*;
809    use crate::{
810        AutosarModelAbstraction, ByteOrder, SystemCategory,
811        communication::{AbstractFrame, AbstractFrameTriggering, CanAddressingMode, CanFrameType, TransferProperty},
812    };
813    use autosar_data::AutosarVersion;
814
815    #[test]
816    fn test_pdus() {
817        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
818        let package = model.get_or_create_package("/pkg").unwrap();
819        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
820
821        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
822        let nm_pdu = system.create_nm_pdu("nm_pdu", &package, 1).unwrap();
823        let n_pdu = system.create_n_pdu("n_pdu", &package, 1).unwrap();
824        let dcm_ipdu = system.create_dcm_ipdu("dcm_ipdu", &package, 1).unwrap();
825        let gp_pdu = system
826            .create_general_purpose_pdu("gp_pdu", &package, 1, GeneralPurposePduCategory::Sd)
827            .unwrap();
828        let gp_ipdu = system
829            .create_general_purpose_ipdu("gp_ipdu", &package, 1, GeneralPurposeIPduCategory::Xcp)
830            .unwrap();
831        let container_ipdu = system
832            .create_container_ipdu(
833                "container_ipdu",
834                &package,
835                1,
836                ContainerIPduHeaderType::ShortHeader,
837                RxAcceptContainedIPdu::AcceptAll,
838            )
839            .unwrap();
840        let secured_ipdu = system
841            .create_secured_ipdu("secured_ipdu", &package, 1, &SecureCommunicationProps::default())
842            .unwrap();
843        let multiplexed_ipdu = system.create_multiplexed_ipdu("multiplexed_ipdu", &package, 1).unwrap();
844
845        assert_eq!(isignal_ipdu.length().unwrap(), 1);
846        assert_eq!(nm_pdu.length().unwrap(), 1);
847        assert_eq!(n_pdu.length().unwrap(), 1);
848        assert_eq!(dcm_ipdu.length().unwrap(), 1);
849        assert_eq!(gp_pdu.length().unwrap(), 1);
850        assert_eq!(gp_ipdu.length().unwrap(), 1);
851        assert_eq!(container_ipdu.length().unwrap(), 1);
852        assert_eq!(secured_ipdu.length().unwrap(), 1);
853        assert_eq!(multiplexed_ipdu.length().unwrap(), 1);
854
855        isignal_ipdu.set_length(2).unwrap();
856        assert_eq!(isignal_ipdu.length().unwrap(), 2);
857
858        let frame = system.create_flexray_frame("frame1", &package, 64).unwrap();
859        frame
860            .map_pdu(&isignal_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
861            .unwrap();
862        frame
863            .map_pdu(&nm_pdu, 8, ByteOrder::MostSignificantByteLast, None)
864            .unwrap();
865        frame
866            .map_pdu(&n_pdu, 16, ByteOrder::MostSignificantByteLast, None)
867            .unwrap();
868        frame
869            .map_pdu(&dcm_ipdu, 24, ByteOrder::MostSignificantByteLast, None)
870            .unwrap();
871        frame
872            .map_pdu(&gp_pdu, 32, ByteOrder::MostSignificantByteLast, None)
873            .unwrap();
874        frame
875            .map_pdu(&gp_ipdu, 40, ByteOrder::MostSignificantByteLast, None)
876            .unwrap();
877        frame
878            .map_pdu(&container_ipdu, 48, ByteOrder::MostSignificantByteLast, None)
879            .unwrap();
880        frame
881            .map_pdu(&secured_ipdu, 56, ByteOrder::MostSignificantByteLast, None)
882            .unwrap();
883        frame
884            .map_pdu(&multiplexed_ipdu, 64, ByteOrder::MostSignificantByteLast, None)
885            .unwrap();
886
887        let mut pdus_iter = frame.mapped_pdus();
888        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "isignal_ipdu");
889        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "nm_pdu");
890        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "n_pdu");
891        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "dcm_ipdu");
892        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "gp_pdu");
893        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "gp_ipdu");
894        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "container_ipdu");
895        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "secured_ipdu");
896        assert_eq!(pdus_iter.next().unwrap().name().unwrap(), "multiplexed_ipdu");
897        assert!(pdus_iter.next().is_none());
898    }
899
900    #[test]
901    fn test_pdu_triggering() {
902        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
903        let package = model.get_or_create_package("/pkg").unwrap();
904        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
905
906        // create an ISignalIPdu with a signal
907        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
908        let syssignal = package.create_system_signal("syssignal").unwrap();
909        let isignal = system.create_isignal("isignal", &package, 1, &syssignal, None).unwrap();
910        isignal_ipdu
911            .map_signal(
912                &isignal,
913                0,
914                ByteOrder::MostSignificantByteLast,
915                None,
916                TransferProperty::Triggered,
917            )
918            .unwrap();
919        // create an ISignalGroup with a second signal
920        let syssignal_group = package.create_system_signal_group("syssignal_group").unwrap();
921        let isignal_group = system
922            .create_isignal_group("isignal_group", &package, &syssignal_group)
923            .unwrap();
924        let syssignal2 = package.create_system_signal("syssignal2").unwrap();
925        let isignal2 = system
926            .create_isignal("isignal2", &package, 1, &syssignal2, None)
927            .unwrap();
928        isignal_ipdu.map_signal_group(&isignal_group).unwrap();
929        isignal_ipdu
930            .map_signal(
931                &isignal2,
932                1,
933                ByteOrder::MostSignificantByteLast,
934                None,
935                TransferProperty::Triggered,
936            )
937            .unwrap();
938
939        // create a frame and map the ISignalIPdu to it
940        let can_cluster = system.create_can_cluster("Cluster", &package, None).unwrap();
941        let channel = can_cluster.create_physical_channel("Channel").unwrap();
942        let frame = system.create_can_frame("frame", &package, 8).unwrap();
943        let frame_triggering = channel
944            .trigger_frame(&frame, 0x123, CanAddressingMode::Standard, CanFrameType::Can20)
945            .unwrap();
946        let _mapping = frame
947            .map_pdu(&isignal_ipdu, 0, ByteOrder::MostSignificantByteLast, None)
948            .unwrap();
949
950        // create an EcuInstance, and connect it to the channel. The frame is reeived by the ECU
951        let ecu = system.create_ecu_instance("ecu", &package).unwrap();
952        let controller = ecu.create_can_communication_controller("controller").unwrap();
953        controller.connect_physical_channel("connection", &channel).unwrap();
954        frame_triggering
955            .connect_to_ecu(&ecu, CommunicationDirection::In)
956            .unwrap();
957
958        let pdu_triggering = frame_triggering.pdu_triggerings().next().unwrap();
959        assert_eq!(pdu_triggering.pdu_ports().count(), 1);
960        assert_eq!(pdu_triggering.signal_triggerings().count(), 3); // one for each signal, and another for the signal group
961
962        let pdu_port = pdu_triggering.pdu_ports().next().unwrap();
963        assert_eq!(pdu_port.ecu().unwrap().name().unwrap(), "ecu");
964        assert_eq!(pdu_port.communication_direction().unwrap(), CommunicationDirection::In);
965        pdu_port
966            .set_communication_direction(CommunicationDirection::Out)
967            .unwrap();
968        assert_eq!(pdu_port.communication_direction().unwrap(), CommunicationDirection::Out);
969        pdu_port.set_name("new_name").unwrap();
970        assert_eq!(pdu_port.name().unwrap(), "new_name");
971    }
972
973    #[test]
974    fn general_purpose_pdu() {
975        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
976        let package = model.get_or_create_package("/pkg").unwrap();
977        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
978
979        let gp_pdu1 = system
980            .create_general_purpose_pdu("gp_pdu1", &package, 1, GeneralPurposePduCategory::Sd)
981            .unwrap();
982        assert_eq!(gp_pdu1.category().unwrap(), GeneralPurposePduCategory::Sd);
983
984        let gp_pdu2 = system
985            .create_general_purpose_pdu("gp_pdu2", &package, 1, GeneralPurposePduCategory::GlobalTime)
986            .unwrap();
987        assert_eq!(gp_pdu2.category().unwrap(), GeneralPurposePduCategory::GlobalTime);
988
989        let gp_pdu3 = system
990            .create_general_purpose_pdu("gp_pdu3", &package, 1, GeneralPurposePduCategory::DoIp)
991            .unwrap();
992        assert_eq!(gp_pdu3.category().unwrap(), GeneralPurposePduCategory::DoIp);
993
994        // conversion of category to string and back
995        assert_eq!(
996            GeneralPurposePduCategory::from_str("SD").unwrap(),
997            GeneralPurposePduCategory::Sd
998        );
999        assert_eq!(
1000            GeneralPurposePduCategory::from_str("GLOBAL_TIME").unwrap(),
1001            GeneralPurposePduCategory::GlobalTime
1002        );
1003        assert_eq!(
1004            GeneralPurposePduCategory::from_str("DOIP").unwrap(),
1005            GeneralPurposePduCategory::DoIp
1006        );
1007        assert!(GeneralPurposePduCategory::from_str("invalid").is_err());
1008    }
1009
1010    #[test]
1011    fn create_general_purpose_ipdu() {
1012        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1013        let package = model.get_or_create_package("/pkg").unwrap();
1014        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1015
1016        let gp_ipdu1 = system
1017            .create_general_purpose_ipdu("gp_ipdu1", &package, 1, GeneralPurposeIPduCategory::Xcp)
1018            .unwrap();
1019        assert_eq!(gp_ipdu1.category().unwrap(), GeneralPurposeIPduCategory::Xcp);
1020
1021        let gp_ipdu2 = system
1022            .create_general_purpose_ipdu("gp_ipdu2", &package, 1, GeneralPurposeIPduCategory::SomeipSegmentedIpdu)
1023            .unwrap();
1024        assert_eq!(
1025            gp_ipdu2.category().unwrap(),
1026            GeneralPurposeIPduCategory::SomeipSegmentedIpdu
1027        );
1028
1029        let gp_ipdu3 = system
1030            .create_general_purpose_ipdu("gp_ipdu3", &package, 1, GeneralPurposeIPduCategory::Dlt)
1031            .unwrap();
1032        assert_eq!(gp_ipdu3.category().unwrap(), GeneralPurposeIPduCategory::Dlt);
1033
1034        // conversion of category to string and back
1035        assert_eq!(
1036            GeneralPurposeIPduCategory::from_str("XCP").unwrap(),
1037            GeneralPurposeIPduCategory::Xcp
1038        );
1039        assert_eq!(
1040            GeneralPurposeIPduCategory::from_str("SOMEIP_SEGMENTED_IPDU").unwrap(),
1041            GeneralPurposeIPduCategory::SomeipSegmentedIpdu
1042        );
1043        assert_eq!(
1044            GeneralPurposeIPduCategory::from_str("DLT").unwrap(),
1045            GeneralPurposeIPduCategory::Dlt
1046        );
1047        assert!(GeneralPurposeIPduCategory::from_str("invalid").is_err());
1048    }
1049
1050    #[test]
1051    fn ipdu() {
1052        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1053        let package = model.get_or_create_package("/pkg").unwrap();
1054        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
1055
1056        let isignal_ipdu = system.create_isignal_ipdu("isignal_ipdu", &package, 1).unwrap();
1057        let n_pdu = system.create_n_pdu("n_pdu", &package, 1).unwrap();
1058        let dcm_ipdu = system.create_dcm_ipdu("dcm_ipdu", &package, 1).unwrap();
1059        let gp_ipdu = system
1060            .create_general_purpose_ipdu("gp_ipdu", &package, 1, GeneralPurposeIPduCategory::Xcp)
1061            .unwrap();
1062        let container_ipdu = system
1063            .create_container_ipdu(
1064                "container_ipdu",
1065                &package,
1066                1,
1067                ContainerIPduHeaderType::LongHeader,
1068                RxAcceptContainedIPdu::AcceptConfigured,
1069            )
1070            .unwrap();
1071        let secured_ipdu = system
1072            .create_secured_ipdu("secured_ipdu", &package, 1, &SecureCommunicationProps::default())
1073            .unwrap();
1074        let multiplexed_ipdu = system.create_multiplexed_ipdu("multiplexed_ipdu", &package, 1).unwrap();
1075
1076        let ipdu: IPdu = isignal_ipdu.clone().into();
1077        assert_eq!(ipdu.element(), isignal_ipdu.element());
1078        assert!(matches!(ipdu, IPdu::ISignalIPdu(_)));
1079
1080        // NmPdu is not an ipdu
1081
1082        let ipdu: IPdu = n_pdu.clone().into();
1083        assert_eq!(ipdu.element(), n_pdu.element());
1084        assert!(matches!(ipdu, IPdu::NPdu(_)));
1085
1086        let ipdu: IPdu = dcm_ipdu.clone().into();
1087        assert_eq!(ipdu.element(), dcm_ipdu.element());
1088        assert!(matches!(ipdu, IPdu::DcmIPdu(_)));
1089
1090        let ipdu: IPdu = n_pdu.clone().into();
1091        assert_eq!(ipdu.element(), n_pdu.element());
1092        assert!(matches!(ipdu, IPdu::NPdu(_)));
1093
1094        // GeneralPurposePdu is not an ipdu
1095
1096        let ipdu: IPdu = gp_ipdu.clone().into();
1097        assert_eq!(ipdu.element(), gp_ipdu.element());
1098        assert!(matches!(ipdu, IPdu::GeneralPurposeIPdu(_)));
1099
1100        let ipdu: IPdu = container_ipdu.clone().into();
1101        assert_eq!(ipdu.element(), container_ipdu.element());
1102        assert!(matches!(ipdu, IPdu::ContainerIPdu(_)));
1103
1104        let ipdu: IPdu = secured_ipdu.clone().into();
1105        assert_eq!(ipdu.element(), secured_ipdu.element());
1106        assert!(matches!(ipdu, IPdu::SecuredIPdu(_)));
1107
1108        let ipdu: IPdu = multiplexed_ipdu.clone().into();
1109        assert_eq!(ipdu.element(), multiplexed_ipdu.element());
1110        assert!(matches!(ipdu, IPdu::MultiplexedIPdu(_)));
1111
1112        // any Ipdu can be converted to a Pdu
1113        let pdu: Pdu = ipdu.clone().into();
1114        assert_eq!(pdu.element(), ipdu.element());
1115    }
1116}