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, 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            _ => Err(AutosarAbstractionError::ConversionError {
132                element,
133                dest: "Frame".to_string(),
134            }),
135        }
136    }
137}
138
139impl Frame {
140    fn map_pdu_internal(
141        &self,
142        pdu: &Pdu,
143        start_position: u32,
144        byte_order: ByteOrder,
145        update_bit: Option<u32>,
146    ) -> Result<PduToFrameMapping, AutosarAbstractionError> {
147        let pdu_name = pdu
148            .name()
149            .ok_or(AutosarAbstractionError::InvalidParameter("invalid PDU".to_string()))?;
150        for mapping in self.mapped_pdus() {
151            // verify that all PDU mappings in this frame use the same byte order
152            if let Some(mapped_byte_order) = mapping.byte_order() {
153                if mapped_byte_order != byte_order {
154                    return Err(AutosarAbstractionError::InvalidParameter(
155                        "All mapped PDUs must use the same byte order".to_string(),
156                    ));
157                }
158            }
159
160            // todo? check if the new PDU overlaps any existing ones
161        }
162
163        // add a pdu triggering for the newly mapped PDU to each frame triggering of this frame
164        for ft in self.frame_triggerings() {
165            let pt = ft.add_pdu_triggering(pdu)?;
166            for frame_port in ft.frame_ports() {
167                if let (Ok(ecu), Some(direction)) = (frame_port.ecu(), frame_port.communication_direction()) {
168                    pt.create_pdu_port(&ecu, direction)?;
169                }
170            }
171        }
172
173        // create and return the new mapping
174        let model = self.element().model()?;
175        let base_path = self.element().path()?;
176        let name = make_unique_name(&model, &base_path, &pdu_name);
177
178        let mappings = self
179            .element()
180            .get_or_create_sub_element(ElementName::PduToFrameMappings)?;
181
182        PduToFrameMapping::new(&name, &mappings, pdu, start_position, byte_order, update_bit)
183    }
184}
185
186//##################################################################
187
188/// A trait for all frame triggerings
189pub trait AbstractFrameTriggering: AbstractionElement {
190    /// The frame type triggered by this `FrameTriggering`
191    type FrameType: AbstractFrame;
192
193    /// get the frame triggered by this `FrameTriggering`
194    #[must_use]
195    fn frame(&self) -> Option<Self::FrameType> {
196        Self::FrameType::try_from(
197            self.element()
198                .get_sub_element(ElementName::FrameRef)?
199                .get_reference_target()
200                .ok()?,
201        )
202        .ok()
203    }
204
205    /// iterate over all frame ports referenced by this frame triggering
206    ///
207    /// # Example
208    ///
209    /// ```
210    /// # use autosar_data::*;
211    /// # use autosar_data_abstraction::{*, communication::*};
212    /// # fn main() -> Result<(), AutosarAbstractionError> {
213    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
214    /// # let package = model.get_or_create_package("/pkg")?;
215    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
216    /// # let ecu = system.create_ecu_instance("ECU", &package)?;
217    /// # let cluster = system.create_can_cluster("Cluster", &package, None)?;
218    /// # let channel = cluster.create_physical_channel("Channel")?;
219    /// # let ecu_instance = system.create_ecu_instance("Ecu", &package)?;
220    /// # let canctrl = ecu_instance.create_can_communication_controller("CanCtrl")?;
221    /// # canctrl.connect_physical_channel("Connector", &channel)?;
222    /// let frame = system.create_can_frame("Frame", &package, 8)?;
223    /// let frame_triggering = channel.trigger_frame(&frame, 0x100, CanAddressingMode::Standard, CanFrameType::Can20)?;
224    /// let frame_port = frame_triggering.connect_to_ecu(&ecu_instance, CommunicationDirection::In)?;
225    /// for fp in frame_triggering.frame_ports() {
226    ///    // ...
227    /// }
228    /// assert_eq!(frame_triggering.frame_ports().count(), 1);
229    /// # Ok(())}
230    /// ```
231    fn frame_ports(&self) -> impl Iterator<Item = FramePort> + Send + use<Self> {
232        self.element()
233            .get_sub_element(ElementName::FramePortRefs)
234            .into_iter()
235            .flat_map(|elem| elem.sub_elements())
236            .filter_map(|fpref| {
237                fpref
238                    .get_reference_target()
239                    .ok()
240                    .and_then(|fp| FramePort::try_from(fp).ok())
241            })
242    }
243
244    /// iterate over all PDU triggerings used by this frame triggering
245    fn pdu_triggerings(&self) -> impl Iterator<Item = PduTriggering> + Send + use<Self> {
246        self.element()
247            .get_sub_element(ElementName::PduTriggerings)
248            .into_iter()
249            .flat_map(|elem| elem.sub_elements())
250            .filter_map(|element| {
251                element
252                    .get_sub_element(ElementName::PduTriggeringRef)
253                    .and_then(|ptr| ptr.get_reference_target().ok())
254                    .and_then(|ptelem| PduTriggering::try_from(ptelem).ok())
255            })
256    }
257
258    /// get the physical channel that contains this frame triggering
259    fn physical_channel(&self) -> Result<PhysicalChannel, AutosarAbstractionError> {
260        let channel_elem = self.element().named_parent()?.ok_or(AutosarDataError::ItemDeleted)?;
261        PhysicalChannel::try_from(channel_elem)
262    }
263}
264
265//##################################################################
266
267/// A wrapper for CAN and `FlexRay` frame triggerings
268#[derive(Debug, Clone, PartialEq, Eq)]
269#[non_exhaustive]
270pub enum FrameTriggering {
271    /// a CAN frame triggering
272    Can(CanFrameTriggering),
273    /// a `FlexRay` frame triggering
274    Flexray(FlexrayFrameTriggering),
275    /// a `LIN` frame triggering
276    Lin(LinFrameTriggering),
277}
278
279impl AbstractionElement for FrameTriggering {
280    fn element(&self) -> &autosar_data::Element {
281        match self {
282            Self::Can(cft) => cft.element(),
283            Self::Flexray(fft) => fft.element(),
284            Self::Lin(lft) => lft.element(),
285        }
286    }
287}
288
289impl IdentifiableAbstractionElement for FrameTriggering {}
290
291impl AbstractFrameTriggering for FrameTriggering {
292    type FrameType = Frame;
293}
294
295impl TryFrom<Element> for FrameTriggering {
296    type Error = AutosarAbstractionError;
297
298    fn try_from(element: Element) -> Result<Self, Self::Error> {
299        match element.element_name() {
300            ElementName::CanFrameTriggering => Ok(CanFrameTriggering::try_from(element)?.into()),
301            ElementName::FlexrayFrameTriggering => Ok(FlexrayFrameTriggering::try_from(element)?.into()),
302            ElementName::LinFrameTriggering => Ok(LinFrameTriggering::try_from(element)?.into()),
303            _ => Err(AutosarAbstractionError::ConversionError {
304                element,
305                dest: "FrameTriggering".to_string(),
306            }),
307        }
308    }
309}
310
311impl FrameTriggering {
312    /// connect this `FrameTriggering` to an `EcuInstance`
313    ///
314    /// The `EcuInstance` must already be connected to the `PhysicalChannel` that contains the `FrameTriggering`.
315    pub fn connect_to_ecu(
316        &self,
317        ecu: &EcuInstance,
318        direction: CommunicationDirection,
319    ) -> Result<FramePort, AutosarAbstractionError> {
320        for frame_port in self.frame_ports() {
321            if let (Ok(existing_ecu), Some(existing_direction)) =
322                (frame_port.ecu(), frame_port.communication_direction())
323            {
324                if existing_ecu == *ecu && existing_direction == direction {
325                    return Ok(frame_port);
326                }
327            }
328        }
329
330        let channel = self.physical_channel()?;
331        let connector = channel
332            .ecu_connector(ecu)
333            .ok_or(AutosarAbstractionError::InvalidParameter(
334                "The ECU is not connected to the channel".to_string(),
335            ))?;
336
337        let name = self.name().ok_or(AutosarDataError::ItemDeleted)?;
338        let suffix = match direction {
339            CommunicationDirection::In => "Rx",
340            CommunicationDirection::Out => "Tx",
341        };
342        let port_name = format!("{name}_{suffix}",);
343        let fp_elem = connector
344            .element()
345            .get_or_create_sub_element(ElementName::EcuCommPortInstances)?
346            .create_named_sub_element(ElementName::FramePort, &port_name)?;
347        fp_elem
348            .create_sub_element(ElementName::CommunicationDirection)?
349            .set_character_data::<EnumItem>(direction.into())?;
350
351        self.element()
352            .get_or_create_sub_element(ElementName::FramePortRefs)?
353            .create_sub_element(ElementName::FramePortRef)?
354            .set_reference_target(&fp_elem)?;
355
356        for pt in self.pdu_triggerings() {
357            pt.create_pdu_port(ecu, direction)?;
358        }
359
360        Ok(FramePort(fp_elem))
361    }
362
363    fn add_pdu_triggering(&self, pdu: &Pdu) -> Result<PduTriggering, AutosarAbstractionError> {
364        let channel = self.physical_channel()?;
365        let pt = PduTriggering::new(pdu, &channel)?;
366        let triggerings = self.element().get_or_create_sub_element(ElementName::PduTriggerings)?;
367        triggerings
368            .create_sub_element(ElementName::PduTriggeringRefConditional)?
369            .create_sub_element(ElementName::PduTriggeringRef)?
370            .set_reference_target(pt.element())?;
371
372        for frame_port in self.frame_ports() {
373            if let (Ok(ecu), Some(direction)) = (frame_port.ecu(), frame_port.communication_direction()) {
374                pt.create_pdu_port(&ecu, direction)?;
375            }
376        }
377
378        Ok(pt)
379    }
380}
381
382//##################################################################
383
384/// `PduToFrameMapping` connects a PDU to a frame
385#[derive(Debug, Clone, PartialEq, Eq, Hash)]
386pub struct PduToFrameMapping(Element);
387abstraction_element!(PduToFrameMapping, PduToFrameMapping);
388impl IdentifiableAbstractionElement for PduToFrameMapping {}
389
390impl PduToFrameMapping {
391    fn new(
392        name: &str,
393        mappings: &Element,
394        pdu: &Pdu,
395        start_position: u32,
396        byte_order: ByteOrder,
397        update_bit: Option<u32>,
398    ) -> Result<Self, AutosarAbstractionError> {
399        let pdumapping_elem = mappings.create_named_sub_element(ElementName::PduToFrameMapping, name)?;
400        pdumapping_elem
401            .create_sub_element(ElementName::PduRef)?
402            .set_reference_target(pdu.element())?;
403
404        let pdumapping = Self(pdumapping_elem);
405
406        pdumapping.set_byte_order(byte_order)?;
407        pdumapping.set_start_position(start_position)?;
408        pdumapping.set_update_bit(update_bit)?;
409
410        Ok(pdumapping)
411    }
412
413    /// Reference to the PDU that is mapped into the frame. The PDU reference is mandatory.
414    #[must_use]
415    pub fn pdu(&self) -> Option<Pdu> {
416        self.element()
417            .get_sub_element(ElementName::PduRef)
418            .and_then(|pduref| pduref.get_reference_target().ok())
419            .and_then(|pdu_elem| Pdu::try_from(pdu_elem).ok())
420    }
421
422    /// set the byte order of the data in the PDU.
423    ///
424    /// All `PduToFrameMappings` within a frame must have the same byte order.
425    /// PDUs may not use the byte order value `Opaque`.
426    ///
427    /// Note: If the byte order is swapped, then the start position must be adjusted accordingly.
428    pub fn set_byte_order(&self, byte_order: ByteOrder) -> Result<(), AutosarAbstractionError> {
429        if byte_order == ByteOrder::Opaque {
430            return Err(AutosarAbstractionError::InvalidParameter(
431                "Byte order: opaque is not allowed for PDUs".to_string(),
432            ));
433        }
434        self.element()
435            .get_or_create_sub_element(ElementName::PackingByteOrder)?
436            .set_character_data::<EnumItem>(byte_order.into())?;
437        Ok(())
438    }
439
440    /// get the byte order of the data in the PDU.
441    ///
442    /// All `PduToFrameMappings` within a frame must have the same byte order.
443    /// PDUs may not use the byte order value `Opaque`.
444    #[must_use]
445    pub fn byte_order(&self) -> Option<ByteOrder> {
446        self.element()
447            .get_sub_element(ElementName::PackingByteOrder)
448            .and_then(|pbo| pbo.character_data())
449            .and_then(|cdata| cdata.enum_value())
450            .and_then(|enumval| enumval.try_into().ok())
451    }
452
453    /// set the start position of the PDU data within the frame (bit position).
454    ///
455    /// PDUs are byte aligned.
456    /// For little-endian data the values 0, 8, 16, ... are allowed;
457    /// for big-endian data the value 7, 15, 23, ... are allowed.
458    ///
459    /// Note: if you intend to change both the byte order and the start position, then you should change the byte order first.
460    /// New values set here must match the configured byte order.
461    pub fn set_start_position(&self, start_position: u32) -> Result<(), AutosarAbstractionError> {
462        if (self.byte_order() == Some(ByteOrder::MostSignificantByteFirst) && (start_position % 8 != 7))
463            || (self.byte_order() == Some(ByteOrder::MostSignificantByteLast) && (start_position % 8 != 0))
464        {
465            return Err(AutosarAbstractionError::InvalidParameter(
466                "PDUs must be byte-aligned".to_string(),
467            ));
468        }
469        self.element()
470            .get_or_create_sub_element(ElementName::StartPosition)?
471            .set_character_data(u64::from(start_position))?;
472        Ok(())
473    }
474
475    /// Start position of the PDU data within the frame (bit position). The start position is mandatory.
476    ///
477    /// PDUs are byte aligned.
478    /// For little-endian data the values 0, 8, 16, ... are allowed;
479    /// for big-endian data the value 7, 15, 23, ... are allowed.
480    #[must_use]
481    pub fn start_position(&self) -> Option<u32> {
482        self.element()
483            .get_sub_element(ElementName::StartPosition)
484            .and_then(|pbo| pbo.character_data())
485            .and_then(|cdata| cdata.parse_integer())
486    }
487
488    /// set or clear the bit position of the update bit for the mapped PDU.
489    pub fn set_update_bit(&self, update_bit: Option<u32>) -> Result<(), AutosarAbstractionError> {
490        if let Some(update_bit) = update_bit {
491            self.element()
492                .get_or_create_sub_element(ElementName::UpdateIndicationBitPosition)?
493                .set_character_data(u64::from(update_bit))?;
494        } else {
495            let _ = self
496                .element()
497                .remove_sub_element_kind(ElementName::UpdateIndicationBitPosition);
498        }
499        Ok(())
500    }
501
502    /// Bit position of the update bit for the mapped PDU. Not all PDUs use an update bit.
503    #[must_use]
504    pub fn update_bit(&self) -> Option<u32> {
505        self.element()
506            .get_sub_element(ElementName::UpdateIndicationBitPosition)
507            .and_then(|pbo| pbo.character_data())
508            .and_then(|cdata| cdata.parse_integer())
509    }
510}
511
512//##################################################################
513
514/// The `FramePort` allows an ECU to send or receive a frame
515#[derive(Debug, Clone, PartialEq, Eq, Hash)]
516pub struct FramePort(Element);
517abstraction_element!(FramePort, FramePort);
518impl IdentifiableAbstractionElement for FramePort {}
519
520impl FramePort {
521    /// get the ECU instance that contains this frame port
522    pub fn ecu(&self) -> Result<EcuInstance, AutosarAbstractionError> {
523        let comm_connector_elem = self.element().named_parent()?.unwrap();
524        let ecu_elem = comm_connector_elem.named_parent()?.unwrap();
525        EcuInstance::try_from(ecu_elem)
526    }
527
528    /// set the communication direction of the frame port
529    pub fn set_communication_direction(
530        &self,
531        direction: CommunicationDirection,
532    ) -> Result<(), AutosarAbstractionError> {
533        self.element()
534            .get_or_create_sub_element(ElementName::CommunicationDirection)?
535            .set_character_data::<EnumItem>(direction.into())?;
536        Ok(())
537    }
538
539    /// get the communication direction of the frame port
540    #[must_use]
541    pub fn communication_direction(&self) -> Option<CommunicationDirection> {
542        self.element()
543            .get_sub_element(ElementName::CommunicationDirection)?
544            .character_data()?
545            .enum_value()?
546            .try_into()
547            .ok()
548    }
549}
550
551//##################################################################
552
553#[cfg(test)]
554mod test {
555    use super::*;
556    use crate::{AutosarModelAbstraction, SystemCategory};
557
558    #[test]
559    fn frame() {
560        let model = AutosarModelAbstraction::create("filename", autosar_data::AutosarVersion::LATEST);
561        let package = model.get_or_create_package("/package").unwrap();
562        let system = package.create_system("System", SystemCategory::SystemExtract).unwrap();
563
564        let can_frame = system.create_can_frame("CanFrame", &package, 8).unwrap();
565        let flexray_frame = system.create_flexray_frame("FlexrayFrame", &package, 32).unwrap();
566
567        let frame_1 = Frame::try_from(can_frame.element().clone()).unwrap();
568        assert_eq!(frame_1.element().element_name(), autosar_data::ElementName::CanFrame);
569        let frame_2 = Frame::try_from(flexray_frame.element().clone()).unwrap();
570        assert_eq!(
571            frame_2.element().element_name(),
572            autosar_data::ElementName::FlexrayFrame
573        );
574
575        let err = Frame::try_from(model.root_element().clone());
576        assert!(err.is_err());
577    }
578}