autosar_data_abstraction/communication/pdu/
container_ipdu.rs

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