autosar_data_abstraction/communication/cluster/
lin.rs

1use crate::communication::{AbstractCluster, LinPhysicalChannel};
2use crate::{
3    AbstractionElement, ArPackage, AutosarAbstractionError, IdentifiableAbstractionElement, abstraction_element,
4};
5use autosar_data::{Element, ElementName};
6
7//##################################################################
8
9/// A `LinCluster` contains all configuration items associated with a LIN network.
10/// The cluster connects multiple ECUs.
11#[derive(Debug, Clone, PartialEq, Eq, Hash)]
12pub struct LinCluster(Element);
13abstraction_element!(LinCluster, LinCluster);
14impl IdentifiableAbstractionElement for LinCluster {}
15
16impl LinCluster {
17    // create a new LinCluster - for internal use. User code should call System::create_lin_cluster
18    pub(crate) fn new(cluster_name: &str, package: &ArPackage) -> Result<Self, AutosarAbstractionError> {
19        let elem_pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
20        let elem_cluster = elem_pkg_elements.create_named_sub_element(ElementName::LinCluster, cluster_name)?;
21        if let Ok(cluster_content) = elem_cluster
22            .create_sub_element(ElementName::LinClusterVariants)
23            .and_then(|ccv| ccv.create_sub_element(ElementName::LinClusterConditional))
24        {
25            let _ = cluster_content
26                .create_sub_element(ElementName::ProtocolName)
27                .and_then(|pn| pn.set_character_data("CAN"));
28
29            let _ = cluster_content.create_sub_element(ElementName::PhysicalChannels);
30        }
31
32        let lin_cluster = LinCluster(elem_cluster);
33
34        Ok(lin_cluster)
35    }
36
37    /// remove this `LinCluster` from the model
38    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
39        // remove the physical channel, if existing
40        if let Some(channel) = self.physical_channel() {
41            channel.remove(deep)?;
42        }
43
44        // delegate to the trait implementation to clean up all other references to the element and the element itself
45        AbstractionElement::remove(self, deep)?;
46
47        Ok(())
48    }
49
50    /// Create a new physical channel for the cluster
51    ///
52    /// A LIN cluster must contain exactly one physical channel; trying to add a second one triggers an error.
53    ///
54    /// # Example
55    ///
56    /// ```
57    /// # use autosar_data::*;
58    /// # use autosar_data_abstraction::*;
59    /// # use autosar_data_abstraction::communication::*;
60    /// # fn main() -> Result<(), AutosarAbstractionError> {
61    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00051);
62    /// # let package = model.get_or_create_package("/pkg1")?;
63    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
64    /// let cluster = system.create_lin_cluster("Cluster", &package)?;
65    /// let channel = cluster.create_physical_channel("Channel")?;
66    /// # Ok(())}
67    /// ```
68    ///
69    /// # Errors
70    ///
71    /// - [`AutosarAbstractionError::ItemAlreadyExists`] There is already a physical channel in this LIN cluster
72    /// - [`AutosarAbstractionError::ModelError`] An error occurred in the Autosar model while trying to create the ECU-INSTANCE
73    pub fn create_physical_channel(&self, channel_name: &str) -> Result<LinPhysicalChannel, AutosarAbstractionError> {
74        let phys_channels = self
75            .0
76            .get_or_create_sub_element(ElementName::LinClusterVariants)?
77            .get_or_create_sub_element(ElementName::LinClusterConditional)?
78            .get_or_create_sub_element(ElementName::PhysicalChannels)?;
79
80        if phys_channels.sub_elements().count() != 0 {
81            return Err(AutosarAbstractionError::ItemAlreadyExists);
82        }
83
84        let channel = phys_channels.create_named_sub_element(ElementName::LinPhysicalChannel, channel_name)?;
85
86        LinPhysicalChannel::try_from(channel)
87    }
88
89    /// return the `LinPhysicalChannel` of the Cluster, if it has been created
90    ///
91    /// # Example
92    ///
93    /// ```
94    /// # use autosar_data::*;
95    /// # use autosar_data_abstraction::*;
96    /// # use autosar_data_abstraction::communication::*;
97    /// # fn main() -> Result<(), AutosarAbstractionError> {
98    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
99    /// # let package = model.get_or_create_package("/pkg1")?;
100    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
101    /// # let cluster = system.create_can_cluster("Cluster", &package, None)?;
102    /// # let can_channel = cluster.create_physical_channel("Channel")?;
103    /// if let Some(channel) = cluster.physical_channel() {
104    /// #   assert_eq!(channel, can_channel);
105    /// }
106    /// # Ok(())}
107    /// ```
108    #[must_use]
109    pub fn physical_channel(&self) -> Option<LinPhysicalChannel> {
110        let channel = self
111            .0
112            .get_sub_element(ElementName::LinClusterVariants)?
113            .get_sub_element(ElementName::LinClusterConditional)?
114            .get_sub_element(ElementName::PhysicalChannels)?
115            .get_sub_element(ElementName::LinPhysicalChannel)?;
116        LinPhysicalChannel::try_from(channel).ok()
117    }
118}
119
120impl AbstractCluster for LinCluster {}
121
122//##################################################################
123
124#[cfg(test)]
125mod test {
126    use crate::{AutosarModelAbstraction, SystemCategory, communication::AbstractCluster};
127    use autosar_data::AutosarVersion;
128
129    #[test]
130    fn cluster() {
131        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00051);
132        let pkg = model.get_or_create_package("/test").unwrap();
133        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
134
135        let pkg2 = model.get_or_create_package("/lin").unwrap();
136        // create the LIN cluster LinCluster
137        let result = system.create_lin_cluster("LinCluster", &pkg2);
138        assert!(result.is_ok());
139        let cluster = result.unwrap();
140        // creating the same cluster again is not possible
141        let result = system.create_lin_cluster("LinCluster", &pkg2);
142        assert!(result.is_err());
143
144        // system link
145        let linked_system = cluster.system().unwrap();
146        assert_eq!(linked_system, system);
147
148        // create a channel
149        let result = cluster.create_physical_channel("Channel1");
150        assert!(result.is_ok());
151        // can't create a second channel
152        let result = cluster.create_physical_channel("Channel2");
153        assert!(result.is_err());
154
155        let pc = cluster.physical_channel();
156        assert!(pc.is_some());
157    }
158}