autosar_data_abstraction/communication/frame/
can.rs

1use crate::communication::{
2    AbstractFrame, AbstractFrameTriggering, AbstractPdu, CanPhysicalChannel, CommunicationDirection, Frame, FramePort,
3    FrameTriggering, Pdu, PduToFrameMapping, PduTriggering,
4};
5use crate::{
6    AbstractionElement, ArPackage, AutosarAbstractionError, ByteOrder, EcuInstance, IdentifiableAbstractionElement,
7    abstraction_element, make_unique_name,
8};
9use autosar_data::{Element, ElementName, EnumItem};
10
11/// A frame on a CAN bus
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub struct CanFrame(Element);
14abstraction_element!(CanFrame, CanFrame);
15impl IdentifiableAbstractionElement for CanFrame {}
16
17impl CanFrame {
18    pub(crate) fn new(name: &str, package: &ArPackage, byte_length: u64) -> Result<Self, AutosarAbstractionError> {
19        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
20        let can_frame = pkg_elements.create_named_sub_element(ElementName::CanFrame, name)?;
21
22        can_frame
23            .create_sub_element(ElementName::FrameLength)?
24            .set_character_data(byte_length.to_string())?;
25
26        Ok(Self(can_frame))
27    }
28}
29
30impl AbstractFrame for CanFrame {
31    type FrameTriggeringType = CanFrameTriggering;
32
33    /// List all [`CanFrameTriggering`]s using this frame
34    fn frame_triggerings(&self) -> Vec<CanFrameTriggering> {
35        let model_result = self.element().model();
36        let path_result = self.element().path();
37        if let (Ok(model), Ok(path)) = (model_result, path_result) {
38            model
39                .get_references_to(&path)
40                .iter()
41                .filter_map(|e| {
42                    e.upgrade()
43                        .and_then(|ref_elem| ref_elem.named_parent().ok().flatten())
44                        .and_then(|elem| CanFrameTriggering::try_from(elem).ok())
45                })
46                .collect()
47        } else {
48            vec![]
49        }
50    }
51
52    /// map a PDU to the frame
53    fn map_pdu<T: AbstractPdu>(
54        &self,
55        gen_pdu: &T,
56        start_position: u32,
57        byte_order: ByteOrder,
58        update_bit: Option<u32>,
59    ) -> Result<PduToFrameMapping, AutosarAbstractionError> {
60        Frame::Can(self.clone()).map_pdu(gen_pdu, start_position, byte_order, update_bit)
61    }
62}
63
64//##################################################################
65
66/// The frame triggering connects a frame to a physical channel
67#[derive(Debug, Clone, PartialEq, Eq, Hash)]
68pub struct CanFrameTriggering(Element);
69abstraction_element!(CanFrameTriggering, CanFrameTriggering);
70impl IdentifiableAbstractionElement for CanFrameTriggering {}
71
72impl CanFrameTriggering {
73    pub(crate) fn new(
74        channel: &CanPhysicalChannel,
75        frame: &CanFrame,
76        identifier: u32,
77        addressing_mode: CanAddressingMode,
78        frame_type: CanFrameType,
79    ) -> Result<Self, AutosarAbstractionError> {
80        let model = channel.element().model()?;
81        let base_path = channel.element().path()?;
82        let frame_name = frame
83            .name()
84            .ok_or(AutosarAbstractionError::InvalidParameter("invalid frame".to_string()))?;
85        let ft_name = format!("FT_{frame_name}");
86        let ft_name = make_unique_name(&model, &base_path, &ft_name);
87
88        let frame_triggerings = channel
89            .element()
90            .get_or_create_sub_element(ElementName::FrameTriggerings)?;
91        let can_triggering = frame_triggerings.create_named_sub_element(ElementName::CanFrameTriggering, &ft_name)?;
92
93        can_triggering
94            .create_sub_element(ElementName::FrameRef)?
95            .set_reference_target(frame.element())?;
96
97        let ft = Self(can_triggering);
98        ft.set_addressing_mode(addressing_mode)?;
99        ft.set_frame_type(frame_type)?;
100        if let Err(error) = ft.set_identifier(identifier) {
101            let _ = frame_triggerings.remove_sub_element(ft.0);
102            return Err(error);
103        }
104
105        for pdu_mapping in frame.mapped_pdus() {
106            if let Some(pdu) = pdu_mapping.pdu() {
107                ft.add_pdu_triggering(&pdu)?;
108            }
109        }
110
111        Ok(ft)
112    }
113
114    /// set the can id associated with this frame
115    pub fn set_identifier(&self, identifier: u32) -> Result<(), AutosarAbstractionError> {
116        let amode = self.addressing_mode().unwrap_or(CanAddressingMode::Standard);
117        if amode == CanAddressingMode::Standard && identifier > 0x7ff {
118            return Err(AutosarAbstractionError::InvalidParameter(format!(
119                "CAN-ID {identifier} is outside the 11-bit range allowed by standard addressing"
120            )));
121        } else if identifier > 0x1fff_ffff {
122            return Err(AutosarAbstractionError::InvalidParameter(format!(
123                "CAN-ID {identifier} is outside the 29-bit range allowed by extended addressing"
124            )));
125        }
126        self.element()
127            .get_or_create_sub_element(ElementName::Identifier)?
128            .set_character_data(identifier.to_string())?;
129
130        Ok(())
131    }
132
133    /// get the can id associated with this frame triggering
134    #[must_use]
135    pub fn identifier(&self) -> Option<u32> {
136        self.element()
137            .get_sub_element(ElementName::Identifier)?
138            .character_data()?
139            .parse_integer()
140    }
141
142    /// set the addressing mode for this frame triggering
143    pub fn set_addressing_mode(&self, addressing_mode: CanAddressingMode) -> Result<(), AutosarAbstractionError> {
144        self.element()
145            .get_or_create_sub_element(ElementName::CanAddressingMode)?
146            .set_character_data::<EnumItem>(addressing_mode.into())?;
147
148        Ok(())
149    }
150
151    /// get the addressing mode for this frame triggering
152    #[must_use]
153    pub fn addressing_mode(&self) -> Option<CanAddressingMode> {
154        self.element()
155            .get_sub_element(ElementName::CanAddressingMode)?
156            .character_data()?
157            .enum_value()?
158            .try_into()
159            .ok()
160    }
161
162    /// set the frame type for this frame triggering
163    pub fn set_frame_type(&self, frame_type: CanFrameType) -> Result<(), AutosarAbstractionError> {
164        self.element()
165            .get_or_create_sub_element(ElementName::CanFrameRxBehavior)?
166            .set_character_data::<EnumItem>(frame_type.into())?;
167        self.element()
168            .get_or_create_sub_element(ElementName::CanFrameTxBehavior)?
169            .set_character_data::<EnumItem>(frame_type.into())?;
170
171        Ok(())
172    }
173
174    /// get the frame type for this frame triggering
175    #[must_use]
176    pub fn frame_type(&self) -> Option<CanFrameType> {
177        self.element()
178            .get_sub_element(ElementName::CanFrameTxBehavior)?
179            .character_data()?
180            .enum_value()?
181            .try_into()
182            .ok()
183    }
184
185    pub(crate) fn add_pdu_triggering(&self, pdu: &Pdu) -> Result<PduTriggering, AutosarAbstractionError> {
186        FrameTriggering::Can(self.clone()).add_pdu_triggering(pdu)
187    }
188
189    /// get the physical channel that contains this frame triggering
190    pub fn physical_channel(&self) -> Result<CanPhysicalChannel, AutosarAbstractionError> {
191        let channel_elem = self.element().named_parent()?.unwrap();
192        CanPhysicalChannel::try_from(channel_elem)
193    }
194
195    /// connect this frame triggering to an ECU
196    ///
197    /// The direction parameter specifies if the communication is incoming or outgoing
198    pub fn connect_to_ecu(
199        &self,
200        ecu: &EcuInstance,
201        direction: CommunicationDirection,
202    ) -> Result<FramePort, AutosarAbstractionError> {
203        FrameTriggering::Can(self.clone()).connect_to_ecu(ecu, direction)
204    }
205}
206
207impl AbstractFrameTriggering for CanFrameTriggering {
208    type FrameType = CanFrame;
209}
210
211impl From<CanFrameTriggering> for FrameTriggering {
212    fn from(cft: CanFrameTriggering) -> Self {
213        FrameTriggering::Can(cft)
214    }
215}
216
217//##################################################################
218
219/// The addressing mode for a CAN frame
220#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221pub enum CanAddressingMode {
222    /// Standard addressing mode: 11-bit identifier
223    Standard,
224    /// Extended addressing mode: 29-bit identifier
225    Extended,
226}
227
228impl TryFrom<EnumItem> for CanAddressingMode {
229    type Error = AutosarAbstractionError;
230
231    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
232        match value {
233            EnumItem::Standard => Ok(CanAddressingMode::Standard),
234            EnumItem::Extended => Ok(CanAddressingMode::Extended),
235            _ => Err(AutosarAbstractionError::ValueConversionError {
236                value: value.to_string(),
237                dest: "CanAddressingMode".to_string(),
238            }),
239        }
240    }
241}
242
243impl From<CanAddressingMode> for EnumItem {
244    fn from(value: CanAddressingMode) -> Self {
245        match value {
246            CanAddressingMode::Standard => EnumItem::Standard,
247            CanAddressingMode::Extended => EnumItem::Extended,
248        }
249    }
250}
251
252//##################################################################
253
254/// The type of a CAN frame
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub enum CanFrameType {
257    /// CAN 2.0 frame (max 8 bytes)
258    Can20,
259    /// CAN FD frame (max 64 bytes, transmitted at the `CanFD` baud rate)
260    CanFd,
261    /// Any CAN frame
262    Any,
263}
264
265impl TryFrom<EnumItem> for CanFrameType {
266    type Error = AutosarAbstractionError;
267
268    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
269        match value {
270            EnumItem::Can20 => Ok(CanFrameType::Can20),
271            EnumItem::CanFd => Ok(CanFrameType::CanFd),
272            EnumItem::Any => Ok(CanFrameType::Any),
273            _ => Err(AutosarAbstractionError::ValueConversionError {
274                value: value.to_string(),
275                dest: "CanFrameType".to_string(),
276            }),
277        }
278    }
279}
280
281impl From<CanFrameType> for EnumItem {
282    fn from(value: CanFrameType) -> Self {
283        match value {
284            CanFrameType::Can20 => EnumItem::Can20,
285            CanFrameType::CanFd => EnumItem::CanFd,
286            CanFrameType::Any => EnumItem::Any,
287        }
288    }
289}
290
291//##################################################################
292
293#[cfg(test)]
294mod test {
295    use super::*;
296    use crate::{AutosarModelAbstraction, ByteOrder, SystemCategory, communication::AbstractPhysicalChannel};
297    use autosar_data::AutosarVersion;
298
299    #[test]
300    fn can_frame() {
301        let model = AutosarModelAbstraction::create("test", AutosarVersion::LATEST);
302        let package = model.get_or_create_package("/package").unwrap();
303        let system = package.create_system("System", SystemCategory::EcuExtract).unwrap();
304        let can_cluster = system.create_can_cluster("Cluster", &package, None).unwrap();
305        let channel = can_cluster.create_physical_channel("Channel").unwrap();
306
307        let ecu_instance = system.create_ecu_instance("ECU", &package).unwrap();
308        let can_controller = ecu_instance.create_can_communication_controller("Controller").unwrap();
309        can_controller.connect_physical_channel("connection", &channel).unwrap();
310        assert_eq!(channel.connectors().count(), 1);
311        assert_eq!(channel.connectors().next().unwrap().name().unwrap(), "connection");
312
313        let pdu1 = system.create_isignal_ipdu("pdu1", &package, 8).unwrap();
314        let pdu2 = system.create_isignal_ipdu("pdu2", &package, 8).unwrap();
315
316        // create frames
317        let frame1 = system.create_can_frame("frame1", &package, 8).unwrap();
318        let frame2 = system.create_can_frame("frame2", &package, 8).unwrap();
319
320        assert_eq!(frame1.length().unwrap(), 8);
321        frame1.set_length(6).unwrap();
322        assert_eq!(frame1.length().unwrap(), 6);
323
324        // map a PDU to the frame before it has been connected to the channel
325        let mapping1 = frame1
326            .map_pdu(&pdu1, 7, ByteOrder::MostSignificantByteFirst, None)
327            .unwrap();
328        assert!(frame1.mapped_pdus().count() == 1);
329        assert_eq!(frame1.mapped_pdus().next().unwrap(), mapping1);
330
331        // trigger both frames
332        let frame_triggering1 = channel
333            .trigger_frame(&frame1, 0x123, CanAddressingMode::Standard, CanFrameType::Can20)
334            .unwrap();
335        assert_eq!(frame1.frame_triggerings().len(), 1);
336        let frame_triggering2 = channel
337            .trigger_frame(&frame2, 0x456, CanAddressingMode::Standard, CanFrameType::Can20)
338            .unwrap();
339        assert_eq!(frame2.frame_triggerings().len(), 1);
340        assert_eq!(channel.frame_triggerings().count(), 2);
341
342        // try to set an invalid identifier
343        let result = frame_triggering1.set_identifier(0xffff_ffff);
344        assert!(result.is_err());
345
346        // frame 1 already had a PDU mapped to it before it was connected to the channel, so a pdu triggering should have been created
347        assert_eq!(frame_triggering1.pdu_triggerings().count(), 1);
348        // frame 2 has no PDUs mapped to it, so it has no PDU triggerings
349        assert_eq!(frame_triggering2.pdu_triggerings().count(), 0);
350
351        // map a PDU to frame2 after it has been connected to the channel
352        let mapping2 = frame2
353            .map_pdu(&pdu2, 7, ByteOrder::MostSignificantByteFirst, None)
354            .unwrap();
355        assert!(frame2.mapped_pdus().count() == 1);
356        assert_eq!(frame2.mapped_pdus().next().unwrap(), mapping2);
357
358        // mapping the PDU to the connected frame should create a PDU triggering
359        assert_eq!(frame_triggering2.pdu_triggerings().count(), 1);
360
361        // connect the frame triggerings to an ECU
362        let port1 = frame_triggering1
363            .connect_to_ecu(&ecu_instance, CommunicationDirection::Out)
364            .unwrap();
365        let port2 = frame_triggering2
366            .connect_to_ecu(&ecu_instance, CommunicationDirection::In)
367            .unwrap();
368
369        assert_eq!(frame_triggering1.identifier().unwrap(), 0x123);
370        assert_eq!(
371            frame_triggering1.addressing_mode().unwrap(),
372            CanAddressingMode::Standard
373        );
374        assert_eq!(frame_triggering1.frame_type().unwrap(), CanFrameType::Can20);
375        assert_eq!(frame_triggering1.frame().unwrap(), frame1);
376        assert_eq!(frame_triggering1.physical_channel().unwrap(), channel);
377
378        assert_eq!(mapping1.pdu().unwrap(), pdu1.into());
379        assert_eq!(mapping1.byte_order().unwrap(), ByteOrder::MostSignificantByteFirst);
380        assert_eq!(mapping1.start_position().unwrap(), 7);
381        assert_eq!(mapping1.update_bit(), None);
382
383        assert_eq!(port1.ecu().unwrap(), ecu_instance);
384        assert_eq!(port1.communication_direction().unwrap(), CommunicationDirection::Out);
385        assert_eq!(port2.ecu().unwrap(), ecu_instance);
386        assert_eq!(port2.communication_direction().unwrap(), CommunicationDirection::In);
387        port2.set_communication_direction(CommunicationDirection::Out).unwrap();
388        assert_eq!(port2.communication_direction().unwrap(), CommunicationDirection::Out);
389    }
390}