autosar_data_abstraction/communication/cluster/
can.rs

1use crate::communication::{AbstractCluster, CanPhysicalChannel};
2use crate::{
3    AbstractionElement, ArPackage, AutosarAbstractionError, IdentifiableAbstractionElement, abstraction_element,
4};
5use autosar_data::{Element, ElementName};
6
7/// A `CanCluster` contains all configuration items associated with a CAN network.
8/// The cluster connects multiple ECUs.
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10pub struct CanCluster(Element);
11abstraction_element!(CanCluster, CanCluster);
12impl IdentifiableAbstractionElement for CanCluster {}
13
14impl CanCluster {
15    // create a new CanCluster - for internal use. User code should call System::create_can_cluster
16    pub(crate) fn new(
17        cluster_name: &str,
18        package: &ArPackage,
19        can_baudrate: Option<u32>,
20    ) -> Result<Self, AutosarAbstractionError> {
21        let elem_pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
22        let elem_cluster = elem_pkg_elements.create_named_sub_element(ElementName::CanCluster, cluster_name)?;
23        if let Ok(cluster_content) = elem_cluster
24            .create_sub_element(ElementName::CanClusterVariants)
25            .and_then(|ccv| ccv.create_sub_element(ElementName::CanClusterConditional))
26        {
27            let _ = cluster_content
28                .create_sub_element(ElementName::ProtocolName)
29                .and_then(|pn| pn.set_character_data("CAN"));
30
31            let _ = cluster_content.create_sub_element(ElementName::PhysicalChannels);
32        }
33
34        let can_cluster = CanCluster(elem_cluster);
35        can_cluster.set_baudrate(can_baudrate.unwrap_or(500_000))?;
36
37        Ok(can_cluster)
38    }
39
40    /// set the baudrate for this `CanCluster`
41    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), AutosarAbstractionError> {
42        self.0
43            .get_or_create_sub_element(ElementName::CanClusterVariants)
44            .and_then(|ccv| ccv.get_or_create_sub_element(ElementName::CanClusterConditional))
45            .and_then(|cc| cc.get_or_create_sub_element(ElementName::Baudrate))
46            .and_then(|br| br.set_character_data(baudrate as u64))?;
47        Ok(())
48    }
49
50    /// get the baudrate for this `CanCluster`
51    #[must_use]
52    pub fn baudrate(&self) -> Option<u32> {
53        self.0
54            .get_sub_element(ElementName::CanClusterVariants)
55            .and_then(|ccv| ccv.get_sub_element(ElementName::CanClusterConditional))
56            .and_then(|cc| cc.get_sub_element(ElementName::Baudrate))
57            .and_then(|br| br.character_data())
58            .and_then(|cdata| cdata.parse_integer())
59    }
60
61    /// set the baudrate for CAN FD for this `CanCluster`
62    pub fn set_can_fd_baudrate(&self, baudrate: Option<u32>) -> Result<(), AutosarAbstractionError> {
63        if let Some(baudrate) = baudrate {
64            self.0
65                .get_or_create_sub_element(ElementName::CanClusterVariants)
66                .and_then(|ccv| ccv.get_or_create_sub_element(ElementName::CanClusterConditional))
67                .and_then(|cc| cc.get_or_create_sub_element(ElementName::CanFdBaudrate))
68                .and_then(|br| br.set_character_data(baudrate as u64))?;
69        } else {
70            let _ = self
71                .0
72                .get_sub_element(ElementName::CanClusterVariants)
73                .and_then(|ccv| ccv.get_sub_element(ElementName::CanClusterConditional))
74                .and_then(|cc| cc.remove_sub_element_kind(ElementName::CanFdBaudrate).ok());
75        }
76        Ok(())
77    }
78
79    /// get the baudrate for CAN FD for this `CanCluster`
80    #[must_use]
81    pub fn can_fd_baudrate(&self) -> Option<u32> {
82        self.0
83            .get_sub_element(ElementName::CanClusterVariants)
84            .and_then(|ccv| ccv.get_sub_element(ElementName::CanClusterConditional))
85            .and_then(|cc| cc.get_sub_element(ElementName::CanFdBaudrate))
86            .and_then(|br| br.character_data())
87            .and_then(|cdata| cdata.parse_integer())
88    }
89
90    /// set the baudrate for CAN XL for this `CanCluster`
91    pub fn set_can_xl_baudrate(&self, baudrate: Option<u32>) -> Result<(), AutosarAbstractionError> {
92        if let Some(baudrate) = baudrate {
93            self.0
94                .get_or_create_sub_element(ElementName::CanClusterVariants)
95                .and_then(|ccv| ccv.get_or_create_sub_element(ElementName::CanClusterConditional))
96                .and_then(|cc| cc.get_or_create_sub_element(ElementName::CanXlBaudrate))
97                .and_then(|br| br.set_character_data(baudrate as u64))?;
98        } else {
99            let _ = self
100                .0
101                .get_sub_element(ElementName::CanClusterVariants)
102                .and_then(|ccv| ccv.get_sub_element(ElementName::CanClusterConditional))
103                .and_then(|cc| cc.remove_sub_element_kind(ElementName::CanXlBaudrate).ok());
104        }
105        Ok(())
106    }
107
108    /// get the baudrate for CAN XL for this `CanCluster`
109    #[must_use]
110    pub fn can_xl_baudrate(&self) -> Option<u32> {
111        self.0
112            .get_sub_element(ElementName::CanClusterVariants)
113            .and_then(|ccv| ccv.get_sub_element(ElementName::CanClusterConditional))
114            .and_then(|cc| cc.get_sub_element(ElementName::CanXlBaudrate))
115            .and_then(|br| br.character_data())
116            .and_then(|cdata| cdata.parse_integer())
117    }
118
119    /// Create a new physical channel for the cluster
120    ///
121    /// A can cluster must contain exactly one physical channel; trying to add a second one triggers an error.
122    ///
123    /// # Example
124    ///
125    /// ```
126    /// # use autosar_data::*;
127    /// # use autosar_data_abstraction::*;
128    /// # use autosar_data_abstraction::communication::*;
129    /// # fn main() -> Result<(), AutosarAbstractionError> {
130    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
131    /// # let package = model.get_or_create_package("/pkg1")?;
132    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
133    /// let cluster = system.create_can_cluster("Cluster", &package, None)?;
134    /// let channel = cluster.create_physical_channel("Channel")?;
135    /// # Ok(())}
136    /// ```
137    ///
138    /// # Errors
139    ///
140    /// - [`AutosarAbstractionError::ItemAlreadyExists`] There is already a physical channel in this CAN cluster
141    /// - [`AutosarAbstractionError::ModelError`] An error occurred in the Autosar model while trying to create the ECU-INSTANCE
142    pub fn create_physical_channel(&self, channel_name: &str) -> Result<CanPhysicalChannel, AutosarAbstractionError> {
143        let phys_channels = self
144            .0
145            .get_or_create_sub_element(ElementName::CanClusterVariants)?
146            .get_or_create_sub_element(ElementName::CanClusterConditional)?
147            .get_or_create_sub_element(ElementName::PhysicalChannels)?;
148
149        if phys_channels.sub_elements().count() != 0 {
150            return Err(AutosarAbstractionError::ItemAlreadyExists);
151        }
152
153        let channel = phys_channels.create_named_sub_element(ElementName::CanPhysicalChannel, channel_name)?;
154
155        CanPhysicalChannel::try_from(channel)
156    }
157
158    /// return the `CanPhysicalChannel` of the Cluster, if it has been created
159    ///
160    /// # Example
161    ///
162    /// ```
163    /// # use autosar_data::*;
164    /// # use autosar_data_abstraction::*;
165    /// # use autosar_data_abstraction::communication::*;
166    /// # fn main() -> Result<(), AutosarAbstractionError> {
167    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
168    /// # let package = model.get_or_create_package("/pkg1")?;
169    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
170    /// # let cluster = system.create_can_cluster("Cluster", &package, None)?;
171    /// # let can_channel = cluster.create_physical_channel("Channel")?;
172    /// if let Some(channel) = cluster.physical_channel() {
173    /// #   assert_eq!(channel, can_channel);
174    /// }
175    /// # Ok(())}
176    /// ````
177    #[must_use]
178    pub fn physical_channel(&self) -> Option<CanPhysicalChannel> {
179        let channel = self
180            .0
181            .get_sub_element(ElementName::CanClusterVariants)?
182            .get_sub_element(ElementName::CanClusterConditional)?
183            .get_sub_element(ElementName::PhysicalChannels)?
184            .get_sub_element(ElementName::CanPhysicalChannel)?;
185        CanPhysicalChannel::try_from(channel).ok()
186    }
187}
188
189impl AbstractCluster for CanCluster {}
190
191//##################################################################
192
193#[cfg(test)]
194mod test {
195    use crate::{AutosarModelAbstraction, SystemCategory, communication::AbstractCluster};
196    use autosar_data::AutosarVersion;
197
198    #[test]
199    fn cluster() {
200        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00051);
201        let pkg = model.get_or_create_package("/test").unwrap();
202        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
203
204        let pkg2 = model.get_or_create_package("/can").unwrap();
205        // create the CAN cluster CanCluster
206        let result = system.create_can_cluster("CanCluster", &pkg2, None);
207        assert!(result.is_ok());
208        let cluster = result.unwrap();
209        // creating the same cluster again is not possible
210        let result = system.create_can_cluster("CanCluster", &pkg2, None);
211        assert!(result.is_err());
212
213        // system link
214        let linked_system = cluster.system().unwrap();
215        assert_eq!(linked_system, system);
216
217        // settings for CanFd
218        cluster.set_baudrate(250_000).unwrap();
219        assert_eq!(cluster.baudrate().unwrap(), 250_000);
220        cluster.set_can_fd_baudrate(Some(2_000_000)).unwrap();
221        assert_eq!(cluster.can_fd_baudrate().unwrap(), 2_000_000);
222        cluster.set_can_xl_baudrate(Some(10_000_000)).unwrap();
223        assert_eq!(cluster.can_xl_baudrate().unwrap(), 10_000_000);
224        // remove CanFd settings
225        cluster.set_can_fd_baudrate(None).unwrap();
226        assert!(cluster.can_fd_baudrate().is_none());
227        // remove CanXl settings
228        cluster.set_can_xl_baudrate(None).unwrap();
229        assert!(cluster.can_xl_baudrate().is_none());
230
231        // create a channel
232        let result = cluster.create_physical_channel("Channel1");
233        assert!(result.is_ok());
234        // can't create a second channel
235        let result = cluster.create_physical_channel("Channel2");
236        assert!(result.is_err());
237
238        let pc = cluster.physical_channel();
239        assert!(pc.is_some());
240    }
241}