autosar_data_abstraction/communication/controller/
ethernet.rs

1use crate::communication::{
2    AbstractCommunicationConnector, AbstractCommunicationController, EthernetPhysicalChannel, EthernetVlanInfo,
3};
4use crate::{
5    AbstractionElement, AutosarAbstractionError, EcuInstance, IdentifiableAbstractionElement, abstraction_element,
6};
7use autosar_data::{AutosarDataError, AutosarModel, Element, ElementName, ElementsIterator, WeakElement};
8
9/// An `EcuInstance` needs an `EthernetCommunicationController` in order to connect to an ethernet cluster.
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct EthernetCommunicationController(Element);
12abstraction_element!(EthernetCommunicationController, EthernetCommunicationController);
13impl IdentifiableAbstractionElement for EthernetCommunicationController {}
14
15impl EthernetCommunicationController {
16    // create an EthernetCommunicationController
17    pub(crate) fn new(
18        name: &str,
19        ecu: &EcuInstance,
20        mac_address: Option<String>,
21    ) -> Result<Self, AutosarAbstractionError> {
22        let commcontrollers = ecu.element().get_or_create_sub_element(ElementName::CommControllers)?;
23        let ctrl = commcontrollers.create_named_sub_element(ElementName::EthernetCommunicationController, name)?;
24        let ethccc = ctrl
25            .create_sub_element(ElementName::EthernetCommunicationControllerVariants)?
26            .create_sub_element(ElementName::EthernetCommunicationControllerConditional)?;
27        if let Some(mac_address) = mac_address {
28            // creating the mac address element fails if the supplied string has an invalid format
29            let result = ethccc
30                .create_sub_element(ElementName::MacUnicastAddress)
31                .and_then(|mua| mua.set_character_data(mac_address));
32            if let Err(mac_address_error) = result {
33                let _ = commcontrollers.remove_sub_element(ctrl);
34                return Err(mac_address_error.into());
35            }
36        }
37        let coupling_port_name = format!("{name}_CouplingPort");
38        let _ = ethccc
39            .create_sub_element(ElementName::CouplingPorts)
40            .and_then(|cps| cps.create_named_sub_element(ElementName::CouplingPort, &coupling_port_name));
41
42        Ok(Self(ctrl))
43    }
44
45    /// remove this `EthernetCommunicationController` from the model
46    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
47        // remove all the connectors using this controller
48        let ecu_instance = self.ecu_instance()?;
49        for connector in ecu_instance
50            .element()
51            .get_sub_element(ElementName::Connectors)
52            .iter()
53            .flat_map(|connectors| connectors.sub_elements())
54            .filter_map(|conn| EthernetCommunicationConnector::try_from(conn).ok())
55        {
56            if let Ok(controller_of_connector) = connector.controller()
57                && controller_of_connector == self
58            {
59                connector.remove(deep)?;
60            }
61        }
62
63        AbstractionElement::remove(self, deep)
64    }
65
66    /// return an iterator over the [`EthernetPhysicalChannel`]s connected to this controller
67    ///
68    /// # Example
69    ///
70    /// ```
71    /// # use autosar_data::*;
72    /// # use autosar_data_abstraction::*;
73    /// # fn main() -> Result<(), AutosarAbstractionError> {
74    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
75    /// # let package = model.get_or_create_package("/pkg1")?;
76    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
77    /// # let ecu_instance = system.create_ecu_instance("ecu_name", &package)?;
78    /// let ethernet_controller = ecu_instance.create_ethernet_communication_controller("EthCtrl", None)?;
79    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
80    /// # let physical_channel = cluster.create_physical_channel("Channel", None)?;
81    /// ethernet_controller.connect_physical_channel("connection", &physical_channel)?;
82    /// for channel in ethernet_controller.connected_channels() {
83    ///     // ...
84    /// }
85    /// # Ok(())}
86    /// ```
87    ///
88    /// # Errors
89    ///
90    /// - [`AutosarAbstractionError::ModelError`] An error occurred in the Autosar model while trying to create the ECU-INSTANCE
91    pub fn connected_channels(&self) -> impl Iterator<Item = EthernetPhysicalChannel> + Send + use<> {
92        if let Ok(ecu) = self.ecu_instance().map(|ecuinstance| ecuinstance.element().clone()) {
93            EthernetCtrlChannelsIterator::new(self, &ecu)
94        } else {
95            EthernetCtrlChannelsIterator {
96                connector_iter: None,
97                comm_controller: self.0.clone(),
98                model: None,
99            }
100        }
101    }
102
103    /// Connect this [`EthernetCommunicationController`] inside an [`EcuInstance`] to an [`EthernetPhysicalChannel`] in the [`crate::System`]
104    ///
105    /// Creates an `EthernetCommunicationConnector` in the [`EcuInstance`] that contains this [`EthernetCommunicationController`].
106    ///
107    /// This function establishes the relationships:
108    ///  - [`EthernetPhysicalChannel`] -> `EthernetCommunicationConnector`
109    ///  - `EthernetCommunicationConnector` -> [`EthernetCommunicationController`]
110    ///
111    /// # Example
112    ///
113    /// ```
114    /// # use autosar_data::*;
115    /// # use autosar_data_abstraction::*;
116    /// # fn main() -> Result<(), AutosarAbstractionError> {
117    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
118    /// # let package = model.get_or_create_package("/pkg1")?;
119    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
120    /// # let ecu_instance = system.create_ecu_instance("ecu_name", &package)?;
121    /// let ethernet_controller = ecu_instance.create_ethernet_communication_controller("EthCtrl", None)?;
122    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
123    /// # let physical_channel = cluster.create_physical_channel("Channel", None)?;
124    /// ethernet_controller.connect_physical_channel("connection", &physical_channel)?;
125    /// # Ok(()) }
126    /// ```
127    ///
128    /// # Errors
129    ///
130    /// - [`AutosarAbstractionError::ModelError`] An error occurred in the Autosar model while trying to create the ECU-INSTANCE
131    pub fn connect_physical_channel(
132        &self,
133        connection_name: &str,
134        eth_channel: &EthernetPhysicalChannel,
135    ) -> Result<EthernetCommunicationConnector, AutosarAbstractionError> {
136        let ecu: Element = self.0.named_parent()?.unwrap();
137        let cluster_of_channel = eth_channel.cluster()?;
138
139        // There can be multiple connectors referring to a single EthernetCommunicationController,
140        // but all of these connectors must refer to different PhysicalChannels
141        // (= VLANs) of the same EthernetCluster.
142        for phys_channel in self.connected_channels() {
143            if phys_channel == *eth_channel {
144                return Err(AutosarAbstractionError::ItemAlreadyExists);
145            }
146
147            if phys_channel.cluster()? != cluster_of_channel {
148                return Err(AutosarAbstractionError::InvalidParameter(
149                    "The EthernetCommunicationController may only refer to different channels within the same cluster"
150                        .to_string(),
151                ));
152            }
153        }
154
155        // create a new connector
156        let connectors = ecu.get_or_create_sub_element(ElementName::Connectors)?;
157        let connector = EthernetCommunicationConnector::new(connection_name, &connectors, self)?;
158
159        // if the ethernet physical channel has a category (WIRED / WIRELESS / CANXL) then
160        // set the category of the connector to the same value
161        if let Some(category) = eth_channel
162            .element()
163            .get_sub_element(ElementName::Category)
164            .and_then(|cat| cat.character_data())
165            .and_then(|cdata| cdata.string_value())
166        {
167            let _ = connector
168                .element()
169                .create_sub_element(ElementName::Category)
170                .and_then(|cat| cat.set_character_data(category));
171        }
172
173        // create a communication connector ref in the ethernet channel that refers to this connector
174        let channel_connctor_refs = eth_channel
175            .element()
176            .get_or_create_sub_element(ElementName::CommConnectors)?;
177        channel_connctor_refs
178            .create_sub_element(ElementName::CommunicationConnectorRefConditional)
179            .and_then(|ccrc| ccrc.create_sub_element(ElementName::CommunicationConnectorRef))
180            .and_then(|ccr| ccr.set_reference_target(connector.element()))?;
181
182        // if the PhysicalChannel has VLAN info AND if there is a coupling port in this CommunicationController
183        // then the coupling port should link to the PhysicalChannel / VLAN
184        if let Some(EthernetVlanInfo { .. }) = eth_channel.vlan_info()
185            && let Some(coupling_port) = self
186                .0
187                .get_sub_element(ElementName::EthernetCommunicationControllerVariants)
188                .and_then(|eccv| eccv.get_sub_element(ElementName::EthernetCommunicationControllerConditional))
189                .and_then(|eccc| eccc.get_sub_element(ElementName::CouplingPorts))
190                .and_then(|cps| cps.get_sub_element(ElementName::CouplingPort))
191        {
192            coupling_port
193                .get_or_create_sub_element(ElementName::VlanMemberships)
194                .and_then(|vms| vms.create_sub_element(ElementName::VlanMembership))
195                .and_then(|vm| vm.create_sub_element(ElementName::VlanRef))
196                .and_then(|vr| vr.set_reference_target(eth_channel.element()))?;
197        }
198
199        Ok(connector)
200    }
201}
202
203impl AbstractCommunicationController for EthernetCommunicationController {}
204
205//##################################################################
206
207/// A connector between an [`EthernetCommunicationController`] in an ECU and an [`EthernetPhysicalChannel`]
208#[derive(Debug, Clone, PartialEq, Eq, Hash)]
209pub struct EthernetCommunicationConnector(Element);
210abstraction_element!(EthernetCommunicationConnector, EthernetCommunicationConnector);
211impl IdentifiableAbstractionElement for EthernetCommunicationConnector {}
212
213impl EthernetCommunicationConnector {
214    pub(crate) fn new(
215        name: &str,
216        parent: &Element,
217        controller: &EthernetCommunicationController,
218    ) -> Result<Self, AutosarAbstractionError> {
219        let connector = parent.create_named_sub_element(ElementName::EthernetCommunicationConnector, name)?;
220        connector
221            .create_sub_element(ElementName::CommControllerRef)
222            .and_then(|refelem| refelem.set_reference_target(&controller.0))?;
223        Ok(Self(connector))
224    }
225}
226
227impl AbstractCommunicationConnector for EthernetCommunicationConnector {
228    type CommunicationControllerType = EthernetCommunicationController;
229
230    fn controller(&self) -> Result<Self::CommunicationControllerType, AutosarAbstractionError> {
231        let controller = self
232            .element()
233            .get_sub_element(ElementName::CommControllerRef)
234            .ok_or_else(|| {
235                AutosarAbstractionError::ModelError(AutosarDataError::ElementNotFound {
236                    target: ElementName::CommControllerRef,
237                    parent: self.element().element_name(),
238                })
239            })?
240            .get_reference_target()?;
241        EthernetCommunicationController::try_from(controller)
242    }
243}
244
245//##################################################################
246
247#[doc(hidden)]
248pub struct EthernetCtrlChannelsIterator {
249    connector_iter: Option<ElementsIterator>,
250    comm_controller: Element,
251    model: Option<AutosarModel>,
252}
253
254impl EthernetCtrlChannelsIterator {
255    fn new(controller: &EthernetCommunicationController, ecu: &Element) -> Self {
256        let iter = ecu.get_sub_element(ElementName::Connectors).map(|c| c.sub_elements());
257        let comm_controller = controller.element().clone();
258        let model = comm_controller.model().ok();
259        Self {
260            connector_iter: iter,
261            comm_controller,
262            model,
263        }
264    }
265}
266
267impl Iterator for EthernetCtrlChannelsIterator {
268    type Item = EthernetPhysicalChannel;
269
270    fn next(&mut self) -> Option<Self::Item> {
271        let model = self.model.as_ref()?;
272        let connector_iter = self.connector_iter.as_mut()?;
273        for connector in connector_iter.by_ref() {
274            if connector.element_name() == ElementName::EthernetCommunicationConnector
275                && let Some(commcontroller_of_connector) = connector
276                    .get_sub_element(ElementName::CommControllerRef)
277                    .and_then(|ccr| ccr.get_reference_target().ok())
278                && commcontroller_of_connector == self.comm_controller
279            {
280                for ref_origin in model
281                    .get_references_to(&connector.path().ok()?)
282                    .iter()
283                    .filter_map(WeakElement::upgrade)
284                    .filter_map(|elem| elem.named_parent().ok().flatten())
285                {
286                    // This assumes that each connector will only ever be referenced by at most one
287                    // PhysicalChannel, which is true for well-formed files.
288                    if ref_origin.element_name() == ElementName::EthernetPhysicalChannel {
289                        return EthernetPhysicalChannel::try_from(ref_origin).ok();
290                    }
291                }
292            }
293        }
294        None
295    }
296}
297
298//##################################################################
299
300#[cfg(test)]
301mod test {
302    use super::*;
303    use crate::{AutosarModelAbstraction, SystemCategory, communication::EthernetVlanInfo};
304    use autosar_data::AutosarVersion;
305
306    #[test]
307    fn controller() {
308        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
309        let pkg = model.get_or_create_package("/test").unwrap();
310        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
311        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
312
313        // can't create a controller with an invalid MAC address
314        let result = ecu.create_ethernet_communication_controller("Controller", Some("abcdef".to_string()));
315        assert!(result.is_err());
316
317        // create a controller
318        let result = ecu.create_ethernet_communication_controller("Controller", Some("01:02:03:04:05:06".to_string()));
319        let controller = result.unwrap();
320
321        // create some physical channels
322        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
323        let channel1 = cluster.create_physical_channel("C1", None).unwrap();
324        let vlan_info = EthernetVlanInfo {
325            vlan_name: "VLAN_1".to_string(),
326            vlan_id: 1,
327        };
328        let channel2 = cluster.create_physical_channel("C2", Some(&vlan_info)).unwrap();
329
330        // connect the controller to channel1
331        let connector = controller
332            .connect_physical_channel("connection_name1", &channel1)
333            .unwrap();
334        assert_eq!(connector.controller().unwrap(), controller);
335        // can't connect to the same channel again
336        let result = controller.connect_physical_channel("connection_name2", &channel1);
337        assert!(result.is_err());
338        // connect the controller to channel2
339        let result = controller.connect_physical_channel("connection_name2", &channel2);
340        assert!(result.is_ok());
341
342        // create a different cluster and channel, then try to connect the controller to it
343        let cluster2 = system.create_ethernet_cluster("EthCluster2", &pkg).unwrap();
344        let channel3 = cluster2.create_physical_channel("C3", None).unwrap();
345        let result = controller.connect_physical_channel("connection_name3", &channel3);
346        // can't connect one ethernet controller to channels from different clusters
347        assert!(result.is_err());
348
349        let count = controller.connected_channels().count();
350        assert_eq!(count, 2);
351
352        // remove the controller and try to list its connected channels again
353        let ctrl_parent = controller.element().parent().unwrap().unwrap();
354        ctrl_parent.remove_sub_element(controller.element().clone()).unwrap();
355        let count = controller.connected_channels().count();
356        assert_eq!(count, 0);
357    }
358
359    #[test]
360    fn connector() {
361        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
362        let pkg = model.get_or_create_package("/test").unwrap();
363        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
364        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
365
366        let controller = ecu
367            .create_ethernet_communication_controller("Controller", None)
368            .unwrap();
369        assert_eq!(controller.ecu_instance().unwrap(), ecu);
370
371        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
372        let channel = cluster.create_physical_channel("C1", None).unwrap();
373
374        // create a connector
375        let connector = controller
376            .connect_physical_channel("connection_name", &channel)
377            .unwrap();
378        assert_eq!(connector.controller().unwrap(), controller);
379        assert_eq!(connector.ecu_instance().unwrap(), ecu);
380
381        // remove the connector and try to get the controller from it
382        let conn_parent = connector.element().parent().unwrap().unwrap();
383        conn_parent.remove_sub_element(connector.element().clone()).unwrap();
384        let result = connector.controller();
385        assert!(result.is_err());
386    }
387
388    #[test]
389    fn remove_controller() {
390        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
391        let pkg = model.get_or_create_package("/test").unwrap();
392        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
393        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
394        let controller = ecu
395            .create_ethernet_communication_controller("Controller", None)
396            .unwrap();
397        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
398        let channel = cluster.create_physical_channel("C1", None).unwrap();
399        let connector = controller
400            .connect_physical_channel("connection_name", &channel)
401            .unwrap();
402
403        // remove the controller (also removes the connector)
404        controller.remove(true).unwrap();
405
406        assert_eq!(ecu.communication_controllers().count(), 0);
407        assert!(connector.element().path().is_err());
408    }
409}