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