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