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