autosar_data_abstraction/communication/pdu/
container_ipdu.rs

1use crate::communication::{AbstractIpdu, AbstractPdu, AbstractPhysicalChannel, IPdu, Pdu, PduToFrameMapping};
2use crate::{
3    AbstractionElement, ArPackage, AutosarAbstractionError, IdentifiableAbstractionElement, abstraction_element,
4    get_reference_parents,
5};
6use autosar_data::{Element, ElementName, EnumItem};
7
8use super::{PduCollectionTrigger, PduTriggering};
9
10//##################################################################
11
12/// Several `IPdus` can be collected in one `ContainerIPdu` based on the headerType
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub struct ContainerIPdu(Element);
15abstraction_element!(ContainerIPdu, ContainerIPdu);
16impl IdentifiableAbstractionElement for ContainerIPdu {}
17
18impl ContainerIPdu {
19    pub(crate) fn new(
20        name: &str,
21        package: &ArPackage,
22        length: u32,
23        header_type: ContainerIPduHeaderType,
24        rx_accept: RxAcceptContainedIPdu,
25    ) -> Result<Self, AutosarAbstractionError> {
26        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
27        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::ContainerIPdu, name)?;
28        let container_ipdu = Self(elem_pdu);
29        container_ipdu.set_length(length)?;
30        container_ipdu.set_header_type(header_type)?;
31        container_ipdu.set_rx_accept_contained_ipdu(rx_accept)?;
32
33        Ok(container_ipdu)
34    }
35
36    /// remove this `ContainerIPdu` from the model
37    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
38        // remove all triggerings of this PDU
39        for pdu_triggering in self.pdu_triggerings() {
40            let _ = pdu_triggering.element().remove_sub_element_kind(ElementName::IPduRef);
41            let _ = pdu_triggering.remove(deep);
42        }
43
44        // remove all contained IPdu triggerings
45        for ipdu_triggering in self.contained_ipdu_triggerings() {
46            let _ = ipdu_triggering.remove(deep);
47        }
48
49        let ref_parents = get_reference_parents(self.element())?;
50
51        AbstractionElement::remove(self, deep)?;
52
53        for (named_parent, _parent) in ref_parents {
54            if named_parent.element_name() == ElementName::PduToFrameMapping
55                && let Ok(pdu_to_frame_mapping) = PduToFrameMapping::try_from(named_parent)
56            {
57                pdu_to_frame_mapping.remove(deep)?;
58            }
59        }
60
61        Ok(())
62    }
63
64    /// set the header type of this `ContainerIPdu`
65    pub fn set_header_type(&self, header_type: ContainerIPduHeaderType) -> Result<(), AutosarAbstractionError> {
66        self.element()
67            .get_or_create_sub_element(ElementName::HeaderType)?
68            .set_character_data::<EnumItem>(header_type.into())?;
69        Ok(())
70    }
71
72    /// get the header type of this `ContainerIPdu`
73    #[must_use]
74    pub fn header_type(&self) -> Option<ContainerIPduHeaderType> {
75        self.element()
76            .get_sub_element(ElementName::HeaderType)?
77            .character_data()?
78            .enum_value()?
79            .try_into()
80            .ok()
81    }
82
83    /// set the rx accept of this `ContainerIPdu`
84    pub fn set_rx_accept_contained_ipdu(
85        &self,
86        rx_accept: RxAcceptContainedIPdu,
87    ) -> Result<(), AutosarAbstractionError> {
88        self.element()
89            .get_or_create_sub_element(ElementName::RxAcceptContainedIPdu)?
90            .set_character_data::<EnumItem>(rx_accept.into())?;
91        Ok(())
92    }
93
94    /// get the rx accept of this `ContainerIPdu`
95    #[must_use]
96    pub fn rx_accept_contained_ipdu(&self) -> Option<RxAcceptContainedIPdu> {
97        self.element()
98            .get_sub_element(ElementName::RxAcceptContainedIPdu)?
99            .character_data()?
100            .enum_value()?
101            .try_into()
102            .ok()
103    }
104
105    /// set the container timeout of this `ContainerIPdu`
106    pub fn set_container_timeout(&self, timeout: Option<f64>) -> Result<(), AutosarAbstractionError> {
107        if let Some(timeout) = timeout {
108            self.element()
109                .get_or_create_sub_element(ElementName::ContainerTimeout)?
110                .set_character_data(timeout)?;
111        } else {
112            let _ = self.element().remove_sub_element_kind(ElementName::ContainerTimeout);
113        }
114        Ok(())
115    }
116
117    /// get the container timeout of this `ContainerIPdu`
118    #[must_use]
119    pub fn container_timeout(&self) -> Option<f64> {
120        self.element()
121            .get_sub_element(ElementName::ContainerTimeout)?
122            .character_data()?
123            .parse_float()
124    }
125
126    /// set the container trigger of this `ContainerIPdu`
127    pub fn set_container_trigger(&self, trigger: Option<ContainerIPduTrigger>) -> Result<(), AutosarAbstractionError> {
128        if let Some(trigger) = trigger {
129            self.element()
130                .get_or_create_sub_element(ElementName::ContainerTrigger)?
131                .set_character_data::<EnumItem>(trigger.into())?;
132        } else {
133            let _ = self.element().remove_sub_element_kind(ElementName::ContainerTrigger);
134        }
135        Ok(())
136    }
137
138    /// get the container trigger of this `ContainerIPdu`
139    #[must_use]
140    pub fn container_trigger(&self) -> Option<ContainerIPduTrigger> {
141        self.element()
142            .get_sub_element(ElementName::ContainerTrigger)?
143            .character_data()?
144            .enum_value()?
145            .try_into()
146            .ok()
147    }
148
149    /// map an `IPdu` to this `ContainerIPdu`, and create a `PduTriggering` for it in the physical channel
150    pub fn map_ipdu<T: AbstractIpdu, U: AbstractPhysicalChannel>(
151        &self,
152        ipdu: &T,
153        physical_channel: &U,
154    ) -> Result<PduTriggering, AutosarAbstractionError> {
155        let contained_pdu_triggering_refs_elem = self
156            .element()
157            .get_or_create_sub_element(ElementName::ContainedPduTriggeringRefs)?;
158        let pdu_triggering = PduTriggering::new(&ipdu.clone().into(), &physical_channel.clone().into())?;
159
160        contained_pdu_triggering_refs_elem
161            .create_sub_element(ElementName::ContainedPduTriggeringRef)?
162            .set_reference_target(pdu_triggering.element())?;
163
164        Ok(pdu_triggering)
165    }
166
167    /// iterate over all contained `IPdu` triggerings
168    pub fn contained_ipdu_triggerings(&self) -> impl Iterator<Item = PduTriggering> + Send + use<> {
169        self.element()
170            .get_sub_element(ElementName::ContainedPduTriggeringRefs)
171            .into_iter()
172            .flat_map(|triggerings| triggerings.sub_elements())
173            .filter_map(|triggering_ref| triggering_ref.get_reference_target().ok())
174            .filter_map(|triggering| PduTriggering::try_from(triggering).ok())
175    }
176}
177
178impl AbstractPdu for ContainerIPdu {}
179
180impl AbstractIpdu for ContainerIPdu {}
181
182impl From<ContainerIPdu> for Pdu {
183    fn from(value: ContainerIPdu) -> Self {
184        Pdu::ContainerIPdu(value)
185    }
186}
187
188impl From<ContainerIPdu> for IPdu {
189    fn from(value: ContainerIPdu) -> Self {
190        IPdu::ContainerIPdu(value)
191    }
192}
193
194//##################################################################
195
196/// The header type of a `ContainerIPdu`
197#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
198pub enum ContainerIPduHeaderType {
199    /// Header size is 64 bit: Header id is 32 bit, dlc is 32 bit
200    LongHeader,
201    /// no header is used, the locations of the contained PDUs are fixed
202    NoHeader,
203    /// Header size is 32 bit: Header id is 24 bit, dlc is 8 bit
204    ShortHeader,
205}
206
207impl From<ContainerIPduHeaderType> for EnumItem {
208    fn from(value: ContainerIPduHeaderType) -> Self {
209        match value {
210            ContainerIPduHeaderType::LongHeader => EnumItem::LongHeader,
211            ContainerIPduHeaderType::NoHeader => EnumItem::NoHeader,
212            ContainerIPduHeaderType::ShortHeader => EnumItem::ShortHeader,
213        }
214    }
215}
216
217impl TryFrom<EnumItem> for ContainerIPduHeaderType {
218    type Error = AutosarAbstractionError;
219
220    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
221        match value {
222            EnumItem::LongHeader => Ok(ContainerIPduHeaderType::LongHeader),
223            EnumItem::NoHeader => Ok(ContainerIPduHeaderType::NoHeader),
224            EnumItem::ShortHeader => Ok(ContainerIPduHeaderType::ShortHeader),
225            _ => Err(AutosarAbstractionError::ValueConversionError {
226                value: value.to_string(),
227                dest: "ContainerIPduHeaderType".to_string(),
228            }),
229        }
230    }
231}
232
233//##################################################################
234
235/// The `RxAcceptContainedIPdu` enum defines whether a fixed set of contained `IPdus` is accepted or all contained `IPdus`
236#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
237pub enum RxAcceptContainedIPdu {
238    /// All contained `IPdus` are accepted
239    AcceptAll,
240    /// Only the configured contained `IPdus` are accepted
241    AcceptConfigured,
242}
243
244impl From<RxAcceptContainedIPdu> for EnumItem {
245    fn from(value: RxAcceptContainedIPdu) -> Self {
246        match value {
247            RxAcceptContainedIPdu::AcceptAll => EnumItem::AcceptAll,
248            RxAcceptContainedIPdu::AcceptConfigured => EnumItem::AcceptConfigured,
249        }
250    }
251}
252
253impl TryFrom<EnumItem> for RxAcceptContainedIPdu {
254    type Error = AutosarAbstractionError;
255
256    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
257        match value {
258            EnumItem::AcceptAll => Ok(RxAcceptContainedIPdu::AcceptAll),
259            EnumItem::AcceptConfigured => Ok(RxAcceptContainedIPdu::AcceptConfigured),
260            _ => Err(AutosarAbstractionError::ValueConversionError {
261                value: value.to_string(),
262                dest: "RxAcceptContainedIPdu".to_string(),
263            }),
264        }
265    }
266}
267
268//##################################################################
269
270/// Defines when the transmission of the `ContainerIPdu` shall be requested
271#[derive(Debug, Clone, PartialEq, Eq, Hash)]
272pub enum ContainerIPduTrigger {
273    /// transmission of the `ContainerIPdu` shall be requested when the default trigger conditions apply
274    DefaultTrigger,
275    /// transmission of the `ContainerIPdu` shall be requested right after the first Contained
276    /// `IPdu` was put into the `ContainerIPdu`
277    FirstContainedTrigger,
278}
279
280impl From<ContainerIPduTrigger> for EnumItem {
281    fn from(value: ContainerIPduTrigger) -> Self {
282        match value {
283            ContainerIPduTrigger::DefaultTrigger => EnumItem::DefaultTrigger,
284            ContainerIPduTrigger::FirstContainedTrigger => EnumItem::FirstContainedTrigger,
285        }
286    }
287}
288
289impl TryFrom<EnumItem> for ContainerIPduTrigger {
290    type Error = AutosarAbstractionError;
291
292    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
293        match value {
294            EnumItem::DefaultTrigger => Ok(ContainerIPduTrigger::DefaultTrigger),
295            EnumItem::FirstContainedTrigger => Ok(ContainerIPduTrigger::FirstContainedTrigger),
296            _ => Err(AutosarAbstractionError::ValueConversionError {
297                value: value.to_string(),
298                dest: "ContainerIPduTrigger".to_string(),
299            }),
300        }
301    }
302}
303
304//##################################################################
305
306/// Properties for an `IPdu` that is transmitted in a container `IPdu`
307#[derive(Debug, Clone, PartialEq)]
308pub struct ContainedIPduProps {
309    /// collection semantics: `LastIsBest` or `Queued`
310    pub collection_semantics: Option<ContainedIPduCollectionSemantics>,
311    /// header id of the contained `IPdu`, used when the header type is `LongHeader`
312    pub header_id_long: Option<u32>,
313    /// header id of the contained `IPdu`, used when the header type is `ShortHeader`
314    pub header_id_short: Option<u32>, // 24 bit
315    /// offset of the contained `IPdu` in the container `IPdu`, used when the header type is `NoHeader`
316    pub offset: Option<u32>,
317    /// priority of the contained `IPdu`. 255: lowest, 0: highest
318    pub priority: Option<u8>,
319    /// sender timeout. Ignored on the receiver side
320    pub timeout: Option<f64>,
321    /// defines whether the contained `IPdu` triggers transmission of the container `IPdu`
322    pub trigger: Option<PduCollectionTrigger>,
323    /// update indication bit position of the contained `IPdu`
324    pub update_indication_bit_position: Option<u32>,
325}
326
327impl ContainedIPduProps {
328    pub(crate) fn get_props(parent_elem: &Element) -> Option<Self> {
329        let props_elem = parent_elem.get_sub_element(ElementName::ContainedIPduProps)?;
330        let collection_semantics = props_elem
331            .get_sub_element(ElementName::CollectionSemantics)
332            .and_then(|elem| elem.character_data()?.enum_value()?.try_into().ok());
333        let header_id_long = props_elem
334            .get_sub_element(ElementName::HeaderIdLongHeader)
335            .and_then(|elem| elem.character_data()?.parse_integer());
336        let header_id_short = props_elem
337            .get_sub_element(ElementName::HeaderIdShortHeader)
338            .and_then(|elem| elem.character_data()?.parse_integer());
339        let offset = props_elem
340            .get_sub_element(ElementName::Offset)
341            .and_then(|elem| elem.character_data()?.parse_integer());
342        let priority = props_elem
343            .get_sub_element(ElementName::Priority)
344            .and_then(|elem| elem.character_data()?.parse_integer());
345        let timeout = props_elem
346            .get_sub_element(ElementName::Timeout)
347            .and_then(|elem| elem.character_data()?.parse_float());
348        let trigger = props_elem
349            .get_sub_element(ElementName::Trigger)
350            .and_then(|elem| elem.character_data()?.enum_value()?.try_into().ok());
351        let update_indication_bit_position = props_elem
352            .get_sub_element(ElementName::UpdateIndicationBitPosition)
353            .and_then(|elem| elem.character_data()?.parse_integer());
354
355        Some(Self {
356            collection_semantics,
357            header_id_long,
358            header_id_short,
359            offset,
360            priority,
361            timeout,
362            trigger,
363            update_indication_bit_position,
364        })
365    }
366
367    pub(crate) fn set_props(parent_elem: &Element, props: Option<&Self>) -> Result<(), AutosarAbstractionError> {
368        if let Some(props) = props {
369            let props_elem = parent_elem.get_or_create_sub_element(ElementName::ContainedIPduProps)?;
370            if let Some(collection_semantics) = props.collection_semantics {
371                props_elem
372                    .get_or_create_sub_element(ElementName::CollectionSemantics)?
373                    .set_character_data::<EnumItem>(collection_semantics.into())?;
374            }
375            if let Some(header_id_long) = props.header_id_long {
376                props_elem
377                    .get_or_create_sub_element(ElementName::HeaderIdLongHeader)?
378                    .set_character_data(header_id_long as u64)?;
379            }
380            if let Some(header_id_short) = props.header_id_short {
381                props_elem
382                    .get_or_create_sub_element(ElementName::HeaderIdShortHeader)?
383                    .set_character_data(header_id_short as u64)?;
384            }
385            if let Some(offset) = props.offset {
386                props_elem
387                    .get_or_create_sub_element(ElementName::Offset)?
388                    .set_character_data(offset as u64)?;
389            }
390            if let Some(priority) = props.priority {
391                props_elem
392                    .get_or_create_sub_element(ElementName::Priority)?
393                    .set_character_data(priority as u64)?;
394            }
395            if let Some(timeout) = props.timeout {
396                props_elem
397                    .get_or_create_sub_element(ElementName::Timeout)?
398                    .set_character_data(timeout)?;
399            }
400            if let Some(trigger) = props.trigger {
401                props_elem
402                    .get_or_create_sub_element(ElementName::Trigger)?
403                    .set_character_data::<EnumItem>(trigger.into())?;
404            }
405            if let Some(update_indication_bit_position) = props.update_indication_bit_position {
406                props_elem
407                    .get_or_create_sub_element(ElementName::UpdateIndicationBitPosition)?
408                    .set_character_data(update_indication_bit_position as u64)?;
409            }
410        } else {
411            let _ = parent_elem.remove_sub_element_kind(ElementName::ContainedIPduProps);
412        }
413        Ok(())
414    }
415}
416
417//##################################################################
418
419/// collection semantics for the `ContainedIPdu`
420#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
421pub enum ContainedIPduCollectionSemantics {
422    /// The `ContainedIPdu` data will be fetched via `TriggerTransmit` just before the transmission executes.
423    LastIsBest,
424    /// The `ContainedIPdu` data will instantly be stored to the `ContainerIPdu` in the context of the Transmit call
425    Queued,
426}
427
428impl From<ContainedIPduCollectionSemantics> for EnumItem {
429    fn from(value: ContainedIPduCollectionSemantics) -> Self {
430        match value {
431            ContainedIPduCollectionSemantics::LastIsBest => EnumItem::LastIsBest,
432            ContainedIPduCollectionSemantics::Queued => EnumItem::Queued,
433        }
434    }
435}
436
437impl TryFrom<EnumItem> for ContainedIPduCollectionSemantics {
438    type Error = AutosarAbstractionError;
439
440    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
441        match value {
442            EnumItem::LastIsBest => Ok(ContainedIPduCollectionSemantics::LastIsBest),
443            EnumItem::Queued => Ok(ContainedIPduCollectionSemantics::Queued),
444            _ => Err(AutosarAbstractionError::ValueConversionError {
445                value: value.to_string(),
446                dest: "ContainedIPduCollectionSemantics".to_string(),
447            }),
448        }
449    }
450}
451
452//##################################################################
453
454#[cfg(test)]
455mod test {
456    use super::*;
457    use crate::{
458        AutosarModelAbstraction, SystemCategory,
459        communication::{FlexrayChannelName, FlexrayClusterSettings},
460    };
461    use autosar_data::AutosarVersion;
462
463    #[test]
464    fn test_container_ipdu() {
465        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
466        let package = model.get_or_create_package("/pkg").unwrap();
467        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
468        let flexray_cluster = system
469            .create_flexray_cluster("FlxCluster", &package, &FlexrayClusterSettings::new())
470            .unwrap();
471        let flexray_channel = flexray_cluster
472            .create_physical_channel("FlxChannel", FlexrayChannelName::A)
473            .unwrap();
474
475        let container_ipdu = system
476            .create_container_ipdu(
477                "container_ipdu",
478                &package,
479                64,
480                ContainerIPduHeaderType::ShortHeader,
481                RxAcceptContainedIPdu::AcceptAll,
482            )
483            .unwrap();
484        assert_eq!(
485            container_ipdu.header_type().unwrap(),
486            ContainerIPduHeaderType::ShortHeader
487        );
488        assert_eq!(
489            container_ipdu.rx_accept_contained_ipdu().unwrap(),
490            RxAcceptContainedIPdu::AcceptAll
491        );
492
493        container_ipdu
494            .set_header_type(ContainerIPduHeaderType::LongHeader)
495            .unwrap();
496        assert_eq!(
497            container_ipdu.header_type().unwrap(),
498            ContainerIPduHeaderType::LongHeader
499        );
500
501        container_ipdu
502            .set_rx_accept_contained_ipdu(RxAcceptContainedIPdu::AcceptConfigured)
503            .unwrap();
504        assert_eq!(
505            container_ipdu.rx_accept_contained_ipdu().unwrap(),
506            RxAcceptContainedIPdu::AcceptConfigured
507        );
508
509        container_ipdu.set_container_timeout(Some(0.1)).unwrap();
510        assert_eq!(container_ipdu.container_timeout().unwrap(), 0.1);
511        container_ipdu.set_container_timeout(None).unwrap();
512        assert_eq!(container_ipdu.container_timeout(), None);
513
514        container_ipdu
515            .set_container_trigger(Some(ContainerIPduTrigger::DefaultTrigger))
516            .unwrap();
517        assert_eq!(
518            container_ipdu.container_trigger().unwrap(),
519            ContainerIPduTrigger::DefaultTrigger
520        );
521        container_ipdu
522            .set_container_trigger(Some(ContainerIPduTrigger::FirstContainedTrigger))
523            .unwrap();
524        assert_eq!(
525            container_ipdu.container_trigger().unwrap(),
526            ContainerIPduTrigger::FirstContainedTrigger
527        );
528        container_ipdu.set_container_trigger(None).unwrap();
529        assert_eq!(container_ipdu.container_trigger(), None);
530
531        let contained_ipdu = system.create_isignal_ipdu("ISignalIpdu", &package, 8).unwrap();
532        let contained_props = ContainedIPduProps {
533            collection_semantics: Some(ContainedIPduCollectionSemantics::LastIsBest),
534            header_id_long: Some(0x12345678),
535            header_id_short: Some(0x123456),
536            offset: Some(0x10),
537            priority: Some(0x10),
538            timeout: Some(0.1),
539            trigger: Some(PduCollectionTrigger::Always),
540            update_indication_bit_position: Some(0x10),
541        };
542        contained_ipdu.set_contained_ipdu_props(Some(&contained_props)).unwrap();
543        assert_eq!(contained_ipdu.contained_ipdu_props().unwrap(), contained_props);
544        contained_ipdu.set_contained_ipdu_props(None).unwrap();
545        assert_eq!(contained_ipdu.contained_ipdu_props(), None);
546
547        let pdu_triggering = container_ipdu.map_ipdu(&contained_ipdu, &flexray_channel).unwrap();
548        assert_eq!(container_ipdu.contained_ipdu_triggerings().count(), 1);
549        assert_eq!(container_ipdu.contained_ipdu_triggerings().next(), Some(pdu_triggering));
550    }
551}