autosar_data_abstraction/communication/controller/
can.rs

1use crate::{
2    AbstractionElement, AutosarAbstractionError, EcuInstance, IdentifiableAbstractionElement, abstraction_element,
3    communication::{AbstractCommunicationConnector, AbstractCommunicationController, CanPhysicalChannel},
4};
5use autosar_data::{AutosarDataError, AutosarModel, Element, ElementName, ElementsIterator, WeakElement};
6
7/// An `EcuInstance` needs a `CanCommunicationController` in order to connect to a CAN cluster.
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub struct CanCommunicationController(Element);
10abstraction_element!(CanCommunicationController, CanCommunicationController);
11impl IdentifiableAbstractionElement for CanCommunicationController {}
12
13impl CanCommunicationController {
14    // create a new CanCommunicationController - called by EcuInstance::create_can_communication_controller
15    pub(crate) fn new(name: &str, ecu: &EcuInstance) -> Result<Self, AutosarAbstractionError> {
16        let commcontrollers = ecu.element().get_or_create_sub_element(ElementName::CommControllers)?;
17        let ctrl = commcontrollers.create_named_sub_element(ElementName::CanCommunicationController, name)?;
18        let _canccc = ctrl
19            .create_sub_element(ElementName::CanCommunicationControllerVariants)?
20            .create_sub_element(ElementName::CanCommunicationControllerConditional)?;
21
22        Ok(Self(ctrl))
23    }
24
25    /// return an iterator over the [`CanPhysicalChannel`]s connected to this controller
26    ///
27    /// # Example
28    ///
29    /// ```
30    /// # use autosar_data::*;
31    /// # use autosar_data_abstraction::*;
32    /// # use autosar_data_abstraction::communication::*;
33    /// # fn main() -> Result<(), AutosarAbstractionError> {
34    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
35    /// # let package = model.get_or_create_package("/pkg1")?;
36    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
37    /// # let ecu_instance = system.create_ecu_instance("ecu_name", &package)?;
38    /// let can_controller = ecu_instance.create_can_communication_controller("CanCtrl")?;
39    /// # let cluster = system.create_can_cluster("Cluster", &package, None)?;
40    /// # let physical_channel = cluster.create_physical_channel("Channel")?;
41    /// can_controller.connect_physical_channel("connection", &physical_channel)?;
42    /// for channel in can_controller.connected_channels() {
43    ///     // ...
44    /// }
45    /// # Ok(())}
46    /// ```
47    ///
48    /// # Errors
49    ///
50    /// - [`AutosarAbstractionError::ModelError`] An error occurred in the Autosar model while trying to get the ECU-INSTANCE
51    pub fn connected_channels(&self) -> impl Iterator<Item = CanPhysicalChannel> + Send + 'static {
52        if let Ok(ecu) = self.ecu_instance().map(|ecuinstance| ecuinstance.element().clone()) {
53            CanCtrlChannelsIterator::new(self, &ecu)
54        } else {
55            CanCtrlChannelsIterator {
56                connector_iter: None,
57                comm_controller: self.0.clone(),
58                model: None,
59            }
60        }
61    }
62
63    /// Connect this [`CanCommunicationController`] inside an [`EcuInstance`] to a [`CanPhysicalChannel`] in the [`crate::System`]
64    ///
65    /// Creates a [`CanCommunicationConnector`] in the [`EcuInstance`] that contains this [`CanCommunicationController`].
66    ///
67    /// This function establishes the relationships:
68    ///  - [`CanPhysicalChannel`] -> [`CanCommunicationConnector`]
69    ///  - [`CanCommunicationConnector`] -> [`CanCommunicationController`]
70    ///
71    /// # Example
72    ///
73    /// ```
74    /// # use autosar_data::*;
75    /// # use autosar_data_abstraction::*;
76    /// # use autosar_data_abstraction::communication::*;
77    /// # fn main() -> Result<(), AutosarAbstractionError> {
78    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
79    /// # let package = model.get_or_create_package("/pkg1")?;
80    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
81    /// # let ecu_instance = system.create_ecu_instance("ecu_name", &package)?;
82    /// let can_controller = ecu_instance.create_can_communication_controller("CanCtrl")?;
83    /// # let cluster = system.create_can_cluster("Cluster", &package, None)?;
84    /// # let physical_channel = cluster.create_physical_channel("Channel")?;
85    /// can_controller.connect_physical_channel("connection", &physical_channel)?;
86    /// # Ok(())}
87    /// ```
88    ///
89    /// # Errors
90    ///
91    /// - [`AutosarAbstractionError::ModelError`] An error occurred in the Autosar model while trying to create the ECU-INSTANCE
92    pub fn connect_physical_channel(
93        &self,
94        connection_name: &str,
95        can_channel: &CanPhysicalChannel,
96    ) -> Result<CanCommunicationConnector, AutosarAbstractionError> {
97        let ecu = self.0.named_parent()?.unwrap();
98        // check that there is no existing connector for this CanCommunicationController
99        if let Some(connectors) = ecu.get_sub_element(ElementName::Connectors) {
100            for connector in connectors.sub_elements() {
101                // Does the existing connector reference this CanCommunicationController?
102                // A CanCommunicationController can only connect to a single CAN cluster, so a second
103                // connector cannot be created.
104                if let Some(ccref) = connector.get_sub_element(ElementName::CommControllerRef) {
105                    if let Ok(commcontroller_of_connector) = ccref.get_reference_target() {
106                        if commcontroller_of_connector == self.0 {
107                            return Err(AutosarAbstractionError::ItemAlreadyExists);
108                        }
109                    }
110                }
111            }
112        }
113        // create a new connector
114        let connectors = ecu.get_or_create_sub_element(ElementName::Connectors)?;
115        let connector = CanCommunicationConnector::new(connection_name, &connectors, self)?;
116
117        let channel_connctor_refs = can_channel
118            .element()
119            .get_or_create_sub_element(ElementName::CommConnectors)?;
120        channel_connctor_refs
121            .create_sub_element(ElementName::CommunicationConnectorRefConditional)
122            .and_then(|ccrc| ccrc.create_sub_element(ElementName::CommunicationConnectorRef))
123            .and_then(|ccr| ccr.set_reference_target(connector.element()))?;
124
125        Ok(connector)
126    }
127}
128
129impl AbstractCommunicationController for CanCommunicationController {}
130
131//##################################################################
132
133/// A connector between a [`CanCommunicationController`] in an ECU and a [`CanPhysicalChannel`]
134#[derive(Debug, Clone, PartialEq, Eq, Hash)]
135pub struct CanCommunicationConnector(Element);
136abstraction_element!(CanCommunicationConnector, CanCommunicationConnector);
137impl IdentifiableAbstractionElement for CanCommunicationConnector {}
138
139impl CanCommunicationConnector {
140    pub(crate) fn new(
141        name: &str,
142        parent: &Element,
143        controller: &CanCommunicationController,
144    ) -> Result<Self, AutosarAbstractionError> {
145        let connector = parent.create_named_sub_element(ElementName::CanCommunicationConnector, name)?;
146        connector
147            .create_sub_element(ElementName::CommControllerRef)?
148            .set_reference_target(controller.element())?;
149        Ok(Self(connector))
150    }
151}
152
153impl AbstractCommunicationConnector for CanCommunicationConnector {
154    type CommunicationControllerType = CanCommunicationController;
155
156    fn controller(&self) -> Result<Self::CommunicationControllerType, AutosarAbstractionError> {
157        let controller = self
158            .element()
159            .get_sub_element(ElementName::CommControllerRef)
160            .ok_or_else(|| {
161                AutosarAbstractionError::ModelError(AutosarDataError::ElementNotFound {
162                    target: ElementName::CommControllerRef,
163                    parent: self.element().element_name(),
164                })
165            })?
166            .get_reference_target()?;
167        CanCommunicationController::try_from(controller)
168    }
169}
170
171//##################################################################
172
173#[doc(hidden)]
174pub struct CanCtrlChannelsIterator {
175    connector_iter: Option<ElementsIterator>,
176    comm_controller: Element,
177    model: Option<AutosarModel>,
178}
179
180impl CanCtrlChannelsIterator {
181    fn new(controller: &CanCommunicationController, ecu: &Element) -> Self {
182        let iter = ecu.get_sub_element(ElementName::Connectors).map(|c| c.sub_elements());
183        let comm_controller = controller.element().clone();
184        let model = comm_controller.model().ok();
185        Self {
186            connector_iter: iter,
187            comm_controller,
188            model,
189        }
190    }
191}
192
193impl Iterator for CanCtrlChannelsIterator {
194    type Item = CanPhysicalChannel;
195
196    fn next(&mut self) -> Option<Self::Item> {
197        let model = self.model.as_ref()?;
198        let connector_iter = self.connector_iter.as_mut()?;
199        for connector in connector_iter.by_ref() {
200            if connector.element_name() == ElementName::CanCommunicationConnector {
201                if let Some(commcontroller_of_connector) = connector
202                    .get_sub_element(ElementName::CommControllerRef)
203                    .and_then(|ccr| ccr.get_reference_target().ok())
204                {
205                    if commcontroller_of_connector == self.comm_controller {
206                        for ref_origin in model
207                            .get_references_to(&connector.path().ok()?)
208                            .iter()
209                            .filter_map(WeakElement::upgrade)
210                            .filter_map(|elem| elem.named_parent().ok().flatten())
211                        {
212                            // This assumes that each connector will only ever be referenced by at most one
213                            // PhysicalChannel, which is true for well-formed files.
214                            if ref_origin.element_name() == ElementName::CanPhysicalChannel {
215                                return CanPhysicalChannel::try_from(ref_origin).ok();
216                            }
217                        }
218                    }
219                }
220            }
221        }
222        None
223    }
224}
225
226//##################################################################
227
228#[cfg(test)]
229mod test {
230    use super::*;
231    use crate::{AutosarModelAbstraction, SystemCategory};
232    use autosar_data::AutosarVersion;
233
234    #[test]
235    fn controller() {
236        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
237        let pkg = model.get_or_create_package("/test").unwrap();
238        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
239        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
240
241        // create a controller
242        let result = ecu.create_can_communication_controller("Controller");
243        let controller = result.unwrap();
244
245        // create some physical channels
246        let cluster = system.create_can_cluster("CanCluster", &pkg, None).unwrap();
247        let channel1 = cluster.create_physical_channel("C1").unwrap();
248
249        // connect the controller to channel1
250        let connector = controller
251            .connect_physical_channel("connection_name1", &channel1)
252            .unwrap();
253        assert_eq!(connector.controller().unwrap(), controller);
254        // can't connect to the same channel again
255        let result = controller.connect_physical_channel("connection_name2", &channel1);
256        assert!(result.is_err());
257
258        let count = controller.connected_channels().count();
259        assert_eq!(count, 1);
260
261        // remove the controller and try to list its connected channels again
262        let ctrl_parent = controller.0.parent().unwrap().unwrap();
263        ctrl_parent.remove_sub_element(controller.0.clone()).unwrap();
264        let count = controller.connected_channels().count();
265        assert_eq!(count, 0);
266    }
267
268    #[test]
269    fn connector() {
270        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
271        let pkg = model.get_or_create_package("/test").unwrap();
272        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
273        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
274
275        // create a controller
276        let controller = ecu.create_can_communication_controller("Controller").unwrap();
277        assert_eq!(controller.ecu_instance().unwrap(), ecu);
278
279        // create some physical channels
280        let cluster = system.create_can_cluster("CanCluster", &pkg, None).unwrap();
281        let channel1 = cluster.create_physical_channel("C1").unwrap();
282
283        // connect the controller to channel1
284        let connector = controller
285            .connect_physical_channel("connection_name1", &channel1)
286            .unwrap();
287        assert_eq!(connector.controller().unwrap(), controller);
288        assert_eq!(connector.ecu_instance().unwrap(), ecu);
289
290        // remove the CommControllerRef from the connector and try to get the controller
291        connector
292            .element()
293            .remove_sub_element_kind(ElementName::CommControllerRef)
294            .unwrap();
295        let result = connector.controller();
296        assert!(result.is_err());
297    }
298}