autosar_data_abstraction/communication/frame/
mod.rs

1use crate::communication::{
2    AbstractPdu, AbstractPhysicalChannel, CommunicationDirection, Pdu, PduTriggering, PhysicalChannel,
3};
4use crate::{
5    AbstractionElement, AutosarAbstractionError, ByteOrder, EcuInstance, IdentifiableAbstractionElement,
6    abstraction_element, is_used_system_element, make_unique_name,
7};
8
9mod can;
10mod flexray;
11// ethernet does not use frames. PDUs are transmitted over SomeIp or static SocketConnections
12mod lin;
13
14use autosar_data::{AutosarDataError, Element, ElementName, EnumItem};
15pub use can::*;
16pub use flexray::*;
17pub use lin::*;
18
19//##################################################################
20
21/// A trait for all frame types
22pub trait AbstractFrame: AbstractionElement {
23    /// The bus-specific frame triggering type
24    type FrameTriggeringType: AbstractFrameTriggering;
25
26    /// returns an iterator over all PDUs in the frame
27    fn mapped_pdus(&self) -> impl Iterator<Item = PduToFrameMapping> + Send + use<Self> {
28        self.element()
29            .get_sub_element(ElementName::PduToFrameMappings)
30            .into_iter()
31            .flat_map(|elem| elem.sub_elements())
32            .filter_map(|elem| PduToFrameMapping::try_from(elem).ok())
33    }
34
35    /// Iterator over all [`FrameTriggering`]s using this frame
36    fn frame_triggerings(&self) -> Vec<Self::FrameTriggeringType>;
37
38    /// map a PDU to the frame
39    fn map_pdu<T: AbstractPdu>(
40        &self,
41        gen_pdu: &T,
42        start_position: u32,
43        byte_order: ByteOrder,
44        update_bit: Option<u32>,
45    ) -> Result<PduToFrameMapping, AutosarAbstractionError>;
46
47    /// set the length of the frame
48    fn set_length(&self, length: u32) -> Result<(), AutosarAbstractionError> {
49        self.element()
50            .get_or_create_sub_element(ElementName::FrameLength)?
51            .set_character_data(u64::from(length))?;
52        Ok(())
53    }
54
55    /// get the length of the frame
56    fn length(&self) -> Option<u32> {
57        self.element()
58            .get_sub_element(ElementName::FrameLength)
59            .and_then(|elem| elem.character_data())
60            .and_then(|cdata| cdata.parse_integer())
61    }
62}
63
64//##################################################################
65
66/// A wrapper for CAN and `FlexRay` frames (Ethernet does not use frames)
67#[derive(Debug, Clone, PartialEq, Eq)]
68#[non_exhaustive]
69pub enum Frame {
70    /// The frame is a CAN frame
71    Can(CanFrame),
72    /// The frame is a `FlexRay` frame
73    Flexray(FlexrayFrame),
74    /// The frame is a `LIN` frame
75    Lin(LinFrame),
76}
77
78impl AbstractionElement for Frame {
79    fn element(&self) -> &autosar_data::Element {
80        match self {
81            Self::Can(cf) => cf.element(),
82            Self::Flexray(ff) => ff.element(),
83            Self::Lin(lf) => lf.element(),
84        }
85    }
86}
87
88impl IdentifiableAbstractionElement for Frame {}
89
90impl AbstractFrame for Frame {
91    type FrameTriggeringType = FrameTriggering;
92
93    fn frame_triggerings(&self) -> Vec<FrameTriggering> {
94        let model_result = self.element().model();
95        let path_result = self.element().path();
96        if let (Ok(model), Ok(path)) = (model_result, path_result) {
97            model
98                .get_references_to(&path)
99                .iter()
100                .filter_map(|e| {
101                    e.upgrade()
102                        .and_then(|ref_elem| ref_elem.named_parent().ok().flatten())
103                        .and_then(|elem| FrameTriggering::try_from(elem).ok())
104                })
105                .collect()
106        } else {
107            vec![]
108        }
109    }
110
111    /// map a PDU to the frame
112    fn map_pdu<T: AbstractPdu>(
113        &self,
114        gen_pdu: &T,
115        start_position: u32,
116        byte_order: ByteOrder,
117        update_bit: Option<u32>,
118    ) -> Result<PduToFrameMapping, AutosarAbstractionError> {
119        let pdu = gen_pdu.clone().into();
120        Self::map_pdu_internal(self, &pdu, start_position, byte_order, update_bit)
121    }
122}
123
124impl TryFrom<Element> for Frame {
125    type Error = AutosarAbstractionError;
126
127    fn try_from(element: Element) -> Result<Self, Self::Error> {
128        match element.element_name() {
129            ElementName::CanFrame => Ok(Self::Can(CanFrame::try_from(element)?)),
130            ElementName::FlexrayFrame => Ok(Self::Flexray(FlexrayFrame::try_from(element)?)),
131            ElementName::LinEventTriggeredFrame => Ok(Self::Lin(LinEventTriggeredFrame::try_from(element)?.into())),
132            ElementName::LinSporadicFrame => Ok(Self::Lin(LinSporadicFrame::try_from(element)?.into())),
133            ElementName::LinUnconditionalFrame => Ok(Self::Lin(LinUnconditionalFrame::try_from(element)?.into())),
134            _ => Err(AutosarAbstractionError::ConversionError {
135                element,
136                dest: "Frame".to_string(),
137            }),
138        }
139    }
140}
141
142impl Frame {
143    fn map_pdu_internal(
144        &self,
145        pdu: &Pdu,
146        start_position: u32,
147        byte_order: ByteOrder,
148        update_bit: Option<u32>,
149    ) -> Result<PduToFrameMapping, AutosarAbstractionError> {
150        let pdu_name = pdu
151            .name()
152            .ok_or(AutosarAbstractionError::InvalidParameter("invalid PDU".to_string()))?;
153        for mapping in self.mapped_pdus() {
154            // verify that all PDU mappings in this frame use the same byte order
155            if let Some(mapped_byte_order) = mapping.byte_order()
156                && mapped_byte_order != byte_order
157            {
158                return Err(AutosarAbstractionError::InvalidParameter(
159                    "All mapped PDUs must use the same byte order".to_string(),
160                ));
161            }
162
163            // todo? check if the new PDU overlaps any existing ones
164        }
165
166        // add a pdu triggering for the newly mapped PDU to each frame triggering of this frame
167        for ft in self.frame_triggerings() {
168            let pt = ft.add_pdu_triggering(pdu)?;
169            for frame_port in ft.frame_ports() {
170                if let (Ok(ecu), Some(direction)) = (frame_port.ecu(), frame_port.communication_direction()) {
171                    pt.create_pdu_port(&ecu, direction)?;
172                }
173            }
174        }
175
176        // create and return the new mapping
177        let model = self.element().model()?;
178        let base_path = self.element().path()?;
179        let name = make_unique_name(&model, &base_path, &pdu_name);
180
181        let mappings = self
182            .element()
183            .get_or_create_sub_element(ElementName::PduToFrameMappings)?;
184
185        // if the PDU is a multiplexed IPdu, make sure the component IPdus have pdu triggerings
186        if let Pdu::MultiplexedIPdu(mpdu) = pdu {
187            if let Some(static_ipdu) = mpdu.static_part() {
188                mpdu.update_pdu_triggerings(None, &static_ipdu)?;
189            }
190            for dynamic_part in mpdu.dynamic_part_alternatives() {
191                if let Some(dynamic_ipdu) = dynamic_part.ipdu() {
192                    mpdu.update_pdu_triggerings(None, &dynamic_ipdu)?;
193                }
194            }
195        }
196
197        PduToFrameMapping::new(&name, &mappings, pdu, start_position, byte_order, update_bit)
198    }
199}
200
201//##################################################################
202
203/// A trait for all frame triggerings
204pub trait AbstractFrameTriggering: AbstractionElement {
205    /// The frame type triggered by this `FrameTriggering`
206    type FrameType: AbstractFrame;
207
208    /// get the frame triggered by this `FrameTriggering`
209    #[must_use]
210    fn frame(&self) -> Option<Self::FrameType> {
211        Self::FrameType::try_from(
212            self.element()
213                .get_sub_element(ElementName::FrameRef)?
214                .get_reference_target()
215                .ok()?,
216        )
217        .ok()
218    }
219
220    /// iterate over all frame ports referenced by this frame triggering
221    ///
222    /// # Example
223    ///
224    /// ```
225    /// # use autosar_data::*;
226    /// # use autosar_data_abstraction::{*, communication::*};
227    /// # fn main() -> Result<(), AutosarAbstractionError> {
228    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
229    /// # let package = model.get_or_create_package("/pkg")?;
230    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
231    /// # let ecu = system.create_ecu_instance("ECU", &package)?;
232    /// # let cluster = system.create_can_cluster("Cluster", &package, None)?;
233    /// # let channel = cluster.create_physical_channel("Channel")?;
234    /// # let ecu_instance = system.create_ecu_instance("Ecu", &package)?;
235    /// # let canctrl = ecu_instance.create_can_communication_controller("CanCtrl")?;
236    /// # canctrl.connect_physical_channel("Connector", &channel)?;
237    /// let frame = system.create_can_frame("Frame", &package, 8)?;
238    /// let frame_triggering = channel.trigger_frame(&frame, 0x100, CanAddressingMode::Standard, CanFrameType::Can20)?;
239    /// let frame_port = frame_triggering.connect_to_ecu(&ecu_instance, CommunicationDirection::In)?;
240    /// for fp in frame_triggering.frame_ports() {
241    ///    // ...
242    /// }
243    /// assert_eq!(frame_triggering.frame_ports().count(), 1);
244    /// # Ok(())}
245    /// ```
246    fn frame_ports(&self) -> impl Iterator<Item = FramePort> + Send + use<Self> {
247        self.element()
248            .get_sub_element(ElementName::FramePortRefs)
249            .into_iter()
250            .flat_map(|elem| elem.sub_elements())
251            .filter_map(|fpref| {
252                fpref
253                    .get_reference_target()
254                    .ok()
255                    .and_then(|fp| FramePort::try_from(fp).ok())
256            })
257    }
258
259    /// iterate over all PDU triggerings used by this frame triggering
260    fn pdu_triggerings(&self) -> impl Iterator<Item = PduTriggering> + Send + use<Self> {
261        self.element()
262            .get_sub_element(ElementName::PduTriggerings)
263            .into_iter()
264            .flat_map(|elem| elem.sub_elements())
265            .filter_map(|element| {
266                element
267                    .get_sub_element(ElementName::PduTriggeringRef)
268                    .and_then(|ptr| ptr.get_reference_target().ok())
269                    .and_then(|ptelem| PduTriggering::try_from(ptelem).ok())
270            })
271    }
272
273    /// get the physical channel that contains this frame triggering
274    fn physical_channel(&self) -> Result<PhysicalChannel, AutosarAbstractionError> {
275        let channel_elem = self.element().named_parent()?.ok_or(AutosarDataError::ItemDeleted)?;
276        PhysicalChannel::try_from(channel_elem)
277    }
278}
279
280//##################################################################
281
282/// A wrapper for CAN and `FlexRay` frame triggerings
283#[derive(Debug, Clone, PartialEq, Eq)]
284#[non_exhaustive]
285pub enum FrameTriggering {
286    /// a CAN frame triggering
287    Can(CanFrameTriggering),
288    /// a `FlexRay` frame triggering
289    Flexray(FlexrayFrameTriggering),
290    /// a `LIN` frame triggering
291    Lin(LinFrameTriggering),
292}
293
294impl AbstractionElement for FrameTriggering {
295    fn element(&self) -> &autosar_data::Element {
296        match self {
297            Self::Can(cft) => cft.element(),
298            Self::Flexray(fft) => fft.element(),
299            Self::Lin(lft) => lft.element(),
300        }
301    }
302}
303
304impl IdentifiableAbstractionElement for FrameTriggering {}
305
306impl AbstractFrameTriggering for FrameTriggering {
307    type FrameType = Frame;
308}
309
310impl TryFrom<Element> for FrameTriggering {
311    type Error = AutosarAbstractionError;
312
313    fn try_from(element: Element) -> Result<Self, Self::Error> {
314        match element.element_name() {
315            ElementName::CanFrameTriggering => Ok(CanFrameTriggering::try_from(element)?.into()),
316            ElementName::FlexrayFrameTriggering => Ok(FlexrayFrameTriggering::try_from(element)?.into()),
317            ElementName::LinFrameTriggering => Ok(LinFrameTriggering::try_from(element)?.into()),
318            _ => Err(AutosarAbstractionError::ConversionError {
319                element,
320                dest: "FrameTriggering".to_string(),
321            }),
322        }
323    }
324}
325
326impl FrameTriggering {
327    /// connect this `FrameTriggering` to an `EcuInstance`
328    ///
329    /// The `EcuInstance` must already be connected to the `PhysicalChannel` that contains the `FrameTriggering`.
330    pub fn connect_to_ecu(
331        &self,
332        ecu: &EcuInstance,
333        direction: CommunicationDirection,
334    ) -> Result<FramePort, AutosarAbstractionError> {
335        for frame_port in self.frame_ports() {
336            if let (Ok(existing_ecu), Some(existing_direction)) =
337                (frame_port.ecu(), frame_port.communication_direction())
338                && existing_ecu == *ecu
339                && existing_direction == direction
340            {
341                return Ok(frame_port);
342            }
343        }
344
345        let channel = self.physical_channel()?;
346        let connector = channel
347            .ecu_connector(ecu)
348            .ok_or(AutosarAbstractionError::InvalidParameter(
349                "The ECU is not connected to the channel".to_string(),
350            ))?;
351
352        let name = self.name().ok_or(AutosarDataError::ItemDeleted)?;
353        let suffix = match direction {
354            CommunicationDirection::In => "Rx",
355            CommunicationDirection::Out => "Tx",
356        };
357        let port_name = format!("{name}_{suffix}",);
358        let fp_elem = connector
359            .element()
360            .get_or_create_sub_element(ElementName::EcuCommPortInstances)?
361            .create_named_sub_element(ElementName::FramePort, &port_name)?;
362        fp_elem
363            .create_sub_element(ElementName::CommunicationDirection)?
364            .set_character_data::<EnumItem>(direction.into())?;
365
366        self.element()
367            .get_or_create_sub_element(ElementName::FramePortRefs)?
368            .create_sub_element(ElementName::FramePortRef)?
369            .set_reference_target(&fp_elem)?;
370
371        for pt in self.pdu_triggerings() {
372            pt.create_pdu_port(ecu, direction)?;
373        }
374
375        Ok(FramePort(fp_elem))
376    }
377
378    fn add_pdu_triggering(&self, pdu: &Pdu) -> Result<PduTriggering, AutosarAbstractionError> {
379        let channel = self.physical_channel()?;
380        let pt = PduTriggering::new(pdu, &channel)?;
381        let triggerings = self.element().get_or_create_sub_element(ElementName::PduTriggerings)?;
382        triggerings
383            .create_sub_element(ElementName::PduTriggeringRefConditional)?
384            .create_sub_element(ElementName::PduTriggeringRef)?
385            .set_reference_target(pt.element())?;
386
387        for frame_port in self.frame_ports() {
388            if let (Ok(ecu), Some(direction)) = (frame_port.ecu(), frame_port.communication_direction()) {
389                pt.create_pdu_port(&ecu, direction)?;
390            }
391        }
392
393        Ok(pt)
394    }
395
396    /// remove this `FrameTriggering` from the model
397    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
398        match self {
399            Self::Can(cft) => cft.remove(deep),
400            Self::Flexray(fft) => fft.remove(deep),
401            Self::Lin(lft) => lft.remove(deep),
402        }
403    }
404}
405
406//##################################################################
407
408/// `PduToFrameMapping` connects a PDU to a frame
409#[derive(Debug, Clone, PartialEq, Eq, Hash)]
410pub struct PduToFrameMapping(Element);
411abstraction_element!(PduToFrameMapping, PduToFrameMapping);
412impl IdentifiableAbstractionElement for PduToFrameMapping {}
413
414impl PduToFrameMapping {
415    fn new(
416        name: &str,
417        mappings: &Element,
418        pdu: &Pdu,
419        start_position: u32,
420        byte_order: ByteOrder,
421        update_bit: Option<u32>,
422    ) -> Result<Self, AutosarAbstractionError> {
423        let pdumapping_elem = mappings.create_named_sub_element(ElementName::PduToFrameMapping, name)?;
424        pdumapping_elem
425            .create_sub_element(ElementName::PduRef)?
426            .set_reference_target(pdu.element())?;
427
428        let pdumapping = Self(pdumapping_elem);
429
430        pdumapping.set_byte_order(byte_order)?;
431        pdumapping.set_start_position(start_position)?;
432        pdumapping.set_update_bit(update_bit)?;
433
434        Ok(pdumapping)
435    }
436
437    /// remove this `PduToFrameMapping` from the model
438    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
439        let opt_pdu = self.pdu();
440
441        AbstractionElement::remove(self, deep)?;
442
443        if deep && let Some(pdu) = opt_pdu {
444            // check if the PDU became unused because of this mapping removal
445            if !is_used_system_element(pdu.element()) {
446                pdu.remove(deep)?;
447            }
448        }
449
450        Ok(())
451    }
452
453    /// Reference to the PDU that is mapped into the frame. The PDU reference is mandatory.
454    #[must_use]
455    pub fn pdu(&self) -> Option<Pdu> {
456        self.element()
457            .get_sub_element(ElementName::PduRef)
458            .and_then(|pduref| pduref.get_reference_target().ok())
459            .and_then(|pdu_elem| Pdu::try_from(pdu_elem).ok())
460    }
461
462    /// set the byte order of the data in the PDU.
463    ///
464    /// All `PduToFrameMappings` within a frame must have the same byte order.
465    /// PDUs may not use the byte order value `Opaque`.
466    ///
467    /// Note: If the byte order is swapped, then the start position must be adjusted accordingly.
468    pub fn set_byte_order(&self, byte_order: ByteOrder) -> Result<(), AutosarAbstractionError> {
469        if byte_order == ByteOrder::Opaque {
470            return Err(AutosarAbstractionError::InvalidParameter(
471                "Byte order: opaque is not allowed for PDUs".to_string(),
472            ));
473        }
474        self.element()
475            .get_or_create_sub_element(ElementName::PackingByteOrder)?
476            .set_character_data::<EnumItem>(byte_order.into())?;
477        Ok(())
478    }
479
480    /// get the byte order of the data in the PDU.
481    ///
482    /// All `PduToFrameMappings` within a frame must have the same byte order.
483    /// PDUs may not use the byte order value `Opaque`.
484    #[must_use]
485    pub fn byte_order(&self) -> Option<ByteOrder> {
486        self.element()
487            .get_sub_element(ElementName::PackingByteOrder)
488            .and_then(|pbo| pbo.character_data())
489            .and_then(|cdata| cdata.enum_value())
490            .and_then(|enumval| enumval.try_into().ok())
491    }
492
493    /// set the start position of the PDU data within the frame (bit position).
494    ///
495    /// PDUs are byte aligned.
496    /// For little-endian data the values 0, 8, 16, ... are allowed;
497    /// for big-endian data the value 7, 15, 23, ... are allowed.
498    ///
499    /// Note: if you intend to change both the byte order and the start position, then you should change the byte order first.
500    /// New values set here must match the configured byte order.
501    pub fn set_start_position(&self, start_position: u32) -> Result<(), AutosarAbstractionError> {
502        if (self.byte_order() == Some(ByteOrder::MostSignificantByteFirst) && (start_position % 8 != 7))
503            || (self.byte_order() == Some(ByteOrder::MostSignificantByteLast) && !start_position.is_multiple_of(8))
504        {
505            return Err(AutosarAbstractionError::InvalidParameter(
506                "PDUs must be byte-aligned".to_string(),
507            ));
508        }
509        self.element()
510            .get_or_create_sub_element(ElementName::StartPosition)?
511            .set_character_data(u64::from(start_position))?;
512        Ok(())
513    }
514
515    /// Start position of the PDU data within the frame (bit position). The start position is mandatory.
516    ///
517    /// PDUs are byte aligned.
518    /// For little-endian data the values 0, 8, 16, ... are allowed;
519    /// for big-endian data the value 7, 15, 23, ... are allowed.
520    #[must_use]
521    pub fn start_position(&self) -> Option<u32> {
522        self.element()
523            .get_sub_element(ElementName::StartPosition)
524            .and_then(|pbo| pbo.character_data())
525            .and_then(|cdata| cdata.parse_integer())
526    }
527
528    /// set or clear the bit position of the update bit for the mapped PDU.
529    pub fn set_update_bit(&self, update_bit: Option<u32>) -> Result<(), AutosarAbstractionError> {
530        if let Some(update_bit) = update_bit {
531            self.element()
532                .get_or_create_sub_element(ElementName::UpdateIndicationBitPosition)?
533                .set_character_data(u64::from(update_bit))?;
534        } else {
535            let _ = self
536                .element()
537                .remove_sub_element_kind(ElementName::UpdateIndicationBitPosition);
538        }
539        Ok(())
540    }
541
542    /// Bit position of the update bit for the mapped PDU. Not all PDUs use an update bit.
543    #[must_use]
544    pub fn update_bit(&self) -> Option<u32> {
545        self.element()
546            .get_sub_element(ElementName::UpdateIndicationBitPosition)
547            .and_then(|pbo| pbo.character_data())
548            .and_then(|cdata| cdata.parse_integer())
549    }
550}
551
552//##################################################################
553
554/// The `FramePort` allows an ECU to send or receive a frame
555#[derive(Debug, Clone, PartialEq, Eq, Hash)]
556pub struct FramePort(Element);
557abstraction_element!(FramePort, FramePort);
558impl IdentifiableAbstractionElement for FramePort {}
559
560impl FramePort {
561    /// get the ECU instance that contains this frame port
562    pub fn ecu(&self) -> Result<EcuInstance, AutosarAbstractionError> {
563        let comm_connector_elem = self.element().named_parent()?.unwrap();
564        let ecu_elem = comm_connector_elem.named_parent()?.unwrap();
565        EcuInstance::try_from(ecu_elem)
566    }
567
568    /// set the communication direction of the frame port
569    pub fn set_communication_direction(
570        &self,
571        direction: CommunicationDirection,
572    ) -> Result<(), AutosarAbstractionError> {
573        self.element()
574            .get_or_create_sub_element(ElementName::CommunicationDirection)?
575            .set_character_data::<EnumItem>(direction.into())?;
576        Ok(())
577    }
578
579    /// get the communication direction of the frame port
580    #[must_use]
581    pub fn communication_direction(&self) -> Option<CommunicationDirection> {
582        self.element()
583            .get_sub_element(ElementName::CommunicationDirection)?
584            .character_data()?
585            .enum_value()?
586            .try_into()
587            .ok()
588    }
589}
590
591//##################################################################
592
593#[cfg(test)]
594mod test {
595    use super::*;
596    use crate::{AutosarModelAbstraction, SystemCategory};
597
598    #[test]
599    fn frame() {
600        let model = AutosarModelAbstraction::create("filename", autosar_data::AutosarVersion::LATEST);
601        let package = model.get_or_create_package("/package").unwrap();
602        let system = package.create_system("System", SystemCategory::SystemExtract).unwrap();
603
604        let can_frame = system.create_can_frame("CanFrame", &package, 8).unwrap();
605        let flexray_frame = system.create_flexray_frame("FlexrayFrame", &package, 32).unwrap();
606
607        let frame_1 = Frame::try_from(can_frame.element().clone()).unwrap();
608        assert_eq!(frame_1.element().element_name(), autosar_data::ElementName::CanFrame);
609        let frame_2 = Frame::try_from(flexray_frame.element().clone()).unwrap();
610        assert_eq!(
611            frame_2.element().element_name(),
612            autosar_data::ElementName::FlexrayFrame
613        );
614
615        let err = Frame::try_from(model.root_element().clone());
616        assert!(err.is_err());
617    }
618}