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#[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 pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
38 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 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 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 #[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 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 #[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 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 #[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 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 #[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 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
198pub enum ContainerIPduHeaderType {
199 LongHeader,
201 NoHeader,
203 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
237pub enum RxAcceptContainedIPdu {
238 AcceptAll,
240 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
272pub enum ContainerIPduTrigger {
273 DefaultTrigger,
275 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#[derive(Debug, Clone, PartialEq)]
308pub struct ContainedIPduProps {
309 pub collection_semantics: Option<ContainedIPduCollectionSemantics>,
311 pub header_id_long: Option<u32>,
313 pub header_id_short: Option<u32>, pub offset: Option<u32>,
317 pub priority: Option<u8>,
319 pub timeout: Option<f64>,
321 pub trigger: Option<PduCollectionTrigger>,
323 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
421pub enum ContainedIPduCollectionSemantics {
422 LastIsBest,
424 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#[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}