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, is_used_system_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    /// remove this `CanFrame` from the model
32    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
33        for pdu_mapping in self.mapped_pdus() {
34            pdu_mapping.remove(deep)?;
35        }
36
37        // get all frame triggerings using this frame
38        let frame_triggerings = self.frame_triggerings();
39
40        // remove the element itself
41        AbstractionElement::remove(self, deep)?;
42
43        // remove the frame triggerings
44        for ft in frame_triggerings {
45            ft.remove(deep)?;
46        }
47
48        Ok(())
49    }
50}
51
52impl AbstractFrame for CanFrame {
53    type FrameTriggeringType = CanFrameTriggering;
54
55    /// List all [`CanFrameTriggering`]s using this frame
56    fn frame_triggerings(&self) -> Vec<CanFrameTriggering> {
57        let model_result = self.element().model();
58        let path_result = self.element().path();
59        if let (Ok(model), Ok(path)) = (model_result, path_result) {
60            model
61                .get_references_to(&path)
62                .iter()
63                .filter_map(|e| {
64                    e.upgrade()
65                        .and_then(|ref_elem| ref_elem.named_parent().ok().flatten())
66                        .and_then(|elem| CanFrameTriggering::try_from(elem).ok())
67                })
68                .collect()
69        } else {
70            vec![]
71        }
72    }
73
74    /// map a PDU to the frame
75    fn map_pdu<T: AbstractPdu>(
76        &self,
77        gen_pdu: &T,
78        start_position: u32,
79        byte_order: ByteOrder,
80        update_bit: Option<u32>,
81    ) -> Result<PduToFrameMapping, AutosarAbstractionError> {
82        Frame::Can(self.clone()).map_pdu(gen_pdu, start_position, byte_order, update_bit)
83    }
84}
85
86//##################################################################
87
88/// The frame triggering connects a frame to a physical channel
89#[derive(Debug, Clone, PartialEq, Eq, Hash)]
90pub struct CanFrameTriggering(Element);
91abstraction_element!(CanFrameTriggering, CanFrameTriggering);
92impl IdentifiableAbstractionElement for CanFrameTriggering {}
93
94impl CanFrameTriggering {
95    pub(crate) fn new(
96        channel: &CanPhysicalChannel,
97        frame: &CanFrame,
98        identifier: u32,
99        addressing_mode: CanAddressingMode,
100        frame_type: CanFrameType,
101    ) -> Result<Self, AutosarAbstractionError> {
102        let model = channel.element().model()?;
103        let base_path = channel.element().path()?;
104        let frame_name = frame
105            .name()
106            .ok_or(AutosarAbstractionError::InvalidParameter("invalid frame".to_string()))?;
107        let ft_name = format!("FT_{frame_name}");
108        let ft_name = make_unique_name(&model, &base_path, &ft_name);
109
110        let frame_triggerings = channel
111            .element()
112            .get_or_create_sub_element(ElementName::FrameTriggerings)?;
113        let can_triggering = frame_triggerings.create_named_sub_element(ElementName::CanFrameTriggering, &ft_name)?;
114
115        can_triggering
116            .create_sub_element(ElementName::FrameRef)?
117            .set_reference_target(frame.element())?;
118
119        let ft = Self(can_triggering);
120        ft.set_addressing_mode(addressing_mode)?;
121        ft.set_frame_type(frame_type)?;
122        if let Err(error) = ft.set_identifier(identifier) {
123            let _ = frame_triggerings.remove_sub_element(ft.0);
124            return Err(error);
125        }
126
127        for pdu_mapping in frame.mapped_pdus() {
128            if let Some(pdu) = pdu_mapping.pdu() {
129                ft.add_pdu_triggering(&pdu)?;
130            }
131        }
132
133        Ok(ft)
134    }
135
136    /// remove this `CanFrameTriggering` from the model
137    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
138        let opt_frame = self.frame();
139
140        // remove all pdu triggerings of this frame triggering
141        for pt in self.pdu_triggerings() {
142            pt.remove(deep)?;
143        }
144        for frame_port in self.frame_ports() {
145            frame_port.remove(deep)?;
146        }
147
148        AbstractionElement::remove(self, deep)?;
149
150        // if deep, check if the frame became unused because of this frame triggering removal
151        // if so remove it too
152        if deep && let Some(frame) = opt_frame {
153            // check if any frame became unused because of this frame triggering removal
154            // if so remove it too
155            if !is_used_system_element(frame.element()) {
156                frame.remove(deep)?;
157            }
158        }
159
160        Ok(())
161    }
162
163    /// set the can id associated with this frame
164    pub fn set_identifier(&self, identifier: u32) -> Result<(), AutosarAbstractionError> {
165        let amode = self.addressing_mode().unwrap_or(CanAddressingMode::Standard);
166        if amode == CanAddressingMode::Standard && identifier > 0x7ff {
167            return Err(AutosarAbstractionError::InvalidParameter(format!(
168                "CAN-ID {identifier} is outside the 11-bit range allowed by standard addressing"
169            )));
170        } else if identifier > 0x1fff_ffff {
171            return Err(AutosarAbstractionError::InvalidParameter(format!(
172                "CAN-ID {identifier} is outside the 29-bit range allowed by extended addressing"
173            )));
174        }
175        self.element()
176            .get_or_create_sub_element(ElementName::Identifier)?
177            .set_character_data(identifier.to_string())?;
178
179        Ok(())
180    }
181
182    /// get the can id associated with this frame triggering
183    #[must_use]
184    pub fn identifier(&self) -> Option<u32> {
185        self.element()
186            .get_sub_element(ElementName::Identifier)?
187            .character_data()?
188            .parse_integer()
189    }
190
191    /// set the addressing mode for this frame triggering
192    pub fn set_addressing_mode(&self, addressing_mode: CanAddressingMode) -> Result<(), AutosarAbstractionError> {
193        self.element()
194            .get_or_create_sub_element(ElementName::CanAddressingMode)?
195            .set_character_data::<EnumItem>(addressing_mode.into())?;
196
197        Ok(())
198    }
199
200    /// get the addressing mode for this frame triggering
201    #[must_use]
202    pub fn addressing_mode(&self) -> Option<CanAddressingMode> {
203        self.element()
204            .get_sub_element(ElementName::CanAddressingMode)?
205            .character_data()?
206            .enum_value()?
207            .try_into()
208            .ok()
209    }
210
211    /// set the frame type for this frame triggering
212    pub fn set_frame_type(&self, frame_type: CanFrameType) -> Result<(), AutosarAbstractionError> {
213        self.element()
214            .get_or_create_sub_element(ElementName::CanFrameRxBehavior)?
215            .set_character_data::<EnumItem>(frame_type.into())?;
216        self.element()
217            .get_or_create_sub_element(ElementName::CanFrameTxBehavior)?
218            .set_character_data::<EnumItem>(frame_type.into())?;
219
220        Ok(())
221    }
222
223    /// get the frame type for this frame triggering
224    #[must_use]
225    pub fn frame_type(&self) -> Option<CanFrameType> {
226        self.element()
227            .get_sub_element(ElementName::CanFrameTxBehavior)?
228            .character_data()?
229            .enum_value()?
230            .try_into()
231            .ok()
232    }
233
234    pub(crate) fn add_pdu_triggering(&self, pdu: &Pdu) -> Result<PduTriggering, AutosarAbstractionError> {
235        FrameTriggering::Can(self.clone()).add_pdu_triggering(pdu)
236    }
237
238    /// get the physical channel that contains this frame triggering
239    pub fn physical_channel(&self) -> Result<CanPhysicalChannel, AutosarAbstractionError> {
240        let channel_elem = self.element().named_parent()?.unwrap();
241        CanPhysicalChannel::try_from(channel_elem)
242    }
243
244    /// connect this frame triggering to an ECU
245    ///
246    /// The direction parameter specifies if the communication is incoming or outgoing
247    pub fn connect_to_ecu(
248        &self,
249        ecu: &EcuInstance,
250        direction: CommunicationDirection,
251    ) -> Result<FramePort, AutosarAbstractionError> {
252        FrameTriggering::Can(self.clone()).connect_to_ecu(ecu, direction)
253    }
254}
255
256impl AbstractFrameTriggering for CanFrameTriggering {
257    type FrameType = CanFrame;
258}
259
260impl From<CanFrameTriggering> for FrameTriggering {
261    fn from(cft: CanFrameTriggering) -> Self {
262        FrameTriggering::Can(cft)
263    }
264}
265
266//##################################################################
267
268/// The addressing mode for a CAN frame
269#[derive(Debug, Clone, Copy, PartialEq, Eq)]
270pub enum CanAddressingMode {
271    /// Standard addressing mode: 11-bit identifier
272    Standard,
273    /// Extended addressing mode: 29-bit identifier
274    Extended,
275}
276
277impl TryFrom<EnumItem> for CanAddressingMode {
278    type Error = AutosarAbstractionError;
279
280    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
281        match value {
282            EnumItem::Standard => Ok(CanAddressingMode::Standard),
283            EnumItem::Extended => Ok(CanAddressingMode::Extended),
284            _ => Err(AutosarAbstractionError::ValueConversionError {
285                value: value.to_string(),
286                dest: "CanAddressingMode".to_string(),
287            }),
288        }
289    }
290}
291
292impl From<CanAddressingMode> for EnumItem {
293    fn from(value: CanAddressingMode) -> Self {
294        match value {
295            CanAddressingMode::Standard => EnumItem::Standard,
296            CanAddressingMode::Extended => EnumItem::Extended,
297        }
298    }
299}
300
301//##################################################################
302
303/// The type of a CAN frame
304#[derive(Debug, Clone, Copy, PartialEq, Eq)]
305pub enum CanFrameType {
306    /// CAN 2.0 frame (max 8 bytes)
307    Can20,
308    /// CAN FD frame (max 64 bytes, transmitted at the `CanFD` baud rate)
309    CanFd,
310    /// Any CAN frame
311    Any,
312}
313
314impl TryFrom<EnumItem> for CanFrameType {
315    type Error = AutosarAbstractionError;
316
317    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
318        match value {
319            EnumItem::Can20 => Ok(CanFrameType::Can20),
320            EnumItem::CanFd => Ok(CanFrameType::CanFd),
321            EnumItem::Any => Ok(CanFrameType::Any),
322            _ => Err(AutosarAbstractionError::ValueConversionError {
323                value: value.to_string(),
324                dest: "CanFrameType".to_string(),
325            }),
326        }
327    }
328}
329
330impl From<CanFrameType> for EnumItem {
331    fn from(value: CanFrameType) -> Self {
332        match value {
333            CanFrameType::Can20 => EnumItem::Can20,
334            CanFrameType::CanFd => EnumItem::CanFd,
335            CanFrameType::Any => EnumItem::Any,
336        }
337    }
338}
339
340//##################################################################
341
342#[cfg(test)]
343mod test {
344    use super::*;
345    use crate::{AutosarModelAbstraction, ByteOrder, SystemCategory, communication::AbstractPhysicalChannel};
346    use autosar_data::AutosarVersion;
347
348    #[test]
349    fn can_frame() {
350        let model = AutosarModelAbstraction::create("test", AutosarVersion::LATEST);
351        let package = model.get_or_create_package("/package").unwrap();
352        let system = package.create_system("System", SystemCategory::EcuExtract).unwrap();
353        let can_cluster = system.create_can_cluster("Cluster", &package, None).unwrap();
354        let channel = can_cluster.create_physical_channel("Channel").unwrap();
355
356        let ecu_instance = system.create_ecu_instance("ECU", &package).unwrap();
357        let can_controller = ecu_instance.create_can_communication_controller("Controller").unwrap();
358        can_controller.connect_physical_channel("connection", &channel).unwrap();
359        assert_eq!(channel.connectors().count(), 1);
360        assert_eq!(channel.connectors().next().unwrap().name().unwrap(), "connection");
361
362        let pdu1 = system.create_isignal_ipdu("pdu1", &package, 8).unwrap();
363        let pdu2 = system.create_isignal_ipdu("pdu2", &package, 8).unwrap();
364
365        // create frames
366        let frame1 = system.create_can_frame("frame1", &package, 8).unwrap();
367        let frame2 = system.create_can_frame("frame2", &package, 8).unwrap();
368
369        assert_eq!(frame1.length().unwrap(), 8);
370        frame1.set_length(6).unwrap();
371        assert_eq!(frame1.length().unwrap(), 6);
372
373        // map a PDU to the frame before it has been connected to the channel
374        let mapping1 = frame1
375            .map_pdu(&pdu1, 7, ByteOrder::MostSignificantByteFirst, None)
376            .unwrap();
377        assert!(frame1.mapped_pdus().count() == 1);
378        assert_eq!(frame1.mapped_pdus().next().unwrap(), mapping1);
379
380        // trigger both frames
381        let frame_triggering1 = channel
382            .trigger_frame(&frame1, 0x123, CanAddressingMode::Standard, CanFrameType::Can20)
383            .unwrap();
384        assert_eq!(frame1.frame_triggerings().len(), 1);
385        let frame_triggering2 = channel
386            .trigger_frame(&frame2, 0x456, CanAddressingMode::Standard, CanFrameType::Can20)
387            .unwrap();
388        assert_eq!(frame2.frame_triggerings().len(), 1);
389        assert_eq!(channel.frame_triggerings().count(), 2);
390
391        // try to set an invalid identifier
392        let result = frame_triggering1.set_identifier(0xffff_ffff);
393        assert!(result.is_err());
394
395        // frame 1 already had a PDU mapped to it before it was connected to the channel, so a pdu triggering should have been created
396        assert_eq!(frame_triggering1.pdu_triggerings().count(), 1);
397        // frame 2 has no PDUs mapped to it, so it has no PDU triggerings
398        assert_eq!(frame_triggering2.pdu_triggerings().count(), 0);
399
400        // map a PDU to frame2 after it has been connected to the channel
401        let mapping2 = frame2
402            .map_pdu(&pdu2, 7, ByteOrder::MostSignificantByteFirst, None)
403            .unwrap();
404        assert!(frame2.mapped_pdus().count() == 1);
405        assert_eq!(frame2.mapped_pdus().next().unwrap(), mapping2);
406
407        // mapping the PDU to the connected frame should create a PDU triggering
408        assert_eq!(frame_triggering2.pdu_triggerings().count(), 1);
409
410        // connect the frame triggerings to an ECU
411        let port1 = frame_triggering1
412            .connect_to_ecu(&ecu_instance, CommunicationDirection::Out)
413            .unwrap();
414        let port2 = frame_triggering2
415            .connect_to_ecu(&ecu_instance, CommunicationDirection::In)
416            .unwrap();
417
418        assert_eq!(frame_triggering1.identifier().unwrap(), 0x123);
419        assert_eq!(
420            frame_triggering1.addressing_mode().unwrap(),
421            CanAddressingMode::Standard
422        );
423        assert_eq!(frame_triggering1.frame_type().unwrap(), CanFrameType::Can20);
424        assert_eq!(frame_triggering1.frame().unwrap(), frame1);
425        assert_eq!(frame_triggering1.physical_channel().unwrap(), channel);
426
427        assert_eq!(mapping1.pdu().unwrap(), pdu1.into());
428        assert_eq!(mapping1.byte_order().unwrap(), ByteOrder::MostSignificantByteFirst);
429        assert_eq!(mapping1.start_position().unwrap(), 7);
430        assert_eq!(mapping1.update_bit(), None);
431
432        assert_eq!(port1.ecu().unwrap(), ecu_instance);
433        assert_eq!(port1.communication_direction().unwrap(), CommunicationDirection::Out);
434        assert_eq!(port2.ecu().unwrap(), ecu_instance);
435        assert_eq!(port2.communication_direction().unwrap(), CommunicationDirection::In);
436        port2.set_communication_direction(CommunicationDirection::Out).unwrap();
437        assert_eq!(port2.communication_direction().unwrap(), CommunicationDirection::Out);
438    }
439
440    #[test]
441    fn remove_frame_triggering() {
442        let model = AutosarModelAbstraction::create("test", AutosarVersion::LATEST);
443        let package = model.get_or_create_package("/package").unwrap();
444        let system = package.create_system("System", SystemCategory::EcuExtract).unwrap();
445        let can_cluster = system.create_can_cluster("Cluster", &package, None).unwrap();
446        let channel = can_cluster.create_physical_channel("Channel").unwrap();
447
448        let frame = system.create_can_frame("frame", &package, 8).unwrap();
449        let pdu = system.create_isignal_ipdu("pdu", &package, 8).unwrap();
450
451        let frame_triggering = channel
452            .trigger_frame(&frame, 0x123, CanAddressingMode::Standard, CanFrameType::Can20)
453            .unwrap();
454
455        let _mapping = frame
456            .map_pdu(&pdu, 0, ByteOrder::MostSignificantByteLast, None)
457            .unwrap();
458
459        assert_eq!(frame.mapped_pdus().count(), 1);
460        assert_eq!(frame.frame_triggerings().len(), 1);
461        assert_eq!(channel.frame_triggerings().count(), 1);
462
463        // remove the frame triggering
464        frame_triggering.remove(false).unwrap();
465        // the frame remains because we did a shallow removal
466        assert_eq!(system.frames().count(), 1);
467
468        // re-create the frame triggering
469        let frame_triggering = channel
470            .trigger_frame(&frame, 0x123, CanAddressingMode::Standard, CanFrameType::Can20)
471            .unwrap();
472        // remove the frame triggering with deep=true
473        frame_triggering.remove(true).unwrap();
474
475        // the frame triggering should be removed
476        assert_eq!(channel.frame_triggerings().count(), 0);
477        // the frame should be removed because it became unused
478        assert_eq!(system.frames().count(), 0);
479        // the mapping should be removed because the frame was removed
480        assert_eq!(frame.mapped_pdus().count(), 0);
481        // the pdu was also removed, because it became unused and we did a deep removal
482        assert_eq!(system.pdus().count(), 0);
483
484        assert_eq!(channel.frame_triggerings().count(), 0);
485        assert_eq!(channel.pdu_triggerings().count(), 0);
486    }
487
488    #[test]
489    fn remove_frame() {
490        let model = AutosarModelAbstraction::create("test", AutosarVersion::LATEST);
491        let package = model.get_or_create_package("/package").unwrap();
492        let system = package.create_system("System", SystemCategory::EcuExtract).unwrap();
493        let can_cluster = system.create_can_cluster("Cluster", &package, None).unwrap();
494        let channel = can_cluster.create_physical_channel("Channel").unwrap();
495        let frame = system.create_can_frame("frame", &package, 8).unwrap();
496        let pdu = system.create_isignal_ipdu("pdu", &package, 8).unwrap();
497        let frame_triggering = channel
498            .trigger_frame(&frame, 0x123, CanAddressingMode::Standard, CanFrameType::Can20)
499            .unwrap();
500        let mapping = frame
501            .map_pdu(&pdu, 0, ByteOrder::MostSignificantByteLast, None)
502            .unwrap();
503        assert_eq!(frame.mapped_pdus().count(), 1);
504        assert_eq!(frame.frame_triggerings().len(), 1);
505        assert_eq!(channel.frame_triggerings().count(), 1);
506        // remove the frame with deep=false
507        frame.remove(false).unwrap();
508        // the frame should be removed
509        assert_eq!(system.frames().count(), 0);
510        // the mapping should be removed
511        assert!(mapping.element().path().is_err());
512        // the pdu should still exist
513        assert_eq!(system.pdus().count(), 1);
514        // the frame triggering should be removed
515        assert!(frame_triggering.element().path().is_err());
516        assert_eq!(channel.frame_triggerings().count(), 0);
517        assert_eq!(channel.pdu_triggerings().count(), 0);
518    }
519}