autosar_data_abstraction/communication/cluster/
can.rs

1use crate::communication::{AbstractCluster, CanNmCluster, CanPhysicalChannel};
2use crate::{
3    AbstractionElement, ArPackage, AutosarAbstractionError, IdentifiableAbstractionElement, abstraction_element,
4    get_reference_parents,
5};
6use autosar_data::{Element, ElementName};
7
8/// A `CanCluster` contains all configuration items associated with a CAN network.
9/// The cluster connects multiple ECUs.
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct CanCluster(Element);
12abstraction_element!(CanCluster, CanCluster);
13impl IdentifiableAbstractionElement for CanCluster {}
14
15impl CanCluster {
16    // create a new CanCluster - for internal use. User code should call System::create_can_cluster
17    pub(crate) fn new(
18        cluster_name: &str,
19        package: &ArPackage,
20        can_baudrate: Option<u32>,
21    ) -> Result<Self, AutosarAbstractionError> {
22        let elem_pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
23        let elem_cluster = elem_pkg_elements.create_named_sub_element(ElementName::CanCluster, cluster_name)?;
24        if let Ok(cluster_content) = elem_cluster
25            .create_sub_element(ElementName::CanClusterVariants)
26            .and_then(|ccv| ccv.create_sub_element(ElementName::CanClusterConditional))
27        {
28            let _ = cluster_content
29                .create_sub_element(ElementName::ProtocolName)
30                .and_then(|pn| pn.set_character_data("CAN"));
31
32            let _ = cluster_content.create_sub_element(ElementName::PhysicalChannels);
33        }
34
35        let can_cluster = CanCluster(elem_cluster);
36        can_cluster.set_baudrate(can_baudrate.unwrap_or(500_000))?;
37
38        Ok(can_cluster)
39    }
40
41    /// remove this `CanCluster` from the model
42    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
43        // remove the physical channel, if existing
44        if let Some(channel) = self.physical_channel() {
45            channel.remove(deep)?;
46        }
47        let ref_parents = get_reference_parents(&self.0)?;
48
49        // delegate to the trait implementation to clean up all other references to the element and the element itself
50        AbstractionElement::remove(self, deep)?;
51
52        // check if any CanTpConfig or CanNmCluster uses this CanCluster
53        // In both the cluster reference is mandatory, so we remove them together with the cluster
54        for (named_parent, _parent) in ref_parents {
55            match named_parent.element_name() {
56                ElementName::CanTpConfig => {
57                    if let Ok(can_tp_config) = crate::communication::CanTpConfig::try_from(named_parent) {
58                        can_tp_config.remove(deep)?;
59                    }
60                }
61                ElementName::CanNmCluster => {
62                    if let Ok(can_nm_cluster) = CanNmCluster::try_from(named_parent) {
63                        can_nm_cluster.remove(deep)?;
64                    }
65                }
66                _ => {}
67            }
68        }
69
70        Ok(())
71    }
72
73    /// set the baudrate for this `CanCluster`
74    pub fn set_baudrate(&self, baudrate: u32) -> Result<(), AutosarAbstractionError> {
75        self.0
76            .get_or_create_sub_element(ElementName::CanClusterVariants)
77            .and_then(|ccv| ccv.get_or_create_sub_element(ElementName::CanClusterConditional))
78            .and_then(|cc| cc.get_or_create_sub_element(ElementName::Baudrate))
79            .and_then(|br| br.set_character_data(baudrate as u64))?;
80        Ok(())
81    }
82
83    /// get the baudrate for this `CanCluster`
84    #[must_use]
85    pub fn baudrate(&self) -> Option<u32> {
86        self.0
87            .get_sub_element(ElementName::CanClusterVariants)
88            .and_then(|ccv| ccv.get_sub_element(ElementName::CanClusterConditional))
89            .and_then(|cc| cc.get_sub_element(ElementName::Baudrate))
90            .and_then(|br| br.character_data())
91            .and_then(|cdata| cdata.parse_integer())
92    }
93
94    /// set the baudrate for CAN FD for this `CanCluster`
95    pub fn set_can_fd_baudrate(&self, baudrate: Option<u32>) -> Result<(), AutosarAbstractionError> {
96        if let Some(baudrate) = baudrate {
97            self.0
98                .get_or_create_sub_element(ElementName::CanClusterVariants)
99                .and_then(|ccv| ccv.get_or_create_sub_element(ElementName::CanClusterConditional))
100                .and_then(|cc| cc.get_or_create_sub_element(ElementName::CanFdBaudrate))
101                .and_then(|br| br.set_character_data(baudrate as u64))?;
102        } else {
103            let _ = self
104                .0
105                .get_sub_element(ElementName::CanClusterVariants)
106                .and_then(|ccv| ccv.get_sub_element(ElementName::CanClusterConditional))
107                .and_then(|cc| cc.remove_sub_element_kind(ElementName::CanFdBaudrate).ok());
108        }
109        Ok(())
110    }
111
112    /// get the baudrate for CAN FD for this `CanCluster`
113    #[must_use]
114    pub fn can_fd_baudrate(&self) -> Option<u32> {
115        self.0
116            .get_sub_element(ElementName::CanClusterVariants)
117            .and_then(|ccv| ccv.get_sub_element(ElementName::CanClusterConditional))
118            .and_then(|cc| cc.get_sub_element(ElementName::CanFdBaudrate))
119            .and_then(|br| br.character_data())
120            .and_then(|cdata| cdata.parse_integer())
121    }
122
123    /// set the baudrate for CAN XL for this `CanCluster`
124    pub fn set_can_xl_baudrate(&self, baudrate: Option<u32>) -> Result<(), AutosarAbstractionError> {
125        if let Some(baudrate) = baudrate {
126            self.0
127                .get_or_create_sub_element(ElementName::CanClusterVariants)
128                .and_then(|ccv| ccv.get_or_create_sub_element(ElementName::CanClusterConditional))
129                .and_then(|cc| cc.get_or_create_sub_element(ElementName::CanXlBaudrate))
130                .and_then(|br| br.set_character_data(baudrate as u64))?;
131        } else {
132            let _ = self
133                .0
134                .get_sub_element(ElementName::CanClusterVariants)
135                .and_then(|ccv| ccv.get_sub_element(ElementName::CanClusterConditional))
136                .and_then(|cc| cc.remove_sub_element_kind(ElementName::CanXlBaudrate).ok());
137        }
138        Ok(())
139    }
140
141    /// get the baudrate for CAN XL for this `CanCluster`
142    #[must_use]
143    pub fn can_xl_baudrate(&self) -> Option<u32> {
144        self.0
145            .get_sub_element(ElementName::CanClusterVariants)
146            .and_then(|ccv| ccv.get_sub_element(ElementName::CanClusterConditional))
147            .and_then(|cc| cc.get_sub_element(ElementName::CanXlBaudrate))
148            .and_then(|br| br.character_data())
149            .and_then(|cdata| cdata.parse_integer())
150    }
151
152    /// Create a new physical channel for the cluster
153    ///
154    /// A can cluster must contain exactly one physical channel; trying to add a second one triggers an error.
155    ///
156    /// # Example
157    ///
158    /// ```
159    /// # use autosar_data::*;
160    /// # use autosar_data_abstraction::*;
161    /// # use autosar_data_abstraction::communication::*;
162    /// # fn main() -> Result<(), AutosarAbstractionError> {
163    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
164    /// # let package = model.get_or_create_package("/pkg1")?;
165    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
166    /// let cluster = system.create_can_cluster("Cluster", &package, None)?;
167    /// let channel = cluster.create_physical_channel("Channel")?;
168    /// # Ok(())}
169    /// ```
170    ///
171    /// # Errors
172    ///
173    /// - [`AutosarAbstractionError::ItemAlreadyExists`] There is already a physical channel in this CAN cluster
174    /// - [`AutosarAbstractionError::ModelError`] An error occurred in the Autosar model while trying to create the ECU-INSTANCE
175    pub fn create_physical_channel(&self, channel_name: &str) -> Result<CanPhysicalChannel, AutosarAbstractionError> {
176        let phys_channels = self
177            .0
178            .get_or_create_sub_element(ElementName::CanClusterVariants)?
179            .get_or_create_sub_element(ElementName::CanClusterConditional)?
180            .get_or_create_sub_element(ElementName::PhysicalChannels)?;
181
182        if phys_channels.sub_elements().count() != 0 {
183            return Err(AutosarAbstractionError::ItemAlreadyExists);
184        }
185
186        let channel = phys_channels.create_named_sub_element(ElementName::CanPhysicalChannel, channel_name)?;
187
188        CanPhysicalChannel::try_from(channel)
189    }
190
191    /// return the `CanPhysicalChannel` of the Cluster, if it has been created
192    ///
193    /// # Example
194    ///
195    /// ```
196    /// # use autosar_data::*;
197    /// # use autosar_data_abstraction::*;
198    /// # use autosar_data_abstraction::communication::*;
199    /// # fn main() -> Result<(), AutosarAbstractionError> {
200    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
201    /// # let package = model.get_or_create_package("/pkg1")?;
202    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
203    /// # let cluster = system.create_can_cluster("Cluster", &package, None)?;
204    /// # let can_channel = cluster.create_physical_channel("Channel")?;
205    /// if let Some(channel) = cluster.physical_channel() {
206    /// #   assert_eq!(channel, can_channel);
207    /// }
208    /// # Ok(())}
209    /// ```
210    #[must_use]
211    pub fn physical_channel(&self) -> Option<CanPhysicalChannel> {
212        let channel = self
213            .0
214            .get_sub_element(ElementName::CanClusterVariants)?
215            .get_sub_element(ElementName::CanClusterConditional)?
216            .get_sub_element(ElementName::PhysicalChannels)?
217            .get_sub_element(ElementName::CanPhysicalChannel)?;
218        CanPhysicalChannel::try_from(channel).ok()
219    }
220}
221
222impl AbstractCluster for CanCluster {}
223
224//##################################################################
225
226#[cfg(test)]
227mod test {
228    use crate::{
229        AbstractionElement, AutosarModelAbstraction, SystemCategory,
230        communication::{AbstractCluster, CanNmClusterSettings},
231    };
232    use autosar_data::AutosarVersion;
233
234    #[test]
235    fn cluster() {
236        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00051);
237        let pkg = model.get_or_create_package("/test").unwrap();
238        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
239
240        let pkg2 = model.get_or_create_package("/can").unwrap();
241        // create the CAN cluster CanCluster
242        let result = system.create_can_cluster("CanCluster", &pkg2, None);
243        assert!(result.is_ok());
244        let cluster = result.unwrap();
245        // creating the same cluster again is not possible
246        let result = system.create_can_cluster("CanCluster", &pkg2, None);
247        assert!(result.is_err());
248
249        // system link
250        let linked_system = cluster.system().unwrap();
251        assert_eq!(linked_system, system);
252
253        // settings for CanFd
254        cluster.set_baudrate(250_000).unwrap();
255        assert_eq!(cluster.baudrate().unwrap(), 250_000);
256        cluster.set_can_fd_baudrate(Some(2_000_000)).unwrap();
257        assert_eq!(cluster.can_fd_baudrate().unwrap(), 2_000_000);
258        cluster.set_can_xl_baudrate(Some(10_000_000)).unwrap();
259        assert_eq!(cluster.can_xl_baudrate().unwrap(), 10_000_000);
260        // remove CanFd settings
261        cluster.set_can_fd_baudrate(None).unwrap();
262        assert!(cluster.can_fd_baudrate().is_none());
263        // remove CanXl settings
264        cluster.set_can_xl_baudrate(None).unwrap();
265        assert!(cluster.can_xl_baudrate().is_none());
266
267        // create a channel
268        let result = cluster.create_physical_channel("Channel1");
269        assert!(result.is_ok());
270        // can't create a second channel
271        let result = cluster.create_physical_channel("Channel2");
272        assert!(result.is_err());
273
274        let pc = cluster.physical_channel();
275        assert!(pc.is_some());
276    }
277
278    #[test]
279    fn remove_cluster() {
280        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00051);
281        let pkg = model.get_or_create_package("/test").unwrap();
282        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
283
284        let pkg2 = model.get_or_create_package("/can").unwrap();
285        let cluster = system.create_can_cluster("CanCluster", &pkg2, None).unwrap();
286        let channel = cluster.create_physical_channel("Channel1").unwrap();
287
288        let nm_config = system.create_nm_config("NmConfig", &pkg2).unwrap();
289        let can_nm_cluster_settings = CanNmClusterSettings {
290            nm_busload_reduction_active: false,
291            nm_immediate_nm_transmissions: 5,
292            nm_message_timeout_time: 5.0,
293            nm_msg_cycle_time: 5.0,
294            nm_network_timeout: 8.0,
295            nm_remote_sleep_indication_time: 3.0,
296            nm_repeat_message_time: 1.0,
297            nm_wait_bus_sleep_time: 1.0,
298        };
299        let can_nm_cluster = nm_config
300            .create_can_nm_cluster("CanNmCluster", &can_nm_cluster_settings, &cluster)
301            .unwrap();
302        let can_tp_config = system.create_can_tp_config("CanTpConfig", &pkg2, &cluster).unwrap();
303
304        // remove the cluster
305        let result = cluster.remove(true);
306        assert!(result.is_ok());
307
308        // check that the channel, CanNmCluster and CanTpConfig are also removed
309        assert!(channel.element().path().is_err());
310        assert!(can_nm_cluster.element().path().is_err());
311        assert!(can_tp_config.element().path().is_err());
312    }
313}