autosar_data_abstraction/communication/pdu/
container_ipdu.rs1use 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#[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 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 #[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 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 #[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 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 #[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 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 #[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 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
169pub enum ContainerIPduHeaderType {
170 LongHeader,
172 NoHeader,
174 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
208pub enum RxAcceptContainedIPdu {
209 AcceptAll,
211 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
243pub enum ContainerIPduTrigger {
244 DefaultTrigger,
246 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#[derive(Debug, Clone, PartialEq)]
279pub struct ContainedIPduProps {
280 pub collection_semantics: Option<ContainedIPduCollectionSemantics>,
282 pub header_id_long: Option<u32>,
284 pub header_id_short: Option<u32>, pub offset: Option<u32>,
288 pub priority: Option<u8>,
290 pub timeout: Option<f64>,
292 pub trigger: Option<PduCollectionTrigger>,
294 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
392pub enum ContainedIPduCollectionSemantics {
393 LastIsBest,
395 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#[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}